Skip to content

Repository files navigation

Alpine JS Form Validation

A lightweight, CSS-driven form validation plugin for Alpine.js that provides real-time client-side validation with data attributes.

✨ Features

  • 🪶 Lightweight - Minimal overhead, maximum performance
  • 🎨 CSS-driven - Style validation states with data attributes
  • Real-time - Immediate feedback as users type
  • 🔧 Flexible - Multiple validation rules per input
  • 📱 Accessible - Works with screen readers and keyboard navigation
  • 🚫 Zero dependencies - Only requires Alpine.js

📦 Installation

With a CDN

<scriptdefersrc="https://unpkg.com/alpinejs-form-validation@latest/dist/validation.min.js"
></script><scriptdefersrc="https://unpkg.com/alpinejs@latest/dist/cdn.min.js"></script>

With a Package Manager

yarn add -D alpinejs-form-validation
npm install -D alpinejs-form-validation
importAlpinefrom'alpinejs'importvalidationfrom'alpinejs-form-validation'Alpine.plugin(validation)Alpine.start()

🚀 Quick Start

  1. Add the validation directive to your inputs with x-validation
  2. Use modifiers to specify validation rules (e.g., x-validation.required)
  3. Trigger validation with $dispatch('validate')
  4. Style validation states using data attributes

📋 Validation Rules

RuleExampleDescription
requiredx-validation.requiredField must have a value
min.Xx-validation.min.18Numeric minimum value
max.Xx-validation.max.65Numeric maximum value
min:length.Xx-validation.min:length.5Minimum string length
max:length.Xx-validation.max:length.100Maximum string length
checkedx-validation.checkedCheckbox must be checked

Combining Rules

You can combine multiple validation rules on a single input:

<inputx-model="age" x-validation.required.min.18.max.65="age" /><textareax-model="message"
x-validation.required.min:length.10.max:length.500="message"
></textarea>

🎨 Data Attributes

The plugin automatically sets these data attributes on validated elements:

  • data-validation-valid - "true" or "false"
  • data-validation-reason - The specific validation rule that failed
  • data-validation-dirty - "true" once the user has interacted with the field
  • data-validation-status - JSON object with all validation results
  • data-validation-options - JSON object with all validation rules

💡 Complete Example

This example uses Tailwind CSS for styling but that is not required.

<formx-data="{ contactName: '', contactAge: '', contactMessage: '', contactTerms: false }"
class="max-w-xl mx-auto space-y-4"
@submit.prevent="$dispatch('validate')"
><divclass="group"><labelfor="contactName" class="font-medium"> Name </label><inputid="contactName"
type="text"
x-model="contactName"
x-validation.required="contactName"
class="w-full mt-1 rounded data-[validation-valid=true]:border-green-500 data-[validation-valid=false]:border-red-500"
/><smallclass="text-red-600 mt-1 hidden group-has-[[data-validation-valid=false]]:block"
>
Need a name
</small></div><divclass="group"><labelfor="contactAge" class="font-medium"> Age </label><inputid="contactAge"
type="number"
x-model="contactAge"
x-validation.min.18.max.24="contactAge"
class="w-full rounded mt-1 data-[validation-valid=true]:border-green-500 data-[validation-valid=false]:border-red-500"
/><smallclass="text-red-600 mt-1 group-has-[[data-validation-reason=min]]:block hidden"
>
Must be at least 18 years old
</small><smallclass="text-red-600 mt-1 group-has-[[data-validation-reason=max]]:block hidden"
>
Can't be older than 24 years old
</small></div><divclass="group"><labelfor="contactMessage" class="font-medium"> Message </label><textareaid="contactMessage"
x-model="contactMessage"
x-validation.min:length.10.max:length.50="contactMessage"
class="rounded w-full mt-1 data-[validation-valid=true]:border-green-500 data-[validation-valid=false]:border-red-500"
></textarea><smallclass="text-red-600 mt-1 group-has-[[data-validation-reason=minLength]]:block hidden"
>
Must be at least 10 characters
</small><smallclass="text-red-600 mt-1 group-has-[[data-validation-reason=maxLength]]:block hidden"
>
Can't be more than 50 characters
</small></div><divclass="group"><labelfor="contactTerms" class="inline-flex items-center gap-2"><inputid="contactTerms"
type="checkbox"
value="true"
x-model="contactTerms"
x-validation.checked="contactTerms"
class="size-5 rounded data-[validation-valid=false]:border-red-500"
/><spanclass="font-medium">I accept the terms and conditions</span></label><smallclass="text-red-600 mt-1 group-has-[[data-validation-valid=false]]:block hidden"
>
Must accept
</small></div><divclass="flex justify-end gap-4"><buttontype="reset" class="px-5 py-2.5 rounded font-medium text-blue-600">
Reset
</button><buttonclass="px-5 py-2.5 rounded bg-blue-600 font-medium text-white">
Submit
</button></div></form>

