Laravel Form Validation: 2026 Helpful Guide with Examples

Laravel Form Validation: 2026 Helpful Guide with Examples

Laravel Form Validation is one of the most important features to understand when building reliable Laravel applications. Whether you are creating a registration form, login form, contact form, checkout page, product form, profile editor, or REST API, validating user input is essential.

Table of Contents

Laravel provides a powerful form validation system with hundreds of built-in rules, readable syntax, custom error messages, conditional validation, Form Request classes, file validation, nested array validation, database-aware rules, and custom form validation rules.

In this guide, we will learn Laravel form validation from the basics to more advanced techniques, with practical examples you can use in real-world applications.

Laravel version note: The examples in this article follow modern Laravel validation practices and are aligned with the current Laravel documentation. Laravel’s validation API continues to evolve, so always check the official documentation when working with a specific Laravel version.

What Is Laravel Form Validation?

Laravel Form validation is the process of checking whether data submitted by a user satisfies specific requirements before the application processes or stores it.

For example, suppose you have a registration form:

Name
Email
Password
Confirm Password

You may want to enforce rules such as:

  • Name is required.
  • Name must be a string.
  • Name must be at least three characters.
  • Email is required.
  • Email must be valid.
  • Email must be unique.
  • Password is required.
  • Password must meet your application’s password requirements.
  • Password confirmation must match.

Instead of manually writing PHP if statements for every condition, Laravel provides a dedicated validation system.

A simple example looks like this:

$request->validate([
    'name' => ['required', 'string', 'max:255'],
    'email' => ['required', 'email'],
    'password' => ['required', 'confirmed'],
]);

If validation succeeds, Laravel continues processing the request.

If validation fails during a normal web request, Laravel automatically redirects the user back and makes validation errors available to the view. For XHR requests, Laravel returns validation errors in JSON with a 422 response.

Why Is Laravel Form Validation Important?

Never assume that data submitted by a browser is valid.

Client-side laravel form validation can improve user experience, but it should never be your only validation layer.

A malicious user can bypass JavaScript validation and send a request directly to your Laravel application.

Server-side Laravel validation helps protect your application from:

  • Invalid data
  • Missing fields
  • Incorrect data types
  • Unexpected values
  • Duplicate records
  • Invalid file uploads
  • Incorrect relationships
  • Business-rule violations
  • Malformed API requests

For example, this JavaScript validation:

if (email === '') {
    alert('Email is required');
}

is useful for the user interface but is not sufficient for application security.

The server should still validate the request:

$request->validate([
    'email' => ['required', 'email'],
]);

A good rule is:

Client-side validation improves UX; server-side validation protects application integrity.


How Laravel Form Validation Works

The basic Laravel validation flow is:

User submits form
       ↓
Laravel receives HTTP request
       ↓
Validation rules are executed
       ↓
       ├── Validation passes → Continue application logic
       │
       └── Validation fails → Return validation errors

Laravel provides several ways to perform validation.

The most common approaches are:

  1. $request->validate()
  2. Form Request classes
  3. Validator facade
  4. Laravel Custom validation rules

For small controllers, $request->validate() is usually convenient.

For larger applications, Form Requests provide a cleaner structure.


Creating a Laravel Form

Let’s create a simple contact form.

Suppose we have the following routes:

use App\Http\Controllers\ContactController;
use Illuminate\Support\Facades\Route;

Route::get('/contact', [ContactController::class, 'create'])
    ->name('contact.create');

Route::post('/contact', [ContactController::class, 'store'])
    ->name('contact.store');

Create the controller:

php artisan make:controller ContactController

Then:

namespace App\Http\Controllers;

use Illuminate\Http\Request;

class ContactController extends Controller
{
    public function create()
    {
        return view('contact');
    }

    public function store(Request $request)
    {
        // Validation will go here
    }
}

Now create:

resources/views/contact.blade.php

Example form:

<form method="POST" action="{{ route('contact.store') }}">
    @csrf

    <div>
        <label for="name">Name</label>

        <input
            type="text"
            id="name"
            name="name"
        >
    </div>

    <div>
        <label for="email">Email</label>

        <input
            type="email"
            id="email"
            name="email"
        >
    </div>

    <div>
        <label for="message">Message</label>

        <textarea
            id="message"
            name="message"
        ></textarea>
    </div>

    <button type="submit">
        Send Message
    </button>
