GioJSdocs
On this page

notFound

Stop rendering and answer 404 with the nearest not-found.tsx - from getServerSideProps, a component, an action or a route handler.

app/posts/[id]/page.tsx
import { notFound, type GetServerSideProps } from '@gio.js/core';

export const getServerSideProps = (async (ctx) => {
  const post = await db.posts.find(ctx.params.id);
  if (post === null) notFound();
  return { props: { post } };
}) satisfies GetServerSideProps<{ post: Post }, '/posts/:id'>;

Reference

notFound() takes no arguments. Its return type is never: it always throws, so TypeScript narrows the value you checked after the call (above, post is a Post on the last line).

Behavior

What answers depends on where it is called:

Called fromResponse
getServerSideProps, or a page or layout while it renders404 with the nearest not-found.tsx at or above the page's folder, inside that folder's layouts. Without one, the built-in 404 page.
A page actionThe same 404 page.
A route.ts handler404 with the JSON body {"error":"Not Found"}.

A 404 is never cached, even on a page that exports revalidate. When a cached page starts answering 404 - its data was deleted - the background revalidation that sees it evicts the cached copy. Returning { notFound: true } from getServerSideProps has the same effect as calling notFound(). When an action re-rendered the page with headers (a flash cookie) and getServerSideProps then calls notFound(), those headers are sent with the 404.

Examples

A missing record in a route handler

app/api/posts/[id]/route.ts
import { notFound, type GioRequest } from '@gio.js/core';

export async function GET(req: GioRequest<'/api/posts/:id'>) {
  const post = await db.posts.find(req.params.id);
  if (post === null) notFound();   // 404 {"error":"Not Found"}
  return post;                     // 200, JSON
}

A section with its own 404 page

text
app/
  not-found.tsx         # unmatched URLs, and pages without a closer file
  shop/
    layout.tsx
    not-found.tsx       # notFound() in any /shop page, rendered inside shop/layout.tsx
    [id]/page.tsx
app/shop/not-found.tsx
import { GioLink } from '@gio.js/react';

export default function ProductNotFound() {
  return (
    <main>
      <h1>That product is gone</h1>
      <GioLink href="/shop">Back to the shop</GioLink>
    </main>
  );
}

Good to know

  • It works by throwing. A try/catch around the call swallows it: call it outside the try, or rethrow what you did not expect.
  • Streaming. Called before a streamed page suspends, the answer is a real 404. Inside a suspended part (under loading.tsx or a <Suspense>), the 200 is already sent: React finishes that part in the browser, where notFound() shows the nearest error.tsx.
  • URLs that match no route always get app/not-found.tsx - they belong to no folder.
  • Static export. gio export skips pages that call notFound() and writes no HTML for them.
  • Safe in the browser. The module has no Node imports, so a component that calls it may ship in a client bundle.

Version history

VersionChanges
v0.1.0-beta.8Introduced: getServerSideProps, render, page actions and route.ts handlers, with per-folder not-found.tsx.