Vize

use-v-model

Two-way binding helper for a component prop.

Package @vizejs/composable/use-v-model
Own the source vize lib pull composable:use-v-model
Runtime exports useVModel, useVModels
Gzip budget 1024 B

Minimal setup

Use a Vue 3.5+ project with Vue SFC compilation configured (see Vite+ integration).

vp install @vizejs/composable

Import the entry directly. Call lifecycle-bound utilities inside <script setup> so cleanup follows the component scope; consult the runtime contract for this entry's SSR and hydration behavior.

import { useVModel, useVModels } from "@vizejs/composable/use-v-model";

Try it

Support v-model with a local fallback

Write a component whose value can be bound with v-model but still works when no parent binds it.

Open the interactive example

What to observe: Type a nickname and the greeting updates, even though nothing binds v-model in this preview.

With passive: true the component keeps a local copy and emits update:modelValue; a parent that binds v-model stays the source of truth.

Compare the initial and interacted states
Support v-model with a local fallback: initial state
Initial state
Support v-model with a local fallback: state after the verified interactions
After the verified interactions

Copy the example

Save this complete SFC in your Vue project. Its public imports and source are the same ones executed in the preview.

<script setup lang="ts">
import { useVModel } from "@vizejs/composable/use-v-model";

const props = defineProps<{ modelValue?: string }>();
const emit = defineEmits<{ "update:modelValue": [value: string | undefined] }>();
const nickname = useVModel(props, "modelValue", emit, { passive: true, defaultValue: "Ada" });
</script>

<template>
  <div class="composable-example">
    <p>
      Write a component that works with <code>v-model</code> and also keeps its own value when no
      parent binds one.
    </p>
    <label>
      Nickname
      <input v-model="nickname" />
    </label>
    <output aria-live="polite">Hello, {{ nickname }}!</output>
  </div>
</template>

Runtime contract

Utility Category Stability SSR Hydration Cleanup Targets Host globals Uses
useVModel reactivity experimental safe stable reactive-scope web, server, worker, native, desktop, terminal structuredClone —
useVModels reactivity experimental safe stable reactive-scope web, server, worker, native, desktop, terminal structuredClone useVModel

API

useVModel

Two-way binding helper for a component prop. Reads the prop and emits update:<key> (or eventName) on write, typed end to end: the key must be a prop, the emitted value has the prop's type, and emit must accept that exact event (a defineEmits result with the matching overload qualifies). With passive: true a local ref mirrors the prop so the component also works uncontrolled. Relies only on the props and emit you pass (no component internals), so it is SSR- and Vapor-friendly; the passive watchers follow the owning scope.

function useVModel< Props extends object, Key extends keyof Props & string, Event extends string = `update:${Key}`, Default extends Exclude<Props[Key], undefined> | undefined = undefined, >( props: Props, key: Key, emit: VModelEmit<NoInfer<Event>, NoInfer<Props[Key]>>, options?: UseVModelOptions<Props[Key], Event, Default>, ): Ref<VModelValue<Props[Key], Default>>
const props = defineProps<{ modelValue: string }>();
const emit = defineEmits<{ "update:modelValue": [value: string] }>();
const model = useVModel(props, "modelValue", emit);
model.value = "next"; // emits update:modelValue

useVModel

function useVModel( props: Readonly<Record<string, unknown>>, key: string, emit: VModelEmit<string, unknown>, options: UseVModelOptions<unknown, string, unknown> = {}, ): Ref<unknown> | WritableComputedRef<unknown>

useVModels

useVModel for several props at once. Returns one writable ref per bound prop, each emitting its own update:<prop> event. emit must declare an update event for every bound key; restrict the set with keys.

function useVModels< Props extends object, const Key extends keyof Props & string = keyof Props & string, >( props: Props, emit: NoInfer<VModelsEmit<Props, Key>>, options?: UseVModelsOptions<Key>, ): { readonly [Name in Key]: Ref<Props[Name]> }
const { open, value } = useVModels(props, emit, { keys: ["open", "value"] });

useVModels

function useVModels( props: Readonly<Record<string, unknown>>, emit: VModelEmit<`update:${string}`, unknown>, options: UseVModelsOptions<string> = {}, ): { readonly [key: string]: Ref<unknown> }

Types

UseVModelOptions

Options for useVModel.

Member Type Description
passive? boolean Keep a local copy that works even when the parent does not bind v-model: writes update the copy and emit, prop changes overwrite it.
eventName? Event Event name to emit.
deep? boolean In passive mode, store the local copy deeply reactive and also emit on nested mutations of it.
defaultValue? Default Value read while the prop is undefined.
clone? boolean | ((value: Value) => Value) In passive mode, copy prop values before storing them locally so local mutations never touch the parent's object: true uses structuredClone on the raw (unproxied) value, a function is used as the cloner.
shouldEmit? (value: Value) => boolean Veto emitting a value.

UseVModelsOptions

Options for useVModels.

Member Type Description
keys? readonly Key[] Props to bind. Defaults to every prop, which then requires an update:<prop> emit for each of them.