</form>

Basic Laravel Validation

Now let’s add validation.

public function store(Request $request)
{
    $validated = $request->validate([
        'name' => ['required', 'string', 'max:255'],
        'email' => ['required', 'email'],
        'message' => ['required', 'string', 'min:10'],
    ]);

    // Process validated data
}

Here:

'name' => ['required', 'string', 'max:255']

means:

  • required: the field must be present and non-empty.
  • string: the value must be a string.
  • max:255: the maximum size is 255 characters.

The validate() method returns the validated data when validation succeeds.

For example:

$validated = $request->validate([
    'name' => ['required', 'string'],
    'email' => ['required', 'email'],
]);

You can then use:

$validated['name'];
$validated['email'];

This is preferable to blindly processing every value in the request.

Laravel Validation Rules

Laravel validation rules can be written as strings:

'email' => 'required|email'

or as arrays:

'email' => ['required', 'email']

The array syntax is often easier to maintain when rules become more complex.

For example:

'username' => [
    'required',
    'string',
    'min:3',
    'max:50',
]

Laravel also provides fluent rule objects for more advanced scenarios:

use Illuminate\Validation\Rule;

For example:

'role' => [
    'required',
    Rule::in(['admin', 'editor', 'author']),
]

Common Laravel Validation Rules

Here are some of the most frequently used validation rules.

Rule Purpose
required Field must exist and contain a value
nullable Field can contain null
string Value must be a string
integer Value must be an integer
numeric Value must be numeric
boolean Value must represent a boolean
array Value must be an array
email Validates email format
url Validates URL
date Validates date
regex Validates against regular expression
min Minimum size/value
max Maximum size/value
between Value must be within a range
in Value must exist in a list
not_in Value must not exist in a list
same Must match another field
different Must differ from another field
confirmed Requires matching confirmation field
unique Checks database uniqueness
exists Checks database existence
file Validates uploaded file
image Validates image
mimes Validates MIME type
required_if Required when another field matches
required_with Required when another field exists
required_without Required when another field is missing

Laravel’s current validation documentation contains a large collection of built-in rules, including rules for strings, arrays, files, dates, passwords, database values, conditional input, and more.


The required Rule

The required rule is probably the most commonly used Laravel validation rule.

'name' => ['required']

It means the field must be present and must not be empty.

Example:

$request->validate([
    'name' => ['required'],
]);

If the user submits an empty name, laravel custom validation fails.


The string Rule

Use string when a value should be text.

'name' => ['required', 'string']

You can combine it with length rules:

'name' => [
    'required',
    'string',
    'min:3',
    'max:100',
]

This is useful for names, titles, descriptions, usernames, and similar fields.


The email Rule

For email addresses:

'email' => [
    'required',
    'email',
]

Laravel’s email validation uses the egulias/email-validator package. Laravel also supports additional email validation styles such as RFC and DNS validation.

For example:

'email' => [
    'required',
    'email:rfc,dns',
]

Be careful with DNS validation because it requires DNS-related checks and may not be appropriate for every application.


The integer and numeric Rules

Use integer when a value must be an integer:

'age' => [
    'required',
    'integer',
]

Use numeric when decimal values should also be accepted:

'price' => [
    'required',
    'numeric',
]

For a product price:

'price' => [
    'required',
    'numeric',
    'min:0',
]

Modern Laravel also provides more specialized rules and fluent builders for numeric constraints.


Minimum and Maximum Laravel Validation

You can use min and max with different types of values.

For a string:

'title' => [
    'required',
    'string',
    'min:5',
    'max:100',
]

For a number:

'age' => [
    'required',
    'integer',
    'min:18',
]

For a file:

'photo' => [
    'required',
    'file',
    'max:2048',
]

The meaning of size-related rules depends on whether the validated value is a string, numeric value, array, or file.


Displaying Laravel Validation Errors in Blade

Laravel automatically makes validation errors available through the $errors variable.

You can display all errors:

@if ($errors->any())
    <div>
        <ul>
            @foreach ($errors->all() as $error)
                <li>{{ $error }}</li>
            @endforeach
        </ul>
    </div>
@endif

You can display the first error for a specific field:

@error('email')
    <p>{{ $message }}</p>
@enderror

This is often cleaner.

