camelon

Errors & not-found

Two boundary files handle failures. Neither has a URL.

  • _404.tsx renders when no route matches a URL under its directory.
  • _error.tsx renders when a handler or middleware throws. Resolution climbs to the nearest boundary.

notFound

throw notFound() renders the 404 page.

import { notFound } from 'camelon';
import type { GetFunction } from 'camelon';

export const get: GetFunction = () => {
  throw notFound('No such page');
};
live

Try it →

redirect

throw redirect(url, status?) sends a redirect. The default status is 302. A Datastar request gets a client-side navigation instead, so in-page actions move the user without a reload.

import { redirect } from 'camelon';
import type { GetFunction } from 'camelon';

export const get: GetFunction = () => {
  throw redirect('/hello');
};
live

Try it →

HttpError

throw new HttpError(status, message) for any other status.

import { HttpError } from 'camelon';
import type { GetFunction } from 'camelon';

export const get: GetFunction = () => {
  throw new HttpError(403, 'Forbidden');
};
live

Try it →

Per-route error boundary

A page route can export error to render its own failure UI:

export const error: ErrorFunction = (err, input, request, source, errorId) => (
  <div class="error">
    <h1>Something broke</h1>
    <p safe>{err.message}</p>
    <code safe>{errorId}</code>
  </div>
);

This route throws, and its own error boundary renders in place — no crash, no blank page:

import { HttpError } from 'camelon';
import type { GetFunction, ErrorFunction } from 'camelon';

export const get: GetFunction = () => {
  throw new HttpError(418, "I'm a teapot");
};

export const error: ErrorFunction = (e) => (
  <div class="d-card">
    <strong>Caught by the route's boundary</strong>
    <p>
      {e.name}: {e.message}
    </p>
  </div>
);

export default function Boundary() {
  return <div class="d-card">this never renders — the get handler throws first</div>;
}
live

errorId is the request correlation id. Print it so a user can quote it and you can find it in the logs.