In a Laravel 13 form request, how do you stop validating after the first failing field and redirect failed browser submissions to a named route?
answer
- a property or a class attribute
- $stopOnFirstFailure / #[StopOnFirstFailure]
- $redirect, $redirectRoute, $redirectAction
- #[RedirectTo] / #[RedirectToRoute] in 13
- default target is url()->previous()
basics
~20 sSet protected $stopOnFirstFailure = true or add #[StopOnFirstFailure]; set $redirectRoute (or $redirect for a URL) or add #[RedirectToRoute('...')] / #[RedirectTo('...')]. Without them, every field is checked and a failed browser submission returns to the previous URL.
solid answer
~30 sA form request exposes both knobs as protected properties: `$stopOnFirstFailure = true` makes the validator stop checking further fields once any field has an error, and `$redirect`, `$redirectRoute` or `$redirectAction` choose where a failed non-JSON submission goes. `getRedirectUrl()` checks them in that order and falls back to `url()->previous()`. Laravel 13 documents class attributes for the same settings — `#[StopOnFirstFailure]`, `#[RedirectTo('/events')]`, `#[RedirectToRoute('events.index')]` (and `#[ErrorBag('registration')]` for the bag name) — which are read before the validator is built. The redirect only matters for browser posts; a request that expects JSON gets a 422 regardless.
code
php · 24 lines<?php
namespace App\Http\Requests;
use Illuminate\Foundation\Http\Attributes\RedirectToRoute;
use Illuminate\Foundation\Http\Attributes\StopOnFirstFailure;
use Illuminate\Foundation\Http\FormRequest;
#[StopOnFirstFailure]
#[RedirectToRoute('registrations.create')]
class StoreRegistrationRequest extends FormRequest
{
// Equivalent without attributes:
// protected $stopOnFirstFailure = true;
// protected $redirectRoute = 'registrations.create';
public function rules(): array
{
return [
'ticket_type' => ['bail', 'required', 'in:standard,vip'],
'dietary_notes' => ['nullable', 'string', 'max:500'],
];
}
}go deeper
Know that a failed form goes back to the previous page by default and that the class can pick another target.
Explain the redirect precedence, the property and attribute spellings, and stopOnFirstFailure versus bail.
Judge when stopping early helps or hurts UX, and override getRedirectUrl() for parameterised routes and shared base requests.
Standardise how forms report errors across teams, weighing one-error-at-a-time flows against showing every problem at once.
## Two defaults you may want to change Out of the box a form request: - validates **every** field and reports every failure, and - on failure sends a browser back to the **previous URL** (`url()->previous()`), with the errors and old input flashed to the session. Both are configurable on the class, either with properties or, in Laravel 13, with class-level PHP attributes. ## Stopping at the first failure | Style | Declaration | |---|---| | Property | `protected $stopOnFirstFailure = true;` | | Attribute | `#[StopOnFirstFailure]` from `Illuminate\Foundation\Http\Attributes` | Either way, the form request passes the flag to the validator with `->stopOnFirstFailure(...)`. Inside `Validator::passes()`, before moving on to the **next attribute**, the loop checks whether any error exists and breaks if so. So once one field has failed, the remaining fields are not validated. Keep the scope straight: - `stopOnFirstFailure` works **across fields**: after the first field with an error, stop. - The `bail` rule works **within one field**: stop running that field's remaining rules after its first failure. They answer different questions and are often combined. Stopping early helps when later checks are expensive (a database lookup) or meaningless once an earlier field is wrong; the cost is that users fix one error per submission, which is poor UX on a long form. ## Choosing the redirect target On failure, `FormRequest::failedValidation()` throws a `ValidationException` whose redirect URL comes from `getRedirectUrl()`. It checks, in order: 1. `$redirect` — a URL or path, passed to `url()->to()`. 2. `$redirectRoute` — a route name, passed to `url()->route()`. 3. `$redirectAction` — a controller action, passed to `url()->action()`. 4. Otherwise `url()->previous()`. Laravel 13's attributes set the first two: - `#[RedirectTo('/events')]` sets `$redirect`. - `#[RedirectToRoute('events.index')]` sets `$redirectRoute`. `$redirectAction` has no attribute; it stays a property. Note that `$redirectRoute` takes a **name only** — a route that needs parameters, such as `events.register` with an `{event}` segment, cannot be expressed this way. For that, override `getRedirectUrl()` and build the URL from `$this->route('event')`. When the default is fine: most forms want the user back on the page they submitted from, which is exactly what `previous()` gives. Custom targets are for multi-step flows, modals rendered on another page, or forms posted from several places that should all return to one. ## When the redirect is ignored The redirect applies only to responses rendered as a redirect. If the request expects JSON (an `Accept: application/json` header, or an XHR that accepts any type), the exception handler renders a **422** JSON response and the configured target is never used. The shape of that JSON and the view-side error bag are separate concerns. ## Attribute resolution rules worth knowing - The attributes are read in `configureFromAttributes()` when the validator is first built. - Laravel walks up the class hierarchy to find them, but a class that **declares the matching property itself** stops the search, so a property on a subclass overrides an attribute on its parent. - Resolving these attributes from parent form requests was added in 13.33, so on the 13.34 pin a shared base request class can carry `#[StopOnFirstFailure]` for all its children. ## Example: an event-registration form The registration form is rendered inside an event page, and the POST goes to a dedicated endpoint. Without a custom target, `previous()` usually works; but when the form is also embedded on the organiser's dashboard, pointing every failure at one named route keeps error display in one place: - `#[RedirectToRoute('registrations.create')]` on the class. - `#[StopOnFirstFailure]` only if a later `after()` check is costly and meaningless when basic fields fail. Both settings are about the failure path only. They never change which data reaches `validated()` on success, and neither affects authorization, which has already run by the time the validator is built. Choosing between the property and the attribute is a matter of style: the attribute keeps configuration visible at the top of the class, while the property works the same way on every supported release.
- How do you redirect a failed form request to a route that needs a parameter?`$redirectRoute` and `#[RedirectToRoute]` accept only a name. Override `getRedirectUrl()` in the form request and return, for example, `route('events.register', $this->route('event'))`; `failedValidation()` uses whatever that method returns.
- Does stopOnFirstFailure do the same thing as the bail rule?No. `stopOnFirstFailure` stops validating further fields once any field has failed. `bail` stops running the remaining rules of one field after that field's first failure, while other fields are still checked.
saying these in an interview costs you the question
- stopOnFirstFailure stops the remaining rules of the same field only
- A custom redirect also applies to JSON requests
- $redirectRoute accepts a route name with parameters
- Laravel 13 removed the $redirect and $stopOnFirstFailure properties