For example:

<div>
    <label for="email">Email</label>

    <input
        type="email"
        name="email"
        id="email"
    >

    @error('email')
        <p>{{ $message }}</p>
    @enderror
</div>

Laravel’s validation system provides a MessageBag for working with validation errors, including retrieving the first error for a specific attribute.


Adding Error Styling

A common Blade pattern is to add a class when a field has an error:

<input
    type="email"
    name="email"
    class="@error('email') border-red-500 @enderror"
    value="{{ old('email') }}"
>

You can also use:

@if ($errors->has('email'))
    ...
@endif

This makes it easy to visually identify invalid fields.


Repopulating Form Fields with old()

One frustrating user experience is losing all form data after a laravel form validation failure.

Laravel solves this using flashed input and the old() helper.

<input
    type="text"
    name="name"
    value="{{ old('name') }}"
>

For email:

<input
    type="email"
    name="email"
    value="{{ old('email') }}"
>

Laravel’s old() helper retrieves input from the previous request, allowing forms to be repopulated after validation failure.

A complete field might look like:

<div>
    <label>Name</label>

    <input
        type="text"
        name="name"
        value="{{ old('name') }}"
    >

    @error('name')
        <p>{{ $message }}</p>
    @enderror
</div>

Never repopulate password fields:

<input
    type="password"
    name="password"
>

Custom Laravel Validation Error Messages

Laravel provides default validation messages, but you may want application-specific messages.

One approach is to pass custom messages to the validator.

$request->validate(
    [
        'email' => ['required', 'email'],
        'name' => ['required'],
    ],
    [
        'email.required' => 'Please enter your email address.',
        'email.email' => 'Please enter a valid email address.',
        'name.required' => 'Please enter your name.',
    ]
);

This gives you complete control over the user-facing messages.

Laravel’s validation messages can also be customized through the application’s language files.


Custom Attribute Names

Sometimes Laravel’s default attribute name is not user-friendly.

Instead of:

The first_name field is required.

you may want:

The first name field is required.

You can customize attribute names.

$request->validate(
    [
        'first_name' => ['required'],
    ],
    [],
    [
        'first_name' => 'first name',
    ]
);

This is particularly useful when field names contain underscores or internal naming conventions.


Using the bail Rule

Suppose you have:

'title' => [
    'required',
    'unique:posts',
    'max:255',
]

Laravel evaluates rules in order.

You can use bail to stop validation for that field after the first failure:

'title' => [
    'bail',
    'required',
    'unique:posts',
    'max:255',
]

For example, if title is empty, Laravel doesn’t need to continue checking subsequent rules for that field.

Laravel explicitly documents bail as a way to stop further validation rules for an attribute after the first failure.


Using nullable for Optional Fields

Consider an optional publication date:

'publish_at' => [
    'date',
]

If the field is submitted as null, validation can fail because date expects a valid date.

Use:

'publish_at' => [
    'nullable',
    'date',
]

Now the field can be either:

  • null
  • A valid date

Laravel’s default middleware also trims strings and converts empty strings to null, which makes nullable particularly important for optional fields.

Validating Checkboxes

Suppose you have:

<input type="checkbox" name="terms" value="1">

You can validate:

'terms' => [
    'accepted',
]

This is useful for terms and conditions.

Example:

$request->validate([
    'terms' => ['accepted'],
]);

Validating Passwords

For password validation, Laravel provides the Password rule.

use Illuminate\Validation\Rules\Password;

$request->validate([
    'password' => [
        'required',
        'confirmed',
        Password::min(8),
    ],
]);

You can create stronger requirements:

Password::min(8)
    ->mixedCase()
    ->numbers()
    ->symbols()

This is more expressive than manually creating complex regular expressions.


Confirming Passwords

Laravel’s confirmed rule is useful for password confirmation.

Validation:

'password' => [
    'required',
    'confirmed',
]

Your form should contain:

<input
    type="password"
    name="password"
>

<input
    type="password"
    name="password_confirmation"
>

Laravel checks whether the confirmation field matches the original password.

The confirmed rule expects a corresponding {field}_confirmation field by default.


Validating Dates

Basic date validation:

'date_of_birth' => [
    'required',
    'date',
]

You can also compare dates.

For example:

'end_date' => [
    'required',
    'date',
    'after:start_date',
]

