In this post, I will show you how to add Form Validation using Alpine.js. It’s very easy to do, and I think you’ll really enjoy how simple it is!
Table of Contents
Alpine.js is a lightweight JavaScript framework designed for minimal effort and maximum interactivity. In this article, we’ll walk through building a simple client-side form validation system using Alpine.js—no jQuery, no additional libraries, just plain HTML and Alpine goodness.
What We’ll Build
To help you understand better, imagine we’re building a simple “Contact Us” form — something you’d find on almost any website. We’ll walk through how to create it step by step, including:
- Name (required)
- Email (required, valid format)
- Message (optional)
- Submit button
- Real-time validation feedback ( response )
Why Use Alpine for This?
- Lightweight: Only a few KBs.
- Simple: Direct binding to inputs and DOM elements.
- No build tools: Works right in HTML—great for quick projects or static sites.
Form Validation Demo
Form Validation using Alpine.js Working
In this example, we’ve built a simple contact form using Alpine.js for interactivity and Tailwind CSS for styling—without the need for any external form libraries.
The form includes three fields: name, email, message, button . When the user starts typing, Alpine.js listens to their input and validates the data in real-time, providing immediate feedback if something’s missing or incorrect.
At the heart of this form is the x-data="formHandler()"
directive. This initializes a small Alpine component that stores the form’s state—like the input values, error messages, and a success flag.
Each input is connected to Alpine using x-model
, making it reactive. We also use @input
to run validation functions whenever the user types. For example, if the name field is empty, an error message appears right away, guiding the user to correct it.
When the form is submitted, the submitForm()
method runs. It first checks the name and email fields for any errors. If everything looks good, it logs the form data to the console, resets the form fields, and displays a green success message. If there are errors, it prevents submission and highlights what needs to be fixed—creating a smooth and user-friendly experience.
You can also read these article: How to Create a Countdown Timer with Alpine.js
Styling is handled entirely with Tailwind CSS. The form is clean, centered on the screen, and fully responsive. Buttons, inputs, and messages all use Tailwind’s utility classes to achieve a modern look without writing any custom CSS. This approach keeps things lightweight and efficient, making it perfect for small projects or quick prototypes where you still want a polished, interactive form.
Full Source code
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Alpine.js Form Validation</title>
<!-- Alpine.js CDN -->
<script defer src="https://cdn.jsdelivr.net/npm/[email protected]/dist/cdn.min.js"></script>
<!-- Tailwind CSS CDN -->
<script src="https://cdn.tailwindcss.com"></script>
</head>
<body class="bg-gray-100 min-h-screen flex items-center justify-center px-4">
<div x-data="formHandler()" class="w-full max-w-2xl bg-white rounded-xl shadow-md p-10">
<h1 class="text-3xl font-bold text-gray-800 mb-8 text-center">Alpine.js Form Validation</h1>
<form @submit.prevent="submitForm" class="space-y-6">
<!-- Name Field -->
<div>
<label class="block font-medium text-gray-700 mb-1">Name</label>
<input type="text" x-model="name" @input="validateName"
class="w-full border border-gray-300 p-3 rounded-md focus:outline-none focus:ring focus:ring-blue-300" />
<p x-show="errors.name" class="text-red-500 text-sm mt-1" x-text="errors.name"></p>
</div>
<!-- Email Field -->
<div>
<label class="block font-medium text-gray-700 mb-1">Email</label>
<input type="email" x-model="email" @input="validateEmail"
class="w-full border border-gray-300 p-3 rounded-md focus:outline-none focus:ring focus:ring-blue-300" />
<p x-show="errors.email" class="text-red-500 text-sm mt-1" x-text="errors.email"></p>
</div>
<!-- Message Field -->
<div>
<label class="block font-medium text-gray-700 mb-1">Message</label>
<textarea x-model="message"
class="w-full border border-gray-300 p-3 rounded-md focus:outline-none focus:ring focus:ring-blue-300"
rows="4"></textarea>
</div>
<!-- Submit Button -->
<button type="submit"
class="w-full bg-blue-600 hover:bg-blue-700 text-white py-3 px-4 rounded-full transition duration-200">
Submit
</button>
</form>
<!-- Success Message -->
<div x-show="success" x-transition
class="text-green-600 font-semibold mt-6 text-center">
Form submitted successfully!
</div>
</div>
<script>
function formHandler() {
return {
name: '',
email: '',
message: '',
errors: {},
success: false,
validateName() {
this.errors.name = this.name.trim() === '' ? 'Name is required.' : '';
},
validateEmail() {
const emailPattern = /^[^@]+@[^@]+\.[^@]+$/;
if (this.email.trim() === '') {
this.errors.email = 'Email is required.';
} else if (!emailPattern.test(this.email)) {
this.errors.email = 'Please enter a valid email.';
} else {
this.errors.email = '';
}
},
submitForm() {
this.validateName();
this.validateEmail();
if (!this.errors.name && !this.errors.email) {
this.success = true;
console.log({
name: this.name,
email: this.email,
message: this.message
});
// Reset form
this.name = '';
this.email = '';
this.message = '';
this.errors = {};
} else {
this.success = false;
}
}
}
}
</script>
</body>
</html>
Final Thoughts
Alpine.js is a perfect fit for small to medium-sized projects where you need interactivity without the complexity of a full-blown framework. As you’ve seen, form validation becomes super clean and declarative.