Form inputs: v-model
The v-model directive links a form field and a variable both ways: the user types — the variable changes; the variable changes in code — the field shows the new value. You no longer read input.value by hand in a handler.
A text field
<!-- src/App.vue -->
<script setup>
import { ref } from 'vue'
const newTodo = ref('')
</script>
<template>
<input v-model="newTodo" placeholder="What needs doing?">
<p>You are typing: {{ newTodo }}</p>
</template>The text under the field repeats everything you type, letter by letter. v-model is a shorthand for the pair :value="newTodo" and @input="newTodo = $event.target.value".
The add-task form
Let's combine v-model with the form's submit event:
<!-- src/App.vue -->
<script setup>
import { ref } from 'vue'
const todos = ref([])
const newTodo = ref('')
let nextId = 1
function addTodo() {
const text = newTodo.value.trim()
if (text === '') return
todos.value.push({ id: nextId++, text, done: false })
newTodo.value = ''
}
</script>
<template>
<form @submit.prevent="addTodo">
<input v-model="newTodo" placeholder="What needs doing?">
<button>Add</button>
</form>
<ul>
<li v-for="todo in todos" :key="todo.id">{{ todo.text }}</li>
</ul>
</template>Type "Buy bread" and press Enter or the button — the task appears in the list, and the field is cleared because the code put an empty string into newTodo.
A checkbox
For a checkbox the variable gets true or false:
<template>
<li v-for="todo in todos" :key="todo.id">
<label>
<input type="checkbox" v-model="todo.done">
{{ todo.text }}
</label>
</li>
</template>Ticking the box changes the task's done field right away — the computed counter of remaining tasks updates too.
Other fields
| Field | Variable value |
|---|---|
input, textarea | A string |
input type="checkbox" | true or false |
Several checkbox with one array | An array of the checked values |
input type="radio" | The value of the chosen one |
select | The value of the chosen option |
<script setup>
import { ref } from 'vue'
const filter = ref('all')
</script>
<template>
<select v-model="filter">
<option value="all">All</option>
<option value="active">Active</option>
<option value="done">Done</option>
</select>
<p>Filter: {{ filter }}</p>
</template>When you pick an option, its value appears under the list, for example "Filter: active".
Modifiers
| Modifier | What it does |
|---|---|
v-model.trim | Strips spaces at both ends |
v-model.number | Turns the input into a number |
v-model.lazy | Updates the variable when the field loses focus, not on every character |
Common mistake
Giving a field both v-model and value:
<input v-model="newTodo" value="New task">v-model ignores the value, checked and selected attributes — the initial value comes only from the variable. Set it in the script: const newTodo = ref('New task').