This ensures the end date occurs after the start date.

Modern Laravel also provides fluent date rule builders for expressing date constraints more clearly.


Conditional Validation

Sometimes a field is required only under certain conditions.

Suppose users can choose:

Contact Method:
Email
Phone

If they select phone, a phone number should be required.

You can use:

'phone' => [
    'required_if:contact_method,phone',
]

Example:

$request->validate([
    'contact_method' => ['required', 'in:email,phone'],

    'email' => [
        'required_if:contact_method,email',
        'email',
    ],

    'phone' => [
        'required_if:contact_method,phone',
    ],
]);

Laravel supports several conditional rules, including required_if, required_unless, required_with, and required_without.

Using Rule::requiredIf

For more complicated conditions, use Rule::requiredIf.

use Illuminate\Validation\Rule;

$request->validate([
    'role_id' => [
        Rule::requiredIf($request->user()->is_admin),
    ],
]);

Closures can also be used:

'role_id' => [
    Rule::requiredIf(fn () => $request->user()->is_admin),
]

This is useful when conditional validation depends on application logic rather than a simple field comparison.


Validating File Uploads

File uploads require special attention because users can submit files that don’t match the expected format.

Basic file validation:

$request->validate([
    'document' => [
        'required',
        'file',
        'max:5120',
    ],
]);

The value of max for files is measured in kilobytes.

You can also validate extensions:

'document' => [
    'required',
    'file',
    'extensions:pdf,doc,docx',
]

However, don’t rely only on the filename extension. Laravel’s documentation specifically notes that extension validation should generally be combined with MIME-related validation when appropriate.


Validating Images

For an image upload:

'avatar' => [
    'required',
    'image',
]

You can add size restrictions:

'avatar' => [
    'required',
    'image',
    'max:2048',
]

You can also use the dimensions rule:

use Illuminate\Validation\Rule;

'avatar' => [
    'required',
    'image',
    Rule::dimensions()
        ->maxWidth(1000)
        ->maxHeight(1000),
]

Laravel provides fluent image-dimension validation options for width, height, and aspect ratio.


Validating Arrays

Suppose your form submits:

tags[]
tags[]
tags[]

You can validate:

'tags' => [
    'required',
    'array',
]

Then validate every element:

'tags.*' => [
    'string',
    'max:50',
]

Example:

$request->validate([
    'tags' => ['required', 'array'],
    'tags.*' => ['string', 'max:50'],
]);

Laravel supports wildcard validation for array elements.


Validating Nested Input

Suppose the request contains:

[
    'author' => [
        'name' => 'John',
        'email' => 'john@example.com',
    ],
]

You can validate it using dot notation:

$request->validate([
    'author.name' => ['required', 'string'],
    'author.email' => ['required', 'email'],
]);

Laravel uses dot notation for nested request attributes.


Validating Dynamic Form Rows

This is useful for invoices, orders, shopping carts, and product variants.

Example request:

[
    'products' => [
        [
            'product_id' => 1,
            'quantity' => 2,
        ],
        [
            'product_id' => 5,
            'quantity' => 4,
        ],
    ],
]

Validation:

$request->validate([
    'products' => ['required', 'array'],

    'products.*.product_id' => [
        'required',
        'integer',
    ],

    'products.*.quantity' => [
        'required',
        'integer',
        'min:1',
    ],
]);

This approach is extremely useful for dynamic forms.


Database Validation with exists

Suppose a product belongs to a category.

You can ensure the category exists:

'category_id' => [
    'required',
    'exists:categories,id',
]

This prevents invalid category IDs from being accepted.

A more expressive version uses Rule:

use Illuminate\Validation\Rule;

'category_id' => [
    'required',
    Rule::exists('categories', 'id'),
]

Database Validation with unique

Suppose user emails must be unique:

'email' => [
    'required',
    'email',
    'unique:users,email',
]

Laravel checks whether the email already exists in the users table.

This is especially important for:

  • Registration
  • Usernames
  • Product SKUs
  • Slugs
  • Coupon codes
  • Order numbers

Unique Validation During Updates

One common mistake occurs when updating an existing record.

Suppose user ID 10 already has:

john@example.com

If you use:

'email' => [
    'required',
    'email',
    'unique:users,email',
]

Laravel may reject the user’s existing email because it already exists.

