Validation is your application's first line of defense: it ensures incoming data matches your expectations before it ever touches the database. Laravel ships with a very complete validation system, yet many developers pile everything into the controller until it becomes a mess. This article shows how to validate forms cleanly in Laravel 10/11 — from the simplest approach to custom rules.
1. Inline Validation with validate()
The fastest way: call $request->validate() directly in the controller. On failure, Laravel automatically redirects the user back with the error messages and old input.
public function store(Request $request)
{
$data = $request->validate([
'name' => 'required|string|max:255',
'email' => 'required|email|unique:users,email',
'age' => 'nullable|integer|min:17',
]);
User::create($data);
return back()->with('success', 'Saved.');
}
The return value of validate() contains only the fields that passed the rules — safe to pass straight into create().
2. Commonly Used Validation Rules
required/nullable— required or allowed to be empty.email,numeric,integer,string,boolean— data types.min:3/max:255— string length or numeric value.unique:users,email/exists:categories,id— database checks.confirmed— great for passwords (needs apassword_confirmationfield).in:draft,published— value must be one of a list.
3. Form Requests for Clean Controllers
As rules grow, move them into a Form Request class so the controller stays lean:
php artisan make:request StoreArticleRequest
Fill in the authorize() and rules() methods:
public function authorize(): bool
{
return true; // replace with permission logic if needed
}
public function rules(): array
{
return [
'title' => 'required|string|max:150',
'body' => 'required|string',
'status' => 'required|in:draft,published',
'tags' => 'array',
'tags.*' => 'string|max:30',
];
}
Then simply type-hint it in the controller — validation runs automatically before the method executes:
public function store(StoreArticleRequest $request)
{
Article::create($request->validated());
return redirect()->route('articles.index');
}
4. Custom Messages and Attribute Names
The default error messages are in English. Override them with the messages() and attributes() methods in the Form Request:
public function messages(): array
{
return [
'title.required' => 'The title is required.',
'title.max' => 'The title may not exceed 150 characters.',
];
}
public function attributes(): array
{
return [
'title' => 'article title',
];
}
With attributes(), a generic message like "The title field is required" becomes "The article title field is required" — handy for the :attribute placeholder.
5. Custom Validation Rules
For unique logic, create your own rule:
php artisan make:rule Uppercase
public function validate(string $attribute, mixed $value, Closure $fail): void
{
if (strtoupper($value) !== $value) {
$fail('The :attribute must be uppercase.');
}
}
Use it alongside built-in rules:
'code' => ['required', new Uppercase],
For one-off validation, you can also write a closure inline:
'slug' => [
'required',
function ($attribute, $value, $fail) {
if (str_contains($value, ' ')) {
$fail('The slug may not contain spaces.');
}
},
],
6. Displaying Errors in Blade
Laravel stores errors in an $errors variable that is always available in every view. Display them per field and preserve old input with old():
<input type="text" name="title" value="{{ old('title') }}">
@error('title')
<span class="text-red-500">{{ $message }}</span>
@enderror
To show all errors at the top of the form:
@if ($errors->any())
<ul>
@foreach ($errors->all() as $error)
<li>{{ $error }}</li>
@endforeach
</ul>
@endif
7. Validating Arrays and Files
Use dot notation with * to validate each element of an array, and file rules for uploads:
$request->validate([
'items' => 'required|array|min:1',
'items.*.name' => 'required|string',
'items.*.qty' => 'required|integer|min:1',
'avatar' => 'required|image|mimes:jpg,png|max:2048',
]);
The max:2048 rule on a file is measured in kilobytes, so the example above caps the image at 2 MB.
Conclusion
Start with inline validate() for simple forms, then move to Form Requests as rules grow. Override messages to be user-friendly, and build custom rules for special needs. With this approach your validation stays centralized, easy to test, and your controllers stay clean.