Features
Handling errors
When a component fails, in production, it shows a fallback in its place and the rest of the page works on. The error goes to Laravel's exception handler. In development, you see the error itself.
On this page
A page is made of many components, and one of them failing shouldn't take the others down. A bank feed
that is down, a view with a bug, a #[Server] call that throws: in production, Nitro shows that
component's fallback, reports the error as Laravel reports any other, and leaves every other component
working. You don't have to do anything for this. A component without a fallback of its own shows a short
message with a link that loads the page again.
A component's own fallback
Give a component a fallback() method to choose what shows in its place. It runs on the server,
and receives the exception:
<?php
namespace App\Nitro\Components;
use App\Support\BankFeed;
use Illuminate\Contracts\View\View;
use Nitro\Attributes\Locked;
use Nitro\Component;
use Throwable;
class BankBalance extends Component
{
#[Locked]
public int $balanceCents = 0;
/** The bank's API: when it is down, this throws. */
public function mount(BankFeed $feed): void
{
$this->balanceCents = $feed->today()['balance_cents'];
}
/** Shown in the component's place when it fails. */
public function fallback(Throwable $exception): View
{
return view('partials.bank-unavailable');
}
}
- Optional. Without
fallback(), the component gets your app's fallback view, or Nitro's message. - Its parameter is optional too. Take
Throwable $exceptionwhen the fallback depends on what went wrong; leave it out when it doesn't. - It shows the visitor something safe. Never put the exception's message in the fallback: it can hold paths, queries or keys. The message goes to your logs.
- If it throws, that error is reported too, and the app's fallback view or Nitro's message shows instead.
fallback() can return a view, an Htmlable, or text:
public function fallback(): string
{
return 'The balance is not available right now.'; // Text: escaped.
}
public function fallback(): View
{
return view('partials.bank-unavailable'); // A view: rendered.
}
public function fallback(): HtmlString
{
return new HtmlString('<p class="muted">Unavailable</p>'); // Htmlable: as it is.
}
Your app's fallback
For the components without fallback(), name a view in config/nitro.php. Without
one, Nitro shows this, which you can style with .nitro-fallback:
<p class="nitro-fallback">This part of the page could not load. <a href="">Try again</a></p>
// config/nitro.php
'fallbacks' => [
'enabled' => null, // null: on when APP_DEBUG is off.
'view' => 'partials.unavailable', // For components without fallback().
],
Where a failure is caught
| When it fails | What shows | |
|---|---|---|
Its
mount()
or its view, on the server: the first page, a visit, a child a
#[Server]
call creates
|
Its
fallback()
, else the app's
|
The fallback is in the page's HTML in the component's place. The page and its other components render as usual |
A
#[Server]
method throws
|
Its
fallback()
, else the app's
|
The call answers 500 with the fallback, and the browser shows it in the component's place |
| Its view, rendering in the browser |
The app's (
fallback()
runs on the server, so it isn't asked)
|
The browser shows it in the component's place, and reports the error in the console |
A component showing its fallback is finished: it doesn't render again or take calls. The others carry on.
Nitro's message, and a link in your own fallback with href="", load the page again, which
starts the component afresh.
What isn't a fallback
- The page itself. A page whose own
mount()or view throws has nothing around it to keep working, so Laravel answers with its error page, as it does for a controller. Fallbacks are for the components inside a page or a plain Blade view. - Laravel's answers.
abort(403), a failedGate::authorize(),findOrFail()finding nothing, a guest where a user is needed, a redirect thrown withHttpResponseException, and every other exception Laravel's handler turns into a response, are answers, not failures. The visitor gets them as Laravel sends them, from a component in a page as from a controller, with fallbacks on or off. - Validation. A rule that fails puts its messages in
$errors, as always. - An error
exception()handles. A#[Server]method's error goes to the component'sexception()hook first. When the hook handles it, the call answers normally, with the state as the hook left it. See Lifecycle.
exception() or fallback()?
Use exception() when the component can carry on, such as "The CRM isn't answering, try again in a minute" beside a form that keeps what was typed. Use fallback() when it can't, such as a widget whose data never loaded.
In development
Fallbacks are off while APP_DEBUG is on, so you see the error itself: Laravel's error page for
a failure on the server, and the error in the browser's console for a call or a render in the browser. A
component whose call failed keeps its last state.
To try your fallbacks locally, set fallbacks.enabled to true in
config/nitro.php, and back to null after. false turns them off everywhere.
Reporting
Every failure that becomes a fallback goes to Laravel's exception handler, so your logs, Sentry, Flare or
Nightwatch see it as they see any other error, with its stack trace. In the browser, Nitro dispatches
nitro:error on the window for each failure there, with the component and the message:
window.addEventListener('nitro:error', (event) => {
const { component, message, status } = event.detail;
Sentry.captureMessage(message, { extra: { component: component?.name, status } });
});