You can ignore the current record.

use Illuminate\Validation\Rule;

'email' => [
    'required',
    'email',
    Rule::unique('users', 'email')
        ->ignore($user->id),
]

This allows the current user to keep their existing email while still preventing another user from using it.


Form Request Validation

When validation becomes complex, putting everything inside a controller can make the controller difficult to maintain.

Laravel provides Form Request classes specifically for this purpose.

Create one:

php artisan make:request StorePostRequest

Laravel creates the request class under:

app/Http/Requests/StorePostRequest.php

Example:

namespace App\Http\Requests;

use Illuminate\Foundation\Http\FormRequest;

class StorePostRequest extends FormRequest
{
    public function authorize(): bool
    {
        return true;
    }

    public function rules(): array
    {
        return [
            'title' => [
                'required',
                'string',
                'max:255',
            ],

            'body' => [
                'required',
                'string',
            ],
        ];
    }
}

Then inject the request into your controller:

use App\Http\Requests\StorePostRequest;

public function store(StorePostRequest $request)
{
    $validated = $request->validated();

    // Store post
}

Laravel validates the Form Request before the controller method is executed.

This keeps controllers clean.


Authorizing Form Requests

A Form Request contains an authorize() method.

public function authorize(): bool
{
    return true;
}

You can use this method for authorization logic.

For example:

public function authorize(): bool
{
    return auth()->user()?->can('create', Post::class) ?? false;
}

This separates authorization from validation.

That is especially useful in applications with policies and permissions.


Custom Messages in Form Requests

Instead of defining custom messages inside the controller, you can define them in the Form Request:

public function messages(): array
{
    return [
        'title.required' => 'Please enter a post title.',
        'title.max' => 'The post title cannot exceed 255 characters.',
        'body.required' => 'Please enter the post content.',
    ];
}

This keeps validation-related configuration together.


Preparing Data Before Validation

Sometimes incoming input needs to be transformed before validation.

A Form Request can prepare the data before validation.

For example:

protected function prepareForValidation(): void
{
    $this->merge([
        'slug' => strtolower($this->slug),
    ]);
}

This can be useful for:

  • Normalizing input
  • Converting formats
  • Cleaning values
  • Preparing derived fields

Be careful not to use preprocessing as a replacement for proper validation.


Working with Validated Data

After validation, prefer validated data rather than the entire request.

For example:

$validated = $request->validated();

Or:

$validated = $request->safe()->only([
    'title',
    'body',
]);

You can also retrieve validated data as a collection:

$collection = $request->safe()->collect();

Laravel’s validation API provides safe(), only(), except(), merge(), and collect() methods for working with validated input.


Manual Validator

For more control, Laravel provides the Validator facade.

use Illuminate\Support\Facades\Validator;

$validator = Validator::make(
    $request->all(),
    [
        'name' => ['required', 'string'],
        'email' => ['required', 'email'],
    ]
);

Then:

if ($validator->fails()) {
    return back()->withErrors($validator);
}

You can also retrieve validated data:

$validated = $validator->validated();

Manual validation is useful when:

  • Validation happens outside normal controller flow.
  • You need custom validation behavior.
  • Multiple validation stages are required.
  • You are building reusable application services.

Laravel Validation for APIs

Laravel validation also works very well for REST APIs.

Example:

public function store(Request $request)
{
    $validated = $request->validate([
        'name' => ['required', 'string'],
        'email' => ['required', 'email'],
    ]);

    return response()->json([
        'message' => 'User created successfully.',
        'data' => $validated,
    ]);
}

When validation fails for an XHR/API-style request, Laravel returns validation errors using a 422 Unprocessable Entity response.

A frontend can therefore receive something conceptually like:

{
    "message": "The given data was invalid.",
    "errors": {
        "email": [
            "The email field must be a valid email address."
        ]
    }
}

This structure works well with React, Vue, mobile applications, and other API clients.


Custom Validation Rules

Built-in validation rules cover most common requirements.

Sometimes your application needs a business-specific rule.

Examples:

  • Username must not contain reserved words.
  • Coupon must be valid for a particular customer.
  • Employee ID must follow a company-specific pattern.
  • Product SKU must follow a custom format.

You can create a custom validation rule.

php artisan make:rule ValidUsername

Example rule:

namespace App\Rules;

