Skip to content

useWatch

Watch form field values reactively.

Import

typescript
import { useWatch } from '@vuehookform/core'

Usage

typescript
// Watch single field
const email = useWatch({ control, name: 'email' })

// Watch multiple fields
const values = useWatch({ control, name: ['email', 'password'] })

// Watch all fields
const allValues = useWatch({ control })

When to Use

Use useWatch when you need to:

  • React to field value changes
  • Compute derived values based on form data
  • Conditionally render UI based on field values
  • Sync form values with external state

Options

control

Type: Control<T>
Required: Yes

The control object from useForm.

name

Type: Path<T> | Path<T>[]
Optional

The field(s) to watch. If omitted, watches all fields.

defaultValue

Type: any
Optional

Value to use before the field has a value.

Return Value

Returns a reactive ref containing the watched value(s):

typescript
// Single field
const email: Ref<string> = useWatch({ control, name: 'email' })

// Multiple fields
const values: Ref<[string, string]> = useWatch({
  control,
  name: ['email', 'password'],
})

// All fields
const all: Ref<FormValues> = useWatch({ control })

Examples

Watch Single Field

vue
<script setup>
import { useForm, useWatch } from '@vuehookform/core'

const { control, register } = useForm({ schema })
const email = useWatch({ control, name: 'email' })
</script>

<template>
  <input v-bind="register('email')" />
  <p>Current email: {{ email }}</p>
</template>

Conditional Rendering

vue
<script setup>
import { useForm, useWatch } from '@vuehookform/core'

const { control, register } = useForm({ schema })
const accountType = useWatch({ control, name: 'accountType' })
</script>

<template>
  <select v-bind="register('accountType')">
    <option value="personal">Personal</option>
    <option value="business">Business</option>
  </select>

  <!-- Only show for business accounts -->
  <div v-if="accountType === 'business'">
    <input v-bind="register('companyName')" placeholder="Company Name" />
    <input v-bind="register('taxId')" placeholder="Tax ID" />
  </div>
</template>

Computed Values

vue
<script setup>
import { computed } from 'vue'
import { useForm, useWatch } from '@vuehookform/core'

const { control, register } = useForm({ schema })

const quantity = useWatch({ control, name: 'quantity' })
const unitPrice = useWatch({ control, name: 'unitPrice' })

const total = computed(() => {
  return (quantity.value || 0) * (unitPrice.value || 0)
})
</script>

<template>
  <input v-bind="register('quantity')" type="number" />
  <input v-bind="register('unitPrice')" type="number" />
  <p>Total: ${{ total.toFixed(2) }}</p>
</template>

Form Preview

vue
<script setup>
import { useForm, useWatch } from '@vuehookform/core'

const { control, register, handleSubmit } = useForm({ schema })
const formData = useWatch({ control })
</script>

<template>
  <div class="form-container">
    <form @submit="handleSubmit(onSubmit)">
      <input v-bind="register('title')" />
      <textarea v-bind="register('content')"></textarea>
    </form>

    <div class="preview">
      <h2>Preview</h2>
      <h3>{{ formData.title || 'Untitled' }}</h3>
      <p>{{ formData.content || 'No content yet...' }}</p>
    </div>
  </div>
</template>

Multiple Fields

vue
<script setup>
import { computed } from 'vue'
import { useForm, useWatch } from '@vuehookform/core'

const { control, register } = useForm({ schema })

const [firstName, lastName] = useWatch({
  control,
  name: ['firstName', 'lastName'],
})

const fullName = computed(() => {
  return `${firstName.value || ''} ${lastName.value || ''}`.trim()
})
</script>

<template>
  <input v-bind="register('firstName')" />
  <input v-bind="register('lastName')" />
  <p>Full name: {{ fullName || 'Enter your name' }}</p>
</template>

Watch in Child Component

vue
<!-- PasswordStrength.vue -->
<script setup>
import { computed } from 'vue'
import { useWatch } from '@vuehookform/core'
import type { Control } from '@vuehookform/core'

const props = defineProps<{
  control: Control<any>
}>()

const password = useWatch({
  control: props.control,
  name: 'password',
  defaultValue: '',
})

const strength = computed(() => {
  const p = password.value
  if (!p) return 0
  let score = 0
  if (p.length >= 8) score++
  if (/[A-Z]/.test(p)) score++
  if (/[0-9]/.test(p)) score++
  if (/[^A-Za-z0-9]/.test(p)) score++
  return score
})

const strengthLabel = computed(() => {
  const labels = ['Weak', 'Fair', 'Good', 'Strong']
  return labels[strength.value - 1] || 'Too short'
})
</script>

<template>
  <div class="password-strength">
    <div class="strength-bar" :data-strength="strength" />
    <span>{{ strengthLabel }}</span>
  </div>
</template>

Comparison with watch from useForm

MethodUse Case
watch from useFormSame component, quick access
useWatch composableChild components, more flexibility

Both return the same reactive values. Choose based on where you need the data.

Released under the MIT License.