error.tsx
The error UI for a folder and everything below it: the 500 page when a render fails on the server, and an error boundary in the browser.
import React from 'react';
import type { ErrorPageProps } from '@gio.js/core';
export default function DashboardError({ error, reset }: ErrorPageProps) {
return (
<div role="alert">
<h2>The dashboard could not be shown</h2>
{error.digest !== undefined && <p>Reference: {error.digest}</p>}
{reset !== undefined && <button onClick={reset}>Try again</button>}
</div>
);
}Reference
File name and location
error.tsx, error.jsx or error.js, in app/ or any folder below it (route groups and dynamic folders included, private folders never). The nearest one at or above the page that failed is used.
Props
| Prop | Type | Default | Description |
|---|---|---|---|
error.message (required) | string | - | The error's message in development. In production it is always generic: Internal Server Error for a server failure, Application Error for one in the browser, and Not Found for a notFound() call caught in the browser. |
error.digest | string | undefined | - | A short reference the server logged the real error under (for example 140011e66130). Show it, so users can quote it in a report. Errors that only happened in the browser have none. |
reset | (() => void) | undefined | - | Renders the failed segment again. Only present when the error was caught in the browser: a server-rendered 500 page is static HTML. |
Type them with ErrorPageProps from @gio.js/core.
Module exports
default (required), and optionally metadata or generateMetadata for the head of the server-rendered 500 page. If generateMetadata throws (often the same outage the page reports), the static metadata exports are used instead.
Behavior
On the server. When getServerSideProps, the page or a layout throws before the response has started:
- The response is
500with the nearesterror.tsx, rendered inside the layouts of its own folder (not those of the page that failed). It is never cached. - The message and stack go to the log under the digest; the page gets only the digest in production.
- If that
error.tsxcannot render either (its own layouts throw), the one in the folder above is tried, and so on. With none left, the server answers with its built-in error page. - The
500page is server-only HTML: it does not hydrate, so it has noreset, and links in it are plain links.
In the browser. Every error.tsx is also bundled into the pages below its folder as a React error boundary:
- A render error after hydration (or during a soft navigation) replaces only that folder's segment with the error UI; layouts above it stay.
reset()clears the error and renders the segment again. Navigating to another URL, or only to another query string, clears it too.- React error boundaries do not catch errors in event handlers or in async code outside rendering: handle those where they happen.
Where the boundary sits
<Layout> app/dashboard/layout.tsx - NOT caught by dashboard/error.tsx
<ErrorBoundary> app/dashboard/error.tsx
<Suspense> app/dashboard/loading.tsx
<Page /> app/dashboard/page.tsx - caughtSo an error.tsx never handles an error thrown by the layout in its own folder. Put an error.tsx in the parent folder for that.
Examples
A root error page
import React from 'react';
import type { ErrorPageProps } from '@gio.js/core';
export default function ErrorPage({ error, reset }: ErrorPageProps) {
return (
<main className="status">
<h1>Something went wrong</h1>
{process.env.NODE_ENV === 'development' && <pre>{error.message}</pre>}
{error.digest !== undefined && (
<p>
Error reference: <code>{error.digest}</code>
</p>
)}
{reset !== undefined ? (
<button type="button" onClick={reset}>Try again</button>
) : (
<a href="/">Go home</a>
)}
</main>
);
}Find the error in the logs
The digest a user reports matches the digest field of the log line written when the render failed:
{"ts":"2026-10-07T15:19:31.677Z","level":"error","msg":"ssr render failed","requestId":"b0533d97-...","path":"/dashboard/boom","digest":"140011e66130","error":"boom from gssp","stack":"Error: boom from gssp\n at getServerSideProps (app/dashboard/[id]/page.tsx:6:39) ..."}Good to know
error.tsxruns in the browser, so it must be browser-safe. One that imports server-only code leaves the pages below it without hydration, and the build error names the file. No'use client'directive is needed.- After a streamed page has suspended inside a loading.tsx boundary, the
200is already sent. A later error is shown by the browser's error boundary and the status stays200. - Route handlers (
route.ts) never useerror.tsx: they answer a JSON500with adigest. - Hiding the error details in production is fixed: it cannot be turned off, because messages and stacks can contain secrets, SQL or file paths. Read them in the log.
- There is no
global-error.tsx. An error in the root layout itself gets the built-in error page, since everyerror.tsxrenders inside it. gio exportreports a page that fails to render, with its digest, instead of writing the error page in its place.
Related
- Error Handling - the guide.
- not-found.tsx, loading.tsx, layout.tsx
- Observability - reading the logs.
Version history
| Version | Changes |
|---|---|
v0.1.0-beta.8 | Works in any folder, with the nearest one applying. Also a client error boundary with reset. Production shows only a digest: the props are { error: { message, digest } }. ErrorPageProps type. |
v0.1.0-beta.5 | Introduced for app/error.tsx: the server renders it with status 500 when a page throws. |