use Closure;
use Illuminate\Contracts\Validation\ValidationRule;

class ValidUsername implements ValidationRule
{
    public function validate(
        string $attribute,
        mixed $value,
        Closure $fail
    ): void {
        if (in_array(strtolower($value), ['admin', 'root', 'system'])) {
            $fail('The :attribute cannot use a reserved username.');
        }
    }
}

Use it:

use App\Rules\ValidUsername;

$request->validate([
    'username' => [
        'required',
        'string',
        new ValidUsername,
    ],
]);

This is much cleaner than placing complex business logic directly inside a controller.


Real-World Registration Form Example

Let’s put several concepts together.

Controller

use Illuminate\Http\Request;
use Illuminate\Validation\Rules\Password;

public function register(Request $request)
{
    $validated = $request->validate([
        'name' => [
            'required',
            'string',
            'min:3',
            'max:100',
        ],

        'email' => [
            'required',
            'email',
            'unique:users,email',
        ],

        'password' => [
            'required',
            'confirmed',
            Password::min(8)
                ->mixedCase()
                ->numbers()
                ->symbols(),
        ],

        'terms' => [
            'accepted',
        ],
    ]);

    // Create user...
}

Blade Form

<form method="POST" action="{{ route('register') }}">
    @csrf

    <div>
        <label>Name</label>

        <input
            type="text"
            name="name"
            value="{{ old('name') }}"
        >

        @error('name')
            <p>{{ $message }}</p>
        @enderror
    </div>

    <div>
        <label>Email</label>

        <input
            type="email"
            name="email"
            value="{{ old('email') }}"
        >

        @error('email')
            <p>{{ $message }}</p>
        @enderror
    </div>

    <div>
        <label>Password</label>

        <input
            type="password"
            name="password"
        >

        @error('password')
            <p>{{ $message }}</p>
        @enderror
    </div>

    <div>
        <label>Confirm Password</label>

        <input
            type="password"
            name="password_confirmation"
        >
    </div>

    <div>
        <label>
            <input
                type="checkbox"
                name="terms"
                value="1"
            >

            I agree to the terms and conditions.
        </label>

        @error('terms')
            <p>{{ $message }}</p>
        @enderror
    </div>

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

This gives you server-side validation for the major registration requirements.


Real-World Product Form Example

Consider a product creation form:

$request->validate([
    'name' => [
        'required',
        'string',
        'max:255',
    ],

    'sku' => [
        'required',
        'string',
        'max:100',
        'unique:products,sku',
    ],

    'price' => [
        'required',
        'numeric',
        'min:0',
    ],

    'category_id' => [
        'required',
        'integer',
        'exists:categories,id',
    ],

    'description' => [
        'nullable',
        'string',
    ],

    'image' => [
        'nullable',
        'image',
        'max:2048',
    ],
]);

This example demonstrates several important validation concepts:

  • Required fields
  • String validation
  • Unique database values
  • Numeric validation
  • Minimum values
  • Foreign-key validation
  • Optional fields
  • File validation

Laravel Form Validation Best Practices

1. Always Validate Server-Side

Never rely solely on JavaScript validation.

Always validate incoming data in Laravel.


2. Validate Before Database Operations

Don’t insert data first and validate afterward.

Bad:

User::create($request->all());

$request->validate([
    'email' => ['required', 'email'],
]);

Good:

$validated = $request->validate([
    'email' => ['required', 'email'],
]);

User::create($validated);

3. Don’t Automatically Store $request->all()

Avoid:

User::create($request->all());

Prefer:

$validated = $request->validate([
    'name' => ['required', 'string'],
    'email' => ['required', 'email'],
]);

User::create($validated);

This makes it clear which fields your application accepts.


4. Use Form Requests for Complex Forms

If a controller contains dozens of validation rules, move them into a Form Request.

Instead of:

public function store(Request $request)
{
    // 50 lines of validation...
}

use:

public function store(StoreProductRequest $request)
{
    $validated = $request->validated();

    // Business logic
}

This improves readability and maintainability.


5. Use nullable for Optional Fields

Don’t assume optional means “no validation.”

For example:

'phone' => [
    'nullable',
    'string',
]

This means:

  • It can be empty/null.
  • If provided, it must be a string.

6. Use Database Rules

If an ID must exist:

