Muhammad Yasir Amin

Loading websiteโ€ฆ

More from Muhammad Yasir Amin

Build Along With Me

Practical walkthroughs on Laravel, React, APIs and deployment workflows.

Subscribe on YouTube

Laravel 13 Form Validation Tutorial: Form Requests, Rules and Error Display

๐Ÿ› ๏ธ Laravel Tutorials4 min read

Laravel 13 Form Validation Tutorial: Form Requests, Rules and Error Display

Muhammad Yasir Amin

Muhammad Yasir Amin

Senior Full Stack Engineerโ€ขNov 9, 2026
Laravel 13 Form Validation Tutorial: Form Requests, Rules and Error Display

To validate a form in Laravel 13, create a Form Request with php artisan make:request, put your rules in its rules() method, type-hint it in the controller, and read clean data with $request->validated(). Laravel then redirects back with errors automatically when validation fails.

This tutorial builds a complete registration-style form with rules, error display, old input, a unique rule and a feature test.

What do I need first?

  • Laravel 13 (current stable at the time of writing) with a supported PHP version.
  • A users table, or any table you want to save into.

Should I validate in the controller or a Form Request?

Both work. Inline $request->validate([...]) is fine for two or three rules. A Form Request is better when rules grow, when you want custom messages, or when authorization belongs with the input rules. It keeps controllers short and rules reusable.

How do I create the Form Request?

Code
php artisan make:request StoreMemberRequest
JavaScript
<?php

namespace App\Http\Requests;

use Illuminate\Foundation\Http\FormRequest;
use Illuminate\Validation\Rule;

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

    public function rules(): array
    {
        return [
            'name'     => ['required', 'string', 'max:100'],
            'email'    => ['required', 'email:rfc', 'max:255', Rule::unique('users', 'email')],
            'age'      => ['nullable', 'integer', 'between:18,100'],
            'website'  => ['nullable', 'url'],
            'password' => ['required', 'string', 'min:8', 'confirmed'],
            'terms'    => ['accepted'],
        ];
    }
}

authorize() must return true for the request to proceed. Return false and Laravel sends a 403 response before the controller runs. Use it for permission checks, for example with a policy.

The confirmed rule expects a matching password_confirmation field. The accepted rule passes for values such as on, yes, 1 and true.

How do I use it in the controller?

JavaScript
use App\Http\Requests\StoreMemberRequest;
use App\Models\User;
use Illuminate\Support\Facades\Hash;

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

    User::create([
        'name'     => $data['name'],
        'email'    => $data['email'],
        'password' => Hash::make($data['password']),
    ]);

    return redirect()->route('members.index')
        ->with('status', 'Member created.');
}

Only fields that have a rule appear in validated(). That protects you from mass assignment of unexpected input.

How do I build the form in Blade?

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

    <label for="name">Name</label>
    <input id="name" name="name" value="{{ old('name') }}">
    @error('name') <p class="error">{{ $message }}</p> @enderror

    <label for="email">Email</label>
    <input id="email" type="email" name="email" value="{{ old('email') }}">
    @error('email') <p class="error">{{ $message }}</p> @enderror

    <label for="password">Password</label>
    <input id="password" type="password" name="password">
    @error('password') <p class="error">{{ $message }}</p> @enderror

    <label for="password_confirmation">Confirm password</label>
    <input id="password_confirmation" type="password" name="password_confirmation">

    <label><input type="checkbox" name="terms"> I accept the terms</label>
    @error('terms') <p class="error">{{ $message }}</p> @enderror

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

old('name') refills the field after a failed attempt. Never refill password fields. Laravel does not flash them by default.

How do I ignore the current record in a unique rule?

On an edit form, the email already exists for this record, so the plain unique rule fails. Ignore the current id.

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

How do I add a check that depends on several fields?

Use the after() method of the Form Request. It returns callables that receive the validator.

JavaScript
use Illuminate\Validation\Validator;

public function after(): array
{
    return [
        function (Validator $validator) {
            if ($this->input('age') && ! $this->filled('guardian') && $this->integer('age') < 21) {
                $validator->errors()->add('guardian', 'A guardian name is required under 21.');
            }
        },
    ];
}

How do I prepare input before validating?

Override prepareForValidation() to trim or normalise values, for example to lower-case the email.

JavaScript
protected function prepareForValidation(): void
{
    $this->merge([
        'email' => strtolower(trim((string) $this->input('email'))),
    ]);
}

What are the common mistakes?

  • Forgetting @csrf. The form returns a 419 error.
  • Returning false from authorize(). You get a 403 and no validation messages.
  • Using $request->all() after validation. It includes unvalidated input. Use validated().
  • Wrong confirmation name. The field must be named password_confirmation.
  • Missing enctype on file forms. Add enctype="multipart/form-data" when uploading.

How do I test the form?

JavaScript
public function test_member_requires_valid_email(): void
{
    $response = $this->post('/members', [
        'name' => 'Sara',
        'email' => 'not-an-email',
    ]);

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

public function test_member_can_be_created(): void
{
    $this->post('/members', [
        'name' => 'Sara',
        'email' => 'sara@example.com',
        'password' => 'long-enough-1',
        'password_confirmation' => 'long-enough-1',
        'terms' => 'on',
    ])->assertRedirect(route('members.index'));

    $this->assertDatabaseHas('users', ['email' => 'sara@example.com']);
}

FAQ

Where do validation errors go for API requests?

If the request expects JSON, Laravel returns a 422 response with an errors object instead of redirecting.

Can I stop at the first failing rule?

Yes. Add the bail rule at the start of a field's rules.

How do I change the messages?

Add a messages() method to the Form Request that returns an array of field.rule keys.

References

Article Index & Topic Tags
LaravelBladeValidationForm-Request

YouTube Channel

Build Along With Me

Practical walkthroughs on Laravel, React, APIs and deployment workflows.

Muhammad Yasir Amin
Available for Contracts
Author & Solutions Architect

Senior Full Stack Engineer

Independent Senior Full Stack Engineer working on Laravel, React and Next.js applications, APIs and delivery workflows.

Laravel & PHP ApplicationsReact & Next.js InterfacesREST APIs & IntegrationsDatabase Queries & DebuggingDocker Development WorkflowsClear Scope & Handover
Discuss Your Next Step

What Does Your Application Need Next?

Share your current system, the problem you want to solve and what a useful result looks like. We can identify the right starting point and agree on a practical scope.

Technical Audit

Find the Next Useful Fix

Application Development

Scoped, Reviewable Milestones

Ongoing Support

Agreed Capacity and Priorities

Share your goals, current stack and requirements before the call.