Tested and maintained, so you don't have to. $99/year.
Validation
Client-side form validation with 40+ rules that match Flick's server-side validation. Define once in PHP, validate in real-time on the browser. Supports multiple JavaScript frameworks.
Why Use This Service?
What you'd build yourself: 40+ JavaScript validation rules that mirror your PHP rules, real-time error display, ARIA accessibility, CSS framework integration, and adapters for Alpine/Vue/React.
Maintained for you: 40+ JavaScript rules kept in exact sync with their PHP counterparts - the part that drifts first when you maintain both by hand.
What Pro provides:
- 40+ rules identical to Flick's server-side validation
- Define once in PHP, validate on both client and server
- Adapters for vanilla JS, Alpine, Vue, and React
- Automatic CSS framework detection (Bootstrap, Tailwind, Bulma)
- ARIA accessibility built-in
- Debounced input handling
Installation
Flick Pro requires a license. See Pro installation for setup instructions.
Configuration
Validation works out of the box with zero configuration:
$config = [ 'services' => [ 'validation' => [] ] ]; $form = new Flick($config);
Full Configuration
$config = [ 'services' => [ 'validation' => [ 'adapter' => 'vanilla', // vanilla, alpine, vue, react, data-only 'delay' => 300, // Debounce delay in ms before validating 'validateOnBlur' => true, // Validate when a field loses focus instead of while typing // (typing re-validation kicks in after a failed blur) 'messageDivId' => 'errors', // Optional element id for a summary message 'css' => [ // Optional class overrides 'isValid' => 'is-valid', 'isInvalid' => 'is-invalid', ], ] ] ];
Basic Usage
1. Define Validation Rules
Use the same rules you use for server-side validation:
<?= $form->validation->scripts([ 'name' => ['required', 'min:2'], 'email' => ['required', 'email'], 'password' => ['required', 'strongPassword', 'confirmed'], 'password_confirmation' => ['required'] ]) ?>
2. Add to Your Form
Place the validation output before the closing </body> tag:
<!DOCTYPE html> <html> <head> <title>Registration</title> </head> <body> <?php $form->create('Name, Email, Password|password, Password Confirmation|password'); ?> <?= $form->validation->scripts([ 'name' => ['required', 'min:2'], 'email' => ['required', 'email'], 'password' => ['required', 'strongPassword', 'confirmed'], 'password_confirmation' => ['required'] ]) ?> </body> </html>
That's it! The form now validates in real-time as users type.
Custom Error Messages
<?= $form->validation->scripts([ 'name' => [ 'rules' => ['required', 'min:2'], 'messages' => [ 'required' => 'Please enter your name', 'min' => 'Name must be at least 2 characters' ] ], 'email' => [ 'rules' => ['required', 'email'], 'messages' => [ 'required' => 'Email is required', 'email' => 'Please enter a valid email address' ] ] ]) ?>
AJAX Form Submission
Submit forms via AJAX with built-in validation:
<?= $form->validation->scripts([ 'name' => ['required'], 'email' => ['required', 'email'] ]) ?> <?= $form->validation->submitAjax('/api/contact') ?>
The AJAX handler:
- Validates all fields before submission
- Submits the form data as a regular form post (
FormData) and expects a JSON response - Handles success/error responses
- Displays server-side errors
Adapters
Choose the adapter that matches your JavaScript framework.
Vanilla (Default)
Pure JavaScript with no dependencies. Works with any project.
$form->validation->useAdapter('vanilla');
Features:
- Real-time validation
- Debounced input handling
- ARIA accessibility attributes
- CSS framework auto-detection
- AJAX form submission
Alpine.js
Lightweight reactive validation using Alpine directives.
$form->validation->useAdapter('alpine');
<div x-data="flickValidation()"> <input x-model="form.name.value" @blur="validate('name')"> <span x-show="hasError('name')" x-text="getError('name')"></span> </div>
Field state lives at form.<field>.value; read errors with getError('field') /
hasError('field').
With several forms on one page, each component finds its own form automatically
when it sits inside an element carrying the form's id (like the <form> tag
Flick renders). You can also name it explicitly: x-data="flickValidation('myForm')".
Vue
Vue composable for reactive form state.
$form->validation->useAdapter('vue');
<script setup> const { register, errors } = useFlickValidation() </script> <template> <input v-bind="register('name')"> <span v-if="errors.name">{{ errors.name }}</span> </template>
register('field') wires up the value, blur/input validation, and CSS classes in
one go. The composable also returns form, validate, validateAll, isValid,
and hasError if you want manual control.
With several forms on one page, tell the composable which form it belongs to:
useFlickValidation('myForm'). With a single form the bare call works as shown.
React
React hook with form state management.
$form->validation->useAdapter('react');
function Form() {
const { register, errors, handleSubmit } = useFlickValidation();
return (
<form onSubmit={handleSubmit((data) => console.log(data))}>
<input {...register('name')} />
{errors.name && <span>{errors.name}</span>}
</form>
);
}
register('field') returns the value, onChange, onBlur, and className
props for an input. The hook also returns form, validate, validateAll, and
touched for manual control.
With several forms on one page, tell the hook which form it belongs to:
useFlickValidation('myForm'). With a single form the bare call works as shown.
Data-Only
Outputs only data attributes for custom implementations.
$form->validation->useAdapter('data-only');
scripts() returns an empty string in this mode — the attributes come from
getFieldAttributes(), which you apply yourself:
$attrs = $form->validation->getFieldAttributes( 'email', ['required', 'email'], ['required' => 'Email is required'] ); // [ // 'data-flick-field' => 'email', // 'data-flick-rules' => '{"required":true,"email":true}', // 'data-flick-messages' => '{"required":"Email is required"}', // ]
<input data-flick-field="email" data-flick-rules='{"required":true,"email":true}' data-flick-messages='{"required":"Email is required"}' >
Validation Rules
All 44 rules match Flick's server-side validation:
Required
| Rule | Description | Example |
|---|---|---|
required |
Field must have a value | required |
requiredWith:field |
Required if another field has a value | requiredWith:phone |
String Validation
| Rule | Description | Example |
|---|---|---|
min:N |
Minimum length | min:3 |
max:N |
Maximum length | max:255 |
exact:N |
Exact length | exact:10 |
alpha |
Letters only (spaces allowed) | alpha |
alphaDash |
Letters, numbers, dashes, underscores | alphaDash |
alphaNumeric |
Letters and numbers only (no spaces or symbols) | alphaNumeric |
startsWith:a,b |
Starts with one of the given values | startsWith:http,https |
endsWith:a,b |
Ends with one of the given values | endsWith:.com,.net |
regex:PATTERN |
Match regex pattern | regex:^[A-Z]+$ |
notRegex:PATTERN |
Not match regex pattern | notRegex:admin |
Numeric Validation
| Rule | Description | Example |
|---|---|---|
numeric |
Numeric value | numeric |
integer |
Integer only | integer |
digits:N |
Exactly N digits (leading zeros allowed) | digits:5 |
digitsBetween:MIN,MAX |
Digits with a length between MIN and MAX | digitsBetween:4,6 |
between:MIN,MAX |
Between two numbers | between:1,100 |
greaterThan:N |
Greater than | greaterThan:0 |
greaterThanOrEqual:N |
Greater than or equal | greaterThanOrEqual:18 |
lessThan:N |
Less than | lessThan:100 |
lessThanOrEqual:N |
Less than or equal | lessThanOrEqual:999 |
Format Validation
| Rule | Description | Example |
|---|---|---|
email |
Valid email address | email |
url |
Valid URL | url |
phone |
Valid phone number | phone |
date |
Valid date | date |
creditCard |
Valid credit card number | creditCard |
uuid |
Valid UUID | uuid |
json |
Valid JSON string | json |
ip |
Valid IP address (v4 or v6) | ip |
ipv4 |
Valid IPv4 address | ipv4 |
ipv6 |
Valid IPv6 address | ipv6 |
Date Validation
| Rule | Description | Example |
|---|---|---|
after:DATE |
After a date | after:2024-01-01 |
afterOrEqual:DATE |
After or equal to date | afterOrEqual:today |
before:DATE |
Before a date | before:2030-01-01 |
beforeOrEqual:DATE |
Before or equal to date | beforeOrEqual:today |
Comparison
| Rule | Description | Example |
|---|---|---|
equals:VALUE |
Must equal value | equals:yes |
matches:FIELD |
Must match another field | matches:email |
notMatches:FIELD |
Must not match another field | notMatches:username |
confirmed |
Must have a matching _confirmation field |
confirmed |
in:VAL1,VAL2 |
Must be one of values | in:red,green,blue |
notIn:VAL1,VAL2 |
Must not be one of values | notIn:admin,root |
Special
| Rule | Description | Example |
|---|---|---|
accepted |
Must be checked (checkboxes) | accepted |
boolean |
Must be true/false | boolean |
strongPassword |
Requires upper, lower, number, special | strongPassword |
Methods
scripts()
Generate client-side validation for fields.
$form->validation->scripts(array $fields): string
| Parameter | Type | Description |
|---|---|---|
$fields |
array |
Field names with their rules |
Returns: JavaScript/HTML for validation.
submitAjax()
Generate AJAX form submission handler.
$form->validation->submitAjax(string $action = '/'): string
| Parameter | Type | Description |
|---|---|---|
$action |
string |
Form submission endpoint |
Returns: JavaScript for AJAX submission.
Warning
Only works with the vanilla adapter. On any other adapter it throws
InvalidArgumentException: Adapter '<name>' does not support AJAX generation.
useAdapter()
Change adapter at runtime.
$form->validation->useAdapter(string $adapter): self
| Parameter | Type | Description |
|---|---|---|
$adapter |
string |
Adapter name |
Available adapters: vanilla, alpine, vue, react, data-only
getFieldAttributes()
Get the HTML attributes a single field needs, for the current adapter.
$form->validation->getFieldAttributes( string $fieldName, array $rules, array $messages = [] ): array
What comes back depends on the adapter:
| Adapter | Returns |
|---|---|
vanilla |
[] — everything lives in the generated script, nothing goes on the field |
alpine |
x-model, x-on:blur, x-on:input, :class, data-flick-rules |
vue |
v-model, @blur, @input, :class, data-flick-rules |
react |
data-flick-field, data-flick-rules, data-flick-messages |
data-only |
data-flick-field, data-flick-rules, data-flick-messages |
An empty array on vanilla is expected, not a failure.
getAdapterName()
Get the current adapter name.
$form->validation->getAdapterName(): string
getAdapter()
Get the current adapter instance.
$form->validation->getAdapter(): AdapterInterface
Complete Example
<?php require 'vendor/autoload.php'; $form = new Flick\Flick([ 'services' => [ 'validation' => [ 'adapter' => 'vanilla', 'delay' => 300 ] ] ]); // Handle server-side validation if ($form->submitted()) { $request = $form->request(' Name[required, min:2], Email[required, email], Password[required, strongPassword, confirmed], Password Confirmation[required], Terms[accepted] '); if ($form->ok()) { // Process registration... $form->successMessage('Account created!'); } } ?> <!DOCTYPE html> <html> <head> <title>Register</title> <link href="https://cdn.jsdelivr.net/npm/bootstrap@5/dist/css/bootstrap.min.css" rel="stylesheet"> </head> <body> <div class="container mt-5"> <h1>Create Account</h1> <?php if ($form->errorsIsNotEmpty()): ?> <div class="alert alert-danger"> <ul> <?php foreach ($form->getErrors() as $error): ?> <li><?= htmlspecialchars($error) ?></li> <?php endforeach; ?> </ul> </div> <?php endif; ?> <?php $form->create(' Name, Email, Password|password, Password Confirmation|password, Terms|checkbox '); ?> </div> <!-- Client-side validation --> <?= $form->validation->scripts([ 'name' => [ 'rules' => ['required', 'min:2'], 'messages' => [ 'required' => 'Please enter your name', 'min' => 'Name must be at least 2 characters' ] ], 'email' => ['required', 'email'], 'password' => [ 'rules' => ['required', 'strongPassword', 'confirmed'], 'messages' => [ 'strongPassword' => 'Password needs uppercase, lowercase, number, and special character' ] ], 'password_confirmation' => ['required'], 'terms' => [ 'rules' => ['accepted'], 'messages' => [ 'accepted' => 'You must accept the terms' ] ] ]) ?> </body> </html>
Features
- Real-Time Validation: Errors appear as users type
- Debounced Input: Prevents excessive validation calls
- ARIA Accessibility: Screen reader support built-in
- CSS Framework Detection: Works with Bootstrap, Tailwind, Bulma
- Server-Side Parity: Same rules work on client and server
- Custom Messages: Full control over error text
- AJAX Support: Submit forms without page reload