'exists:categories,id'

If a value must be unique:

'unique:users,email'

Database validation is important because client-side checks cannot guarantee database consistency.


7. Validate Uploaded Files Carefully

Don’t simply trust:

.jpg
.png
.pdf

Validate uploaded files using Laravel’s file-related rules and appropriate MIME/content checks. Laravel specifically recommends not relying on the user-provided extension alone.


8. Return User-Friendly Errors

Technical field names aren’t always appropriate for users.

Instead of:

first_name.required

provide a clear message:

Please enter your first name.

9. Use old() Carefully

Repopulate ordinary form fields:

value="{{ old('name') }}"

But don’t repopulate sensitive values such as passwords.


10. Validate Business Rules

Validation isn’t only about data types.

For example:

'discount' => [
    'numeric',
    'min:0',
    'max:100',
]

can enforce business requirements.

For more complex rules, use custom validation rules or application/domain logic.

Common Laravel Validation Mistakes

Mistake 1: Only Using Client-Side Validation

JavaScript can be bypassed.

Always validate on the server.

Mistake 2: Using $request->all()

This can unintentionally pass fields you didn’t intend to process.

Use validated input instead.

Mistake 3: Forgetting nullable

Optional date or numeric fields often need:

nullable

when null should be accepted.

Mistake 4: Forgetting Unique Validation on Registration

A registration form should generally ensure that identifiers such as email addresses or usernames meet the application’s uniqueness requirements.

Mistake 5: Incorrect Unique Rule During Updates

When updating records, remember to ignore the current record:

Rule::unique('users', 'email')->ignore($user->id)

Mistake 6: Putting Too Much Validation in Controllers

If validation becomes large, use Form Requests.

Mistake 7: Not Validating Nested Arrays

For dynamic forms, validate both the parent array and individual items:

'items' => ['required', 'array'],
'items.*.quantity' => ['required', 'integer', 'min:1'],

Testing Laravel Validation

Validation should also be tested.

Laravel provides testing support and includes PHPUnit/Pest support in the framework ecosystem.

A feature test can verify that invalid input produces the expected validation response.

For example:

$response = $this->post('/register', [
    'name' => '',
    'email' => 'invalid-email',
    'password' => 'short',
]);

$response->assertSessionHasErrors([
    'name',
    'email',
    'password',
]);

You can also test successful submissions:

$response = $this->post('/register', [
    'name' => 'John Doe',
    'email' => 'john@example.com',
    'password' => 'StrongPassword123!',
    'password_confirmation' => 'StrongPassword123!',
]);

$response->assertSessionDoesntHaveErrors();

Laravel is designed with testing in mind and provides built-in testing infrastructure for application behavior.

Laravel Form Validation vs Client-Side Validation

Client-side and server-side Laravel form validation should complement each other rather than replace one another.

Feature Client-Side Laravel Server-Side
User experience Excellent Good
Prevents invalid requests No Yes
Can be bypassed Yes Much harder
Database validation No Yes
Business rules Limited Yes
API protection No Yes
Immediate feedback Yes Usually after request
Security layer No Yes

The ideal approach is:

Client-side validation
        +
Laravel server-side validation
        =
Better UX + Reliable application

Laravel Validation for Modern Applications

Laravel validation works particularly well across different application architectures.

Blade Applications

Use:

$request->validate(...)

or Form Requests.

Display errors with:

@error('field')
    {{ $message }}
@enderror

Laravel APIs

Use the same Laravel form validation rules and return JSON responses.

$request->validate([
    'email' => ['required', 'email'],
]);

Frontend applications can consume Laravel’s validation response.


React or Vue Frontends

If Laravel is acting as an API backend, validation remains on the Laravel side.

The frontend can display errors returned from the API.

This is particularly useful for applications built using:

  • React
  • Vue
  • Next.js
  • Mobile applications
  • SPA architectures

Advanced Validation: after

Sometimes initial validation is not enough.

For example, you may need to check a business condition after the normal validation rules have passed.

Form Requests provide an after mechanism for additional validation. Laravel’s documentation describes using after callbacks to perform additional validation with a Validator instance.

This is useful when the condition is more complex than a normal rule.

However, don’t put every business rule into validation. Some business logic belongs in services, policies, domain logic, or database constraints.


Validation and Database Constraints

