Vue.js Form Inputs
Forms are how users send data to your application — through text boxes, checkboxes, dropdowns, and radio buttons. Vue's v-model directive creates a live, two-way connection between a form input and your data, so the input and the data always match each other.
Two-Way Binding — What It Means
Without v-model, when a user types in an input, your data does not change. You have to write code to listen for the input event and update the data manually. v-model does this automatically in both directions.
Diagram: Two-Way Binding
Without v-model: User types → (nothing happens to data unless you write code) Data changes → (input does not reflect new data unless you write code) With v-model: User types in input ──────▶ Vue updates data automatically Data changes ──────▶ Input field updates automatically Both sides stay in sync.
v-model with Text Inputs
<div id="app">
<input type="text" v-model="username" placeholder="Enter your name">
<p>Hello, {{ username }}!</p>
</div>
<script>
Vue.createApp({
data() {
return { username: "" };
}
}).mount("#app");
</script>
What Happens as You Type
User types "A" → username = "A" → Page shows: Hello, A! User types "Al" → username = "Al" → Page shows: Hello, Al! User types "Ali" → username = "Ali" → Page shows: Hello, Ali!
v-model with Textarea
v-model works the same way on a <textarea>. Note that in Vue, you do not put content inside the textarea tags — the value comes entirely from the bound data.
<textarea v-model="message" rows="4" placeholder="Write your message..."></textarea>
<p>Character count: {{ message.length }}</p>
v-model with Checkboxes
Single Checkbox — Bound to a Boolean
<div id="app">
<input type="checkbox" v-model="agreed" id="terms">
<label for="terms">I agree to the terms</label>
<p v-if="agreed">Thank you for agreeing!</p>
</div>
<script>
Vue.createApp({
data() {
return { agreed: false };
}
}).mount("#app");
</script>
Checking the box sets agreed to true. Unchecking sets it back to false.
Multiple Checkboxes — Bound to an Array
Bind several checkboxes to the same array. Vue adds each checked item's value to the array and removes it when unchecked.
<div id="app">
<p>Select your interests:</p>
<input type="checkbox" v-model="interests" value="Coding" id="c1">
<label for="c1">Coding</label>
<input type="checkbox" v-model="interests" value="Design" id="c2">
<label for="c2">Design</label>
<input type="checkbox" v-model="interests" value="Writing" id="c3">
<label for="c3">Writing</label>
<p>Selected: {{ interests.join(", ") }}</p>
</div>
<script>
Vue.createApp({
data() {
return { interests: [] };
}
}).mount("#app");
</script>
Diagram: Array Checkbox Binding
Start: interests = [] Check Coding: interests = ["Coding"] Check Design: interests = ["Coding", "Design"] Uncheck Coding: interests = ["Design"] Check Writing: interests = ["Design", "Writing"] Page always shows the current state of the array.
v-model with Radio Buttons
Radio buttons let the user pick one option from a group. Bind all radios in the group to the same v-model property. The selected radio's value becomes the property's value.
<div id="app">
<p>Preferred contact:</p>
<input type="radio" v-model="contactMethod" value="Email" id="r1">
<label for="r1">Email</label>
<input type="radio" v-model="contactMethod" value="Phone" id="r2">
<label for="r2">Phone</label>
<input type="radio" v-model="contactMethod" value="Chat" id="r3">
<label for="r3">Chat</label>
<p>You chose: {{ contactMethod }}</p>
</div>
<script>
Vue.createApp({
data() {
return { contactMethod: "Email" };
}
}).mount("#app");
</script>
Diagram: Radio Binding
contactMethod = "Email" (initial) ● Email ○ Phone ○ Chat User clicks Phone: contactMethod = "Phone" ○ Email ● Phone ○ Chat Page shows: You chose: Phone
v-model with Select (Dropdown)
Single Select
<div id="app">
<select v-model="country">
<option value="">-- Choose a country --</option>
<option value="us">United States</option>
<option value="uk">United Kingdom</option>
<option value="in">India</option>
</select>
<p>Selected: {{ country }}</p>
</div>
<script>
Vue.createApp({
data() {
return { country: "" };
}
}).mount("#app");
</script>
Multiple Select
Add the multiple attribute and bind to an array. The user can hold Ctrl/Cmd to select multiple options.
<select v-model="selectedSkills" multiple>
<option>HTML</option>
<option>CSS</option>
<option>JavaScript</option>
<option>Vue.js</option>
</select>
<p>Skills: {{ selectedSkills }}</p>
v-model Modifiers
Modifiers adjust how v-model syncs data.
.lazy — Update on Change Instead of on Input
By default, v-model updates data on every keystroke. The .lazy modifier delays the update until the user moves away from the input (the change event).
<!-- Updates on every key press --> <input v-model="name"> <!-- Updates only when the user finishes typing and leaves the field --> <input v-model.lazy="name">
.number — Convert Input to a Number
HTML inputs always return text. Use .number to automatically convert the value to a JavaScript number.
<input type="number" v-model.number="age"> <!-- Without .number: age = "25" (string) --> <!-- With .number: age = 25 (number) -->
.trim — Remove Extra Whitespace
The .trim modifier removes leading and trailing spaces from the input value automatically.
<input v-model.trim="email"> <!-- User types " hello@example.com " --> <!-- email = "hello@example.com" (spaces removed) -->
Full Form Example
<div id="app">
<h3>Registration Form</h3>
<label>Name:</label>
<input v-model.trim="form.name" type="text">
<label>Age:</label>
<input v-model.number="form.age" type="number">
<label>Plan:</label>
<select v-model="form.plan">
<option value="free">Free</option>
<option value="pro">Pro</option>
</select>
<input type="checkbox" v-model="form.newsletter" id="nl">
<label for="nl">Subscribe to newsletter</label>
<button @click.prevent="submitForm">Register</button>
<pre>{{ JSON.stringify(form, null, 2) }}</pre>
</div>
<script>
Vue.createApp({
data() {
return {
form: {
name: "",
age: null,
plan: "free",
newsletter: false
}
};
},
methods: {
submitForm() {
console.log("Form submitted:", this.form);
}
}
}).mount("#app");
</script>
Diagram: Live Form Data Preview
User fills in the form:
Name: "Jamie"
Age: 28
Plan: "pro"
Newsletter: checked
form object in Vue:
{
"name": "Jamie",
"age": 28,
"plan": "pro",
"newsletter": true
}
The <pre> tag shows this live as the user types.
Summary
v-model creates two-way binding between form inputs and Vue data. Text inputs and textareas bind to strings. Checkboxes bind to booleans (single) or arrays (multiple). Radio buttons set the bound property to the selected value. Dropdowns work with both single values and arrays. Use modifiers like .lazy, .number, and .trim to control how the sync happens. All form state stays in data(), making it easy to read, validate, and submit.
