Fix: useModel Called with Prop Not Declared in Vue.js

Error message:
useModel() called with prop "{name}" which is not declared.
Composition API 2025-01-25

What Causes This Warning?

This warning occurs when you use useModel() (or defineModel()) with a prop name that hasn’t been declared in your component’s props. The model needs a corresponding prop declaration.

The Problem

<script setup>
// ❌ Using useModel without declaring the prop
const count = useModel('count') // 'count' prop not declared!
</script>

The Fix

Use defineModel (Vue 3.4+)

<script setup>
// ✅ defineModel declares the prop automatically
const count = defineModel('count')
const modelValue = defineModel() // For default v-model
</script>

Declare Props Explicitly (Vue 3.3 and earlier)

<script setup>
// ✅ Declare the prop first
const props = defineProps({
  count: Number
})

const emit = defineEmits(['update:count'])

// Then create a computed for two-way binding
const count = computed({
  get: () => props.count,
  set: (value) => emit('update:count', value)
})
</script>

Common Scenarios

Basic v-model

<!-- Parent -->
<template>
  <ChildComponent v-model="value" />
</template>

<!-- Child with defineModel (Vue 3.4+) -->
<script setup>
// ✅ Automatically declares modelValue prop
const model = defineModel()
</script>

<template>
  <input v-model="model" />
</template>

Named v-model

<!-- Parent -->
<template>
  <ChildComponent v-model:title="title" v-model:content="content" />
</template>

<!-- Child -->
<script setup>
// ✅ Named models
const title = defineModel('title')
const content = defineModel('content')
</script>

<template>
  <input v-model="title" placeholder="Title" />
  <textarea v-model="content" placeholder="Content" />
</template>

With Type and Default

<script setup>
// ✅ With options
const count = defineModel('count', {
  type: Number,
  default: 0,
  required: false
})

// ✅ TypeScript
const count = defineModel<number>('count', {
  default: 0
})
</script>

Multiple Models

<script setup>
// ✅ Multiple named models
const firstName = defineModel('firstName', { type: String })
const lastName = defineModel('lastName', { type: String })
const email = defineModel('email', { type: String })
</script>

<!-- Parent usage -->
<FormComponent
  v-model:first-name="user.firstName"
  v-model:last-name="user.lastName"
  v-model:email="user.email"
/>

Manual Implementation (Pre-Vue 3.4)

<script setup>
// For Vue < 3.4 or when you need more control

// Step 1: Declare props
const props = defineProps({
  modelValue: {
    type: String,
    default: ''
  },
  count: {
    type: Number,
    default: 0
  }
})

// Step 2: Declare emits
const emit = defineEmits(['update:modelValue', 'update:count'])

// Step 3: Create computed properties
const model = computed({
  get: () => props.modelValue,
  set: (value) => emit('update:modelValue', value)
})

const countModel = computed({
  get: () => props.count,
  set: (value) => emit('update:count', value)
})
</script>

<template>
  <input v-model="model" />
  <input v-model="countModel" type="number" />
</template>

Modifiers

<script setup>
// ✅ Handle modifiers
const [model, modifiers] = defineModel({
  set(value) {
    if (modifiers.capitalize) {
      return value.charAt(0).toUpperCase() + value.slice(1)
    }
    return value
  }
})
</script>

<!-- Parent usage -->
<TextInput v-model.capitalize="text" />

Migration from useModel to defineModel

<!-- Old way (deprecated) -->
<script setup>
import { useModel } from 'vue'

const props = defineProps(['modelValue'])
const model = useModel(props, 'modelValue')
</script>

<!-- New way (Vue 3.4+) -->
<script setup>
const model = defineModel()
</script>

Quick Checklist

  • Use defineModel() in Vue 3.4+ (auto-declares props)
  • For named models: defineModel('propName')
  • Add type and default options as needed
  • In older Vue, manually declare props and emits
  • Use computed with getter/setter for manual implementation