Alpine.js Forms
Alpine.js is a lightweight frontend library that offers reactive binding and declarative syntax without the complexity of larger frameworks like React or Vue. It is a popular choice for static sites, Jekyll, Astro, or WordPress projects.
Using Alpine.js, you can build contact forms that submit data asynchronously via AJAX, preventing page redirects and providing instant user feedback.
Code Example
Section titled “Code Example”Here is a copy-pasteable example of an Alpine.js form. It handles input binding, displays status alerts, and submits values using fetch().
<!-- Load Alpine.js from CDN (if not already included in your project) --><script src="https://cdn.jsdelivr.net/npm/alpinejs@3.x.x/dist/cdn.min.js" defer></script>
<div x-data="{ fields: { name: '', email: '', message: '', _honeypot: '' }, status: 'idle', // 'idle' | 'loading' | 'success' | 'error' errorMessage: '',
async submitForm() { this.status = 'loading'; this.errorMessage = '';
// Honeypot spam bot check if (this.fields._honeypot) { this.status = 'success'; this.resetForm(); return; }
try { const response = await fetch('https://api.lucidforms.co/f/{your-form-id}', { method: 'POST', headers: { 'Content-Type': 'application/json', 'Accept': 'application/json' }, body: JSON.stringify(this.fields) });
const data = await response.json();
if (response.ok) { this.status = 'success'; this.resetForm(); } else { this.status = 'error'; this.errorMessage = data.error || 'Form submission failed. Please try again.'; } } catch (err) { this.status = 'error'; this.errorMessage = 'A network error occurred. Please check your connection.'; } },
resetForm() { this.fields.name = ''; this.fields.email = ''; this.fields.message = ''; this.fields._honeypot = ''; } }" style="max-width: 400px; margin: 0 auto; font-family: sans-serif;"> <h2>Contact Us</h2>
<!-- Success Message --> <template x-if="status === 'success'"> <div style="color: green; background-color: #e8f5e9; padding: 10px; border-radius: 4px; margin-bottom: 15px;"> ✓ Thank you! Your message was submitted successfully. </div> </template>
<!-- Error Message --> <template x-if="status === 'error'"> <div style="color: red; background-color: #ffebee; padding: 10px; border-radius: 4px; margin-bottom: 15px;"> ✗ <span x-text="errorMessage"></span> </div> </template>
<form x-on:submit.prevent="submitForm"> <!-- Honeypot bot trap (hidden) --> <input type="text" x-model="fields._honeypot" style="display: none;" tabindex="-1" autocomplete="off" />
<div style="margin-bottom: 12px;"> <label for="name" style="display: block; margin-bottom: 4px; font-weight: 500;">Name</label> <input type="text" id="name" x-model="fields.name" required style="width: 100%; padding: 8px; border: 1px solid #ccc; border-radius: 4px; box-sizing: border-box;" /> </div>
<div style="margin-bottom: 12px;"> <label for="email" style="display: block; margin-bottom: 4px; font-weight: 500;">Email</label> <input type="email" id="email" x-model="fields.email" required style="width: 100%; padding: 8px; border: 1px solid #ccc; border-radius: 4px; box-sizing: border-box;" /> </div>
<div style="margin-bottom: 16px;"> <label for="message" style="display: block; margin-bottom: 4px; font-weight: 500;">Message</label> <textarea id="message" x-model="fields.message" required rows="4" style="width: 100%; padding: 8px; border: 1px solid #ccc; border-radius: 4px; box-sizing: border-box;" ></textarea> </div>
<button type="submit" x-bind:disabled="status === 'loading'" style="padding: 10px 16px; background-color: #4f46e5; color: white; border: none; border-radius: 4px; cursor: pointer; font-weight: bold;" > <span x-text="status === 'loading' ? 'Sending...' : 'Submit'"></span> </button> </form></div>Key Alpine Directives Explained
Section titled “Key Alpine Directives Explained”x-data: Initializes the component’s state variables (inputs, status, error states) and custom logic methods (likesubmitFormandresetForm).x-model: Enables two-way data binding. As the user types in the input fields, Alpine automatically syncs the value to your javascriptx-dataobject properties.x-on:submit.prevent: Attaches an event listener to the form’s submit action. The.preventmodifier automatically stops the browser’s default reload event, allowing Alpine’ssubmitForm()method to run in the background.x-if: Conditionally renders UI chunks (like success or error cards) based on the component’s current status variable.x-text: Dynamically binds the inner text of an HTML tag to state values, such as displaying the custom error message returned from the API.