Flick
Guide Validation Services API Pro Examples GitHub
Docs / Services
Flick Pro Service

Tested and maintained, so you don't have to. $99/year.

Get Flick Pro

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