Validation

Gale integrates seamlessly with Laravel's validation system. Validation errors automatically appear next to form fields using the x-message directive, with no JavaScript code required.

Basic Validation

Use Laravel's standard validation in your controller. Gale automatically sends validation errors to the frontend when validation fails.

public function register(Request $request)
{
    // Standard Laravel validation
    $validated = $request->validate([
        'name' => 'required|string|max:255',
        'email' => 'required|email|unique:users',
        'password' => 'required|min:8|confirmed',
    ]);

    // Process on success...
    User::create($validated);

    return gale()->navigate('/dashboard');
}

The form displays errors automatically:

<form x-data="{ name: '', email: '', password: '', password_confirmation: '' }"
      @submit.prevent="$postx('/register')">

    <div>
        <input type="text" x-model="name" placeholder="Name">
        <span x-message="name" class="text-red-500"></span>
    </div>

    <div>
        <input type="email" x-model="email" placeholder="Email">
        <span x-message="email" class="text-red-500"></span>
    </div>

    <div>
        <input type="password" x-model="password" placeholder="Password">
        <span x-message="password" class="text-red-500"></span>
    </div>

    <div>
        <input type="password" x-model="password_confirmation" placeholder="Confirm Password">
    </div>

    <button type="submit">Register</button>
</form>

Form Request Validation

For complex validation, use Laravel's Form Request classes. Gale handles them identically:

// app/Http/Requests/RegisterRequest.php
class RegisterRequest extends FormRequest
{
    public function rules(): array
    {
        return [
            'name' => 'required|string|max:255',
            'email' => 'required|email|unique:users',
            'password' => ['required', 'confirmed', Password::defaults()],
        ];
    }

    public function messages(): array
    {
        return [
            'email.unique' => 'This email is already registered.',
        ];
    }
}
// Controller
public function register(RegisterRequest $request)
{
    // Validation already passed
    User::create($request->validated());

    return gale()->navigate('/dashboard');
}

Nested Field Validation

Laravel's dot notation for nested fields works seamlessly with x-message:

<form x-data="{
    user: { name: '', email: '' },
    address: { street: '', city: '', zip: '' }
}" @submit.prevent="$postx('/save')">

    <input x-model="user.name" placeholder="Name">
    <span x-message="user.name" class="error"></span>

    <input x-model="user.email" placeholder="Email">
    <span x-message="user.email" class="error"></span>

    <input x-model="address.street" placeholder="Street">
    <span x-message="address.street" class="error"></span>

    <button type="submit">Save</button>
</form>
// Backend validation rules
$request->validate([
    'user.name' => 'required|string',
    'user.email' => 'required|email',
    'address.street' => 'required|string',
    'address.city' => 'required|string',
    'address.zip' => 'required|string|size:5',
]);

Array Validation

For dynamic arrays like repeatable fields, use template literals in x-message:

<form x-data="{ items: [{ name: '', price: '' }] }"
      @submit.prevent="$postx('/save-items')">

    <template x-for="(item, index) in items" :key="index">
        <div class="item-row">
            <input x-model="item.name" placeholder="Item name">
            <!-- Dynamic field key using template literal -->
            <span x-message=`items.${index}.name` class="error"></span>

            <input x-model="item.price" placeholder="Price">
            <span x-message=`items.${index}.price` class="error"></span>
        </div>
    </template>

    <button type="button" @click="items.push({ name: '', price: '' })">
        Add Item
    </button>
    <button type="submit">Save</button>
</form>
// Laravel validation for arrays
$request->validate([
    'items' => 'required|array|min:1',
    'items.*.name' => 'required|string|max:255',
    'items.*.price' => 'required|numeric|min:0',
]);

// Errors are returned as: 'items.0.name', 'items.1.price', etc.

Custom Error Messages

Send custom messages for business logic validation beyond Laravel's built-in rules:

public function applyDiscount(Request $request)
{
    $code = $request->input('coupon_code');
    $coupon = Coupon::where('code', $code)->first();

    if (!$coupon) {
        return gale()->messages([
            'coupon_code' => 'This coupon code is invalid.',
        ]);
    }

    if ($coupon->isExpired()) {
        return gale()->messages([
            'coupon_code' => 'This coupon has expired.',
        ]);
    }

    if ($coupon->usage_limit_reached) {
        return gale()->messages([
            'coupon_code' => 'This coupon has reached its usage limit.',
        ]);
    }

    // Apply discount...
    return gale()
        ->state('discount', $coupon->amount)
        ->clearMessages();
}

Clearing Messages

Clear validation messages after successful submission or when starting fresh:

// Clear all messages
gale()->clearMessages();

// Clear specific fields
gale()->messages([
    'email' => null,  // Clears email message
    'name' => null,   // Clears name message
]);

// Send success and clear errors
gale()
    ->clearMessages()
    ->messages(['_success' => 'Saved successfully!']);

Message Types

Use underscore-prefixed keys for special message types:

Key Purpose Example
_success Success messages Form saved!
_error General errors Something went wrong.
_warning Warning messages Session expires in 5 minutes.
_info Informational Changes will take effect after refresh.
<!-- Display different message types -->
<div x-message="_success" class="bg-green-100 text-green-700 p-3"></div>
<div x-message="_error" class="bg-red-100 text-red-700 p-3"></div>
<div x-message="_warning" class="bg-yellow-100 text-yellow-700 p-3"></div>
<div x-message="_info" class="bg-blue-100 text-blue-700 p-3"></div>

Auto-Show and Auto-Hide

The x-message directive automatically shows and hides based on whether a message exists. Elements are hidden with display: none when empty.

<!-- Hidden when no error, shown when error exists -->
<span x-message="email" class="error"></span>

<!-- Use x-show for custom visibility logic -->
<div x-data="{ messages: {} }">
    <div x-show="messages.email" class="error-box">
        <span x-message="email"></span>
    </div>
</div>

Complete Validation Example

<form
    x-data="{
        name: '',
        email: '',
        password: '',
        password_confirmation: ''
    }"
    @submit.prevent="$postx('/register')"
    class="space-y-4 max-w-md">

    <!-- Global messages -->
    <div x-message="_success" class="bg-green-50 border border-green-200 p-3 rounded"></div>
    <div x-message="_error" class="bg-red-50 border border-red-200 p-3 rounded"></div>

    <!-- Name -->
    <div>
        <label class="block font-medium">Name</label>
        <input
            type="text"
            x-model="name"
            class="border rounded w-full p-2">
        <span x-message="name" class="text-red-500 text-sm"></span>
    </div>

    <!-- Email -->
    <div>
        <label class="block font-medium">Email</label>
        <input
            type="email"
            x-model="email"
            class="border rounded w-full p-2">
        <span x-message="email" class="text-red-500 text-sm"></span>
    </div>

    <!-- Password -->
    <div>
        <label class="block font-medium">Password</label>
        <input
            type="password"
            x-model="password"
            class="border rounded w-full p-2">
        <span x-message="password" class="text-red-500 text-sm"></span>
    </div>

    <!-- Confirm Password -->
    <div>
        <label class="block font-medium">Confirm Password</label>
        <input
            type="password"
            x-model="password_confirmation"
            class="border rounded w-full p-2">
    </div>

    <!-- Submit -->
    <button
        type="submit"
        x-loading.disabled
        class="bg-blue-500 text-white px-4 py-2 rounded">
        <span x-loading.remove>Register</span>
        <span x-loading>Creating account...</span>
    </button>
</form>

On this page