Error Pages

Davaux lets you replace the default error responses with custom JSX pages. Create a _error.tsx file in any route directory to handle errors for that scope.

Creating an error page

Export a handler via defineError. It receives an ErrorPageProps object and must return JSX:

// src/routes/_error.tsx
import { defineError } from 'davaux'
import type { ErrorPageProps } from 'davaux'

export default defineError(({ status, message, ctx }: ErrorPageProps) => {
  ctx.head.title = `${status} — Error`

  return (
    <main class="error-page">
      <h1>{status}</h1>
      <p>{message}</p>
      <a href="/">Go home</a>
    </main>
  )
})

ErrorPageProps

PropertyTypeDescription
statusnumberHTTP status code (404, 500, etc.)
messagestringHuman-readable error description
ctxRequestContextFull request context (for cookies, head, etc.)

Scope and inheritance

_error.tsx applies to all routes at the same directory level and below. A root-level _error.tsx catches everything:

src/routes/
├── _error.tsx           ← catches all unhandled errors
├── _layout.tsx
├── index.page.tsx
└── admin/
    ├── _error.tsx       ← overrides for /admin/* only
    └── dashboard.page.tsx

If the route that threw is inside admin/ and that directory has its own _error.tsx, the admin error page is used. Otherwise the root one applies.

Error types

404 Not Found

Triggered when no route matches the incoming URL. The error page receives status: 404 and a generic not-found message.

export default defineError(({ status, message, ctx }) => {
  if (status === 404) {
    ctx.head.title = 'Page Not Found'
    return (
      <main>
        <h1>Page not found</h1>
        <p>The page you were looking for does not exist.</p>
        <a href="/">Return to home</a>
      </main>
    )
  }

  // Fallback for 500 and other errors
  ctx.head.title = 'Something went wrong'
  return (
    <main>
      <h1>Something went wrong</h1>
      <p>An unexpected error occurred. Please try again.</p>
    </main>
  )
})

500 Internal Server Error

Triggered when a route handler or middleware throws an unhandled exception. In development, Davaux includes the error stack trace in the message prop. In production it is a generic message to avoid leaking internals.

Custom status codes

Handlers can throw errors with specific status codes:

import { defineError as defineHandlerError } from 'davaux'

// In any handler or middleware:
throw Object.assign(new Error('Forbidden'), { status: 403 })

The error page receives status: 403 and the error message.

Layouts wrap error pages

Error pages go through the same layout chain as normal pages. This means your site navigation, header, and footer appear on error pages automatically — no need to duplicate the HTML shell.

The layout receives the error page's JSX as children, just as it does for normal pages.

Fallback behavior

If the error page itself throws (a bug in your _error.tsx), Davaux falls back to a plain-text response rather than entering an infinite error loop:

HTTP/1.1 500 Internal Server Error
Content-Type: text/plain

Internal Server Error

This ensures the server always responds, even in degenerate cases.

Custom 404 with search

A more complete example that suggests similar pages:

// src/routes/_error.tsx
import { defineError } from 'davaux'
import type { ErrorPageProps } from 'davaux'

export default defineError(async ({ status, ctx }: ErrorPageProps) => {
  ctx.head.title = status === 404 ? 'Not Found' : 'Error'

  if (status !== 404) {
    return (
      <main class="error-page">
        <h1>Something went wrong</h1>
        <p>We had trouble processing your request. Please try again later.</p>
        <a href="/">Home</a>
      </main>
    )
  }

  const path = ctx.url.pathname

  return (
    <main class="error-page not-found">
      <div class="error-code">404</div>
      <h1>Page not found</h1>
      <p>
        No page exists at <code>{path}</code>. It may have moved or been deleted.
      </p>
      <nav class="error-nav">
        <a href="/">Home</a>
        <a href="/getting-started">Documentation</a>
      </nav>
    </main>
  )
})