Installation
pnpm dlx shadcn@latest add https://asciicn.dev/r/not-found.jsonUsage
In Next.js, render it from app/not-found.tsx. That file handles unknown URLs and every notFound() call, and Next.js answers with a 404 status.
import Link from "next/link";
import { NotFound } from "@/components/asciicn/not-found";
import { Button } from "@/components/ui/button";
export default function NotFoundPage() {
return (
<main className="flex min-h-svh flex-col items-center justify-center">
<NotFound
actions={
<>
<Button asChild>
<Link href="/">Back home</Link>
</Button>
<Button asChild variant="outline">
<Link href="/docs">Browse the docs</Link>
</Button>
</>
}
/>
</main>
);
}The code is set in the page's own typeface at a heavy weight, then sampled into glyphs. Each sample knows how far it sits from the edge of a digit. Two gusts per loop sweep across the code and wear its edges down a few samples deep, so it changes shape without ever breaking up. Grains lift off the tops and right-hand faces, arc downwind in accent and fade as they settle, and the hole each one leaves fills back in as it lands. Faint wind ripples run through the sand. The loop lasts 16 seconds.
The terminal line types $ cd <path>, answers cd: no such file or directory: <path> and waits at a blinking cursor. Without path it reads window.location.pathname after mount, so the server markup and the first client render match. command and message change the two lines, for a failed request rather than a missing page:
<NotFound
code="500"
title="Something broke"
path="/api/orders"
command="curl -f"
message="(22) the server returned 500"
/>Examples
Something broke
A 500 page with a curl line, a red accent and a stronger wind.
Inside a card
compact shrinks the art, the terminal line and the type, for a missing record inside an app.
Classic ramp
The standard charset at a 12px cell. The sand thins from @ to . toward its edges, and the grains in flight are . and :.
Props
| Prop | Type | Default |
|---|---|---|
| code The code drawn in sand. | Type: string | Default "404" |
| title The heading under the art. | Type: ReactNode | Default "Page not found" |
| description One or two sentences under the heading. | Type: ReactNode | Default A line about a moved or mistyped link |
| path The path echoed in the terminal line. Read after mount when omitted. | Type: string | Default window.location.pathname |
| command The command typed before the path. | Type: string | Default "cd" |
| message The error the command prints. | Type: string | Default "no such file or directory" |
| actions Buttons or links, laid out in a centred row. | Type: ReactNode | Default – |
| titleAs Heading level of the title, to fit the page outline. | Type: "h1" | "h2" | "h3" | "p" | Default "h1" |
| accent Colour of the drifting grains, the prompt and the cursor: a palette name or any CSS colour. | Type: AsciiColor | Default "orange" |
| wind How hard the wind blows, from 0 (still sand, no grains) to 2. | Type: number | Default 1 |
| compact A smaller layout for cards and panels. | Type: boolean | Default false |
| charset Glyphs the sand is drawn with. Braille draws one dot per grain. | Type: CharsetName | Default "braille" |
| colorMode `truecolor` draws the sand in ink and the grains in `accent`; `mono` draws both in ink. | Type: "mono" | "truecolor" | "ansi16" | "duotone" | "palette" | Default "truecolor" |
| dither Ordered dithering. Blue noise turns the thinning edges into a stipple of loose grains. | Type: "none" | DitherMatrix | Default "blue-noise" for Braille and quadrants, "none" for ramps |
| cell Glyph cell height of the art in CSS pixels. | Type: number | Default 10, or 8 when compact |
| speed Time multiplier for the wind. | Type: number | Default 1 |
| animate `false` holds a still frame, prints the terminal line at once and stops the cursor blinking. | Type: boolean | Default true |
| stillTime The moment drawn when motion is off or reduced, in seconds. | Type: number | Default 5.5 |
| chars, phosphor, ramp, ansi, grade, invert, threshold, edgeThreshold, background, fx The shared look props. See [the engine](/docs/engine/ascii-engine). | Type: AsciiLook | Default – |
| handle Filled with a handle for export. | Type: RefObject<AsciiHandle | null> | Default – |
Notes
- The art is a canvas of real glyphs and is labelled with the code (
role="img"). The terminal line, the title and the description are DOM text, so they are readable, selectable and translatable. Screen readers get the error line once, not letter by letter. - The motion is a function of time on a 16 second loop, so a still frame needs no history. Under
prefers-reduced-motionthe art holds thestillTimeframe, the terminal line prints at once and the cursor stays lit. - The art pauses offscreen and in hidden tabs, and follows the theme: ink glyphs on the page, the accent from the
--ascii-color-*tokens. - The code fits its box, so a three-digit code and a word such as
"gone"both work. Short codes read best.