Mischief

56 / Blocks

Not Found

The page-scale empty state: a status, a heading you can read across a room, and somewhere to go.

404

That page moved, or never existed.

Component pages live under /docs/components. If you followed a link, the component may have been renamed.

Installation

Copy the source into your project, or keep it behind a package.

npx shadcn@latest add Tinkerers-Labs/mischief-ui/not-found
import { NotFound } from "mischief-ui/not-found"

Or paste it in yourself. The source imports the shared cn helper from @/lib/utils, so point that at your own copy.

registry/default/not-found/not-found.tsx
import * as React from "react" import { cn } from "@/lib/utils" export type NotFoundProps = Omit<React.HTMLAttributes<HTMLElement>, "title"> & {  /** The status, shown small above the title. */  code?: React.ReactNode  title: React.ReactNode  description?: React.ReactNode  /** What to do instead: a link home, a link to the docs. */  actions?: React.ReactNode} /**

Usage

export default function NotFoundPage() {
  return (
    <NotFound
      code="404"
      title="That page moved, or never existed."
      description="If you followed a link, the page may have been renamed."
      actions={<Link href="/docs">Browse the docs</Link>}
    >
      <PopularPages />
    </NotFound>
  )
}

Saying what happened

A 404 is the one page nobody chose to visit, so it should spend its words on what to do rather than on apology. Say what probably happened -- a renamed page, a stale link -- and offer the one or two places most people actually wanted.

  • Name the likely cause; a bare Not Found tells someone only what they already know.
  • Offer a way onward that does not require guessing a URL.
  • Keep the status as a code above the title rather than as the title itself, so the sentence is the thing read first.

The same shape suits any dead end with a page to itself: a deleted record, an expired invitation, a region you cannot serve. The heading changes, the structure does not.

API

titleReactNodeThe line that carries it. The only required prop.
codeReactNodeThe status above the title, such as "404".
descriptionReactNodeWhat likely happened, held to a readable measure.
actionsReactNodeWhere to go instead.
childrenReactNodeAnything more: a search, a list of likely destinations.

Accessibility

The title is the page's h1, because on a page whose only content is this, it is the heading. The status code sits above it as plain text rather than as part of the heading, so the sentence is what a screen reader announces first. Nothing here traps focus or announces itself; the controls inside are your own, with your own semantics.