Laravel validation is important, but database constraints should still be used where appropriate.

For example, if email addresses must be unique, consider both:

'email' => [
    'required',
    'email',
    'unique:users,email',
]

and a database-level unique constraint.

Why?

Because validation happens before the database operation, but concurrent requests can still create race conditions.

A robust application uses:

Application validation
        +
Database constraints

rather than relying exclusively on either one.


Validation and Security

Validation is an important security layer, but it isn’t the entire security strategy.

You should also consider:

  • Authentication
  • Authorization
  • CSRF protection
  • Mass-assignment protection
  • Output escaping
  • File upload security
  • Rate limiting
  • Database constraints
  • Secure password hashing
  • Proper access control

For example, validating:

'role' => ['in:user,editor']

doesn’t automatically mean every user should be allowed to submit editor.

Authorization should determine whether the current user is permitted to assign that role.

Validation and authorization solve different problems.


When Should You Use Form Requests?

A simple form may only need:

$request->validate([
    'name' => ['required'],
    'email' => ['required', 'email'],
]);

A larger application may benefit from:

php artisan make:request StoreProductRequest

Use Form Requests when:

  • Validation contains many rules.
  • Authorization is related to the request.
  • Multiple controllers use similar validation.
  • Custom messages are required.
  • Input needs preprocessing.
  • You want thin controllers.
  • You want better testability and maintainability.

A useful architecture is:

Route
  ↓
Form Request
  ↓
Controller
  ↓
Service / Domain Logic
  ↓
Model / Database

This keeps responsibilities separated.


Frequently Asked Questions

What is Laravel form validation?

Laravel form validation is the framework’s built-in mechanism for checking incoming request data against predefined validation rules before the application processes that data.


How do I validate a form in Laravel?

Use the request’s validate() method:

$request->validate([
    'name' => ['required', 'string'],
    'email' => ['required', 'email'],
]);

How do I display validation errors in Laravel?

In Blade, use:

@error('email')
    {{ $message }}
@enderror

Or display all errors using $errors.


How do I keep form values after Laravel form validation fails?

Use the old() helper:

<input
    type="text"
    name="name"
    value="{{ old('name') }}"
>

What is a Laravel Form Request?

A Form Request is a dedicated request class that contains Laravel form validation and authorization logic.

Create one with:

php artisan make:request StorePostRequest

What is the difference between validate() and Validator::make()?

$request->validate() is convenient and integrates directly with Laravel’s request/redirect behavior.

Validator::make() provides more explicit control over the validator instance and is useful for custom validation workflows.


How do I validate an email in Laravel?

Use:

'email' => [
    'required',
    'email',
]

How do I validate a unique email?

Use:

'email' => [
    'required',
    'email',
    'unique:users,email',
]

For updates, use Rule::unique()->ignore() to exclude the current record.


How do I validate an uploaded image?

For example:

'image' => [
    'required',
    'image',
    'max:2048',
]

Additional MIME, extension, and dimension rules can be added according to your application’s requirements.


What HTTP status does Laravel return for API validation errors?

For XHR requests, Laravel returns validation errors with HTTP status 422.

References

For the complete and version-specific list of Laravel validation rules and advanced laravel validation features, refer to the official Laravel Validation Documentation.

Conclusion

Laravel Form Validation provides a clean and powerful way to protect your Laravel application‘s data and enforce application requirements.

For simple forms, Laravel’s request validation syntax is often all you need:

$request->validate([
    'name' => ['required', 'string'],
    'email' => ['required', 'email'],
]);

As your application grows, you can move validation into Form Request classes:

php artisan make:request StorePostRequest

You can then use advanced features such as:

  • Laravel Custom validation messages
  • Conditional validation
  • Database validation
  • Unique and exists rules
  • File and image validation
  • Nested array validation
  • Password rules
  • Laravel Custom validation rules
  • Form Request authorization
  • Input preparation
  • Additional validation
  • API validation

The most important principle is to never trust incoming user input. Validate data on the server, process only validated values, enforce important constraints at the database level, and keep complex validation logic organized using Form Requests and custom rules.

Once you understand Laravel validation, you can build everything from simple contact forms to large admin panels, ecommerce checkout systems, authentication flows, and API-driven applications with much greater confidence.

Write a Reply or Comment

Your email address will not be published. Required fields are marked *