x-model and form inputs
The x-model directive binds a form field to a component property in both directions. The user types — the property changes; code changes the property — the field updates. This link is called two-way binding.
A text field
<script defer src="https://cdn.jsdelivr.net/npm/alpinejs@3.x.x/dist/cdn.min.js"></script>
<div x-data="{ title: '' }">
<input x-model="title" placeholder="New task">
<p>You typed: <span x-text="title"></span></p>
</div>The text below the field repeats what you type on every key press.
The to-do form
Now the form can be built without $refs. After a task is added, the field is cleared by assigning title = '':
<script defer src="https://cdn.jsdelivr.net/npm/alpinejs@3.x.x/dist/cdn.min.js"></script>
<style>
.done { text-decoration: line-through; }
</style>
<div x-data="{
title: '',
tasks: [{ id: 1, title: 'Buy milk', done: false }],
add() {
if (this.title.trim() === '') return;
this.tasks.push({ id: Date.now(), title: this.title.trim(), done: false });
this.title = '';
}
}">
<form @submit.prevent="add()">
<input x-model="title" placeholder="New task">
<button>Add</button>
</form>
<ul>
<template x-for="task in tasks" :key="task.id">
<li>
<label>
<input type="checkbox" x-model="task.done">
<span :class="{ done: task.done }" x-text="task.title"></span>
</label>
</li>
</template>
</ul>
</div>Type a task and press Enter — it appears in the list and the field is cleared. The checkbox is bound to the done property: checking it crosses the task out.
Other fields
| Field | What goes into the property |
|---|---|
<input type="checkbox"> | true or false |
| Several checkboxes bound to one array | An array of the checked values |
<input type="radio"> | The value of the selected one |
<select> | The value of the selected option |
<textarea> | The text |
An example with a task filter:
<script defer src="https://cdn.jsdelivr.net/npm/alpinejs@3.x.x/dist/cdn.min.js"></script>
<div x-data="{ filter: 'all' }">
<select x-model="filter">
<option value="all">All</option>
<option value="active">Active</option>
<option value="done">Done</option>
</select>
<p x-text="'Filter: ' + filter"></p>
</div>Pick another option — the line below shows its value.
Modifiers
| Modifier | What it does |
|---|---|
x-model.number | Turns the input into a number |
x-model.lazy | Updates the property when the field loses focus, not on every key |
x-model.debounce | Waits for a pause in typing, handy for search |
Common mistake
Forgetting .number on a numeric field. The value of an <input type="number"> still arrives as a string, and addition glues strings together:
<script defer src="https://cdn.jsdelivr.net/npm/alpinejs@3.x.x/dist/cdn.min.js"></script>
<div x-data="{ hours: 2 }">
<input type="number" x-model="hours">
<p x-text="'Total hours: ' + (hours + 1)"></p>
</div>At first it shows 3, but after typing 5 you get "51". With x-model.number the result is 6.