Description: The x-model directive in AlpineJS provides a powerful and convenient way to create a seamless, reactive, two-way synchronization between form input elements and component data properties. This means that when the user interacts with an input (e.g., types in a text field, checks a box), the corresponding data property in your Alpine component updates automatically. Conversely, if the data property changes programmatically, the input element's value or state will reflect that change in the UI.
x-model is primarily used with form elements. Here's how it applies to common input types:
<input type="text" x-model="propertyName">
<textarea x-model="anotherProperty"></textarea>
Binds the value of the text input or textarea to the propertyName in your Alpine component's data. As the user types, propertyName is updated. If propertyName changes, the input field's content updates.
<!-- For a single boolean state -->
<input type="checkbox" x-model="booleanProperty">
<!-- For binding to an array (when multiple checkboxes share the same x-model and have 'value' attributes) -->
<input type="checkbox" x-model="arrayProperty" value="apple"> Apple
<input type="checkbox" x-model="arrayProperty" value="banana"> Banana
For a single checkbox, it binds the checked state (true/false) to booleanProperty. If multiple checkboxes use the same x-model and have distinct value attributes, x-model will bind to an array containing the values of the checked checkboxes.
<input type="radio" x-model="selectedOptionProperty" value="option1"> Option 1
<input type="radio" x-model="selectedOptionProperty" value="option2"> Option 2
Binds the value of the selected radio button to selectedOptionProperty.
<select x-model="selectedProperty">
<option value="val1">Option 1</option>
<option value="val2">Option 2</option>
</select>
<!-- For multiple select -->
<select x-model="multipleSelectedArray" multiple>
<option value="valA">Value A</option>
<option value="valB">Value B</option>
</select>
Binds the value of the selected <option> to selectedProperty. For <select multiple>, it binds to an array of selected option values.
x-model supports several modifiers to customize its behavior:
.lazy: By default, x-model updates the data on every input event. With .lazy, it updates on the change event instead (e.g., when a text input loses focus).
<input type="text" x-model.lazy="message">
.number: Automatically casts the input's value to a JavaScript Number. This is useful for inputs like type="number" or even text inputs where you expect numerical data, as HTML input values are typically strings.
<input type="number" x-model.number="quantity">
.debounce: Delays updating the data property until a certain amount of time has passed since the last input event. This is useful for scenarios like live search to avoid excessive updates.
<!-- Default debounce is 250ms -->
<input type="text" x-model.debounce="searchTerm">
<!-- Custom debounce time -->
<input type="text" x-model.debounce.500ms="searchTerm">
<input type="text" x-model.debounce.1s="searchTerm">
.trim: Automatically trims whitespace from the input value.
<input type="text" x-model.trim="username">
x-model data:
If an input element has an HTML value attribute (e.g., <input value="initial">) and x-model is also set, AlpineJS will initialize its data property based on x-model's binding, effectively overriding the HTML value attribute for the initial state of the Alpine component's data. However, the browser might render the HTML value momentarily before Alpine initializes. For clarity and predictability, it's best practice to define the initial state of your data exclusively within your x-data definition. Let x-model then reflect that single source of truth to the input element.
// In your Alpine component:
// Good:
// data: { message: 'Initial from x-data' }
// HTML:
// Avoid ambiguity:
// data: { message: '' }
// HTML:
// Alpine's 'message' will likely become '' on init.
x-model on non-form elements:
x-model is specifically designed for form elements like <input>, <select>, and <textarea>. Attempting to use x-model on elements like <div> or <p> will not establish two-way data binding and will not work as expected. For displaying data in non-form elements (one-way binding), use directives like x-text or x-bind.
<!-- Correct for display -->
<p x-text="myTextData"></p>
<div x-bind:class="{ 'active': isActive }"></div>
<!-- Incorrect for two-way binding -->
<div x-model="someProperty"></div> <!-- This won't work -->
.number for numerical inputs:
Values retrieved from HTML input elements are always strings, even from <input type="number">. If you bind x-model to such an input and expect the corresponding data property in your Alpine component to be a number, you might encounter issues if you perform arithmetic operations directly. Always use the .number modifier (x-model.number="myNumericProperty") to ensure the value is automatically cast to a JavaScript Number type before being stored in your component's data.
// Alpine component data
// data: { quantity: 0 }
// HTML
//
// this.quantity might be "5" (string) after input
// Correct HTML for numerical data
//
// this.quantity will be 5 (number) after input
Current message:
Current lazy message:
Current debounced message:
Agreed:
Selected fruit ID:
Current quantity: (Data type: )
Your feedback:
Selected method: