Loader Errors & Redirects

A route loader can stop a page from rendering with its data:

  • notFound(): the 404 page, with HTTP 404.

  • A thrown Response: a redirect or a refusal.

  • Any other error: a 500 on the server; on the client, the page renders without data.

Not found

JavaScript
import { notFound } from "@opentf/afw/server";

export default function loader({ params }) {
  const post = db.posts.find(params.id);
  if (!post) notFound(); // → the 404 page, with HTTP 404
  return post;
}

Redirects and refusals

A loader can throw a Response, as API routes and server functions can:

JavaScript
export default async function loader({ params, locals }) {
  const post = await db.posts.find(params.id);
  if (post && post.slug !== params.slug) {
    throw new Response(null, { status: 301, headers: { location: `/posts/${post.id}/${post.slug}` } });
  }
  if (post?.private && post.owner !== locals.user?.id) throw new Response(null, { status: 403 });
  return post;
}
  • On a server render, the thrown Response is the page's response.

  • On a client navigation, a redirect navigates to its target, replacing the history entry; another status is reported as a failed data fetch.

  • Use it for decisions that need the loader's data: a canonical URL, a moved record, access to this one item. Sign-in checks for a whole section belong in middleware, once.

  • When middleware redirects a data request (an auth guard), the router loads the page in full, so the server's redirect applies.

Other errors

Any other thrown error is a 500. On the client, a failed data fetch is reported (phase: "data") and the navigation still commits with router.data undefined — pages should treat "no data" as a valid state.

Last updated on
Edit this page