🎯 How It Works

Triggering Validation

$dispatch('validate')

Dispatch the validate event to trigger validation for all inputs within the target element:

<!-- Validate entire form --><form@submit.prevent="$dispatch('validate')"></form>

Real-time Validation

Validation automatically runs as users interact with inputs. The plugin tracks whether an input is "dirty" (has been interacted with) to avoid showing errors prematurely.

🎨 Styling with CSS

Basic Styling

/* Valid inputs */
[data-validation-valid='true'] {
border-color:#10b981;
}
/* Invalid inputs */
[data-validation-valid='false'] {
border-color:#ef4444;
}
/* Error messages (hidden by default) */
.error-message {
display: none;
color:#ef4444;
}
/* Show error when parent contains invalid input */
.form-group:has([data-validation-valid='false']) .error-message {
display: block;
}

Specific Error Messages

Target specific validation failures for tailored messaging:

/* Show specific error for minimum value */
.form-group:has([data-validation-reason='min']) .min-error {
display: block;
}
/* Show specific error for required field */
.form-group:has([data-validation-reason='required']) .required-error {
display: block;
}

Advanced Example with Tailwind CSS

<divclass="group"><inputx-model="email"
x-validation.required.min:length.5="email"
class="w-full border rounded px-3 py-2 data-[validation-valid=true]:border-green-500 data-[validation-valid=false]:border-red-500"
/><!-- Different error messages for different validation failures --><smallclass="text-red-600 mt-1 hidden group-has-[[data-validation-reason=required]]:block"
>
Email is required
</small><smallclass="text-red-600 mt-1 hidden group-has-[[data-validation-reason=minLength]]:block"
>
Email must be at least 5 characters
</small></div>

🌐 Browser Support

This plugin uses modern CSS features like :has() selector in the examples. For broader browser support, consider:

  • Using JavaScript to toggle classes instead of CSS :has()
  • Providing fallback styles for older browsers
  • Using utility CSS frameworks that handle browser compatibility

🤝 Contributing

Contributions are welcome! Please feel free to submit a Pull Request. For major changes, please open an issue first to discuss what you would like to change.

🔧 Advanced Usage

How Error Messages Are Shown

This plugin relies primarily on CSS to show or hide error messages. When an input is validated, data attributes such as data-validation-valid and data-validation-reason are set on the input element.

Why use data-validation-reason?

The data-validation-reason attribute is useful for inputs with multiple validation criteria. By targeting specific reasons, you can display different error messages for each type of validation failure, improving the user experience.

Example CSS

[data-validation-valid='false'] {
border-color: red;
}
[data-validation-valid='true'] {
border-color: green;
}
.group:has([data-validation-valid='false']) small {
display: block;
}
.groupsmall {
display: none;
}
.group:has([data-validation-reason='min']) .min-error {
display: block;
}

Note: The :has() CSS selector is supported in all modern browsers. For older browser support, consider alternative approaches.

About

A lightweight, CSS-driven form validation plugin for Alpine.js that provides real-time client-side validation with data attributes.

Topics

Resources

Stars

43 stars

Watchers

1 watching

Forks

Used by

Contributors

Languages