404 / Not Found
NewAnimated 404 pages in five styles: glitch scramble, magnetic digits, cursor spotlight, a fanning card stack and a typed terminal.
Glitch
glitch.tsxDigits scramble through random glyphs before resolving, with a chromatic split on hover.
404
Page not found
The page you are looking for moved, vanished, or never existed.
"use client";
import { NotFoundGlitch } from "@/components/motion/not-found/glitch";
export function NotFoundGlitchPreview() {
return (
<div className="w-full">
<NotFoundGlitch />
</div>
);
}
"use client";
import { useEffect, useState } from "react";
import { useReducedMotion } from "motion/react";
import {
NOT_FOUND_DEFAULTS,
NotFoundActions,
NotFoundStage,
type NotFoundProps,
} from "./shared";
const GLYPHS = "ABCDEFGHJKLMNPQRSTUVWXYZ0123456789#%&@$?/\\";
const SCRAMBLE_MS = 700;
const TICK_MS = 45;
/**
* Renders the code, scrambling each character on mount before it settles.
* SSR and the first paint show the real code, so the scramble is a pure
* client-side enhancement and reduced-motion users see the code immediately.
*/
function Scramble({ text }: { text: string }) {
const reduce = useReducedMotion();
const [display, setDisplay] = useState(text);
useEffect(() => {
if (reduce) {
setDisplay(text);
return;
}
const chars = text.split("");
const start = performance.now();
let raf = 0;
let last = 0;
const loop = (now: number) => {
if (now - last >= TICK_MS) {
last = now;
const progress = Math.min((now - start) / SCRAMBLE_MS, 1);
const settled = Math.floor(progress * chars.length);
setDisplay(
chars
.map((ch, i) =>
i < settled || ch === " "
? ch
: GLYPHS[Math.floor(Math.random() * GLYPHS.length)],
)
.join(""),
);
}
if (now - start < SCRAMBLE_MS) {
raf = requestAnimationFrame(loop);
} else {
setDisplay(text);
}
};
raf = requestAnimationFrame(loop);
return () => cancelAnimationFrame(raf);
}, [text, reduce]);
return <span className="tabular-nums">{display}</span>;
}
export function NotFoundGlitch({
className,
code = NOT_FOUND_DEFAULTS.code,
title = NOT_FOUND_DEFAULTS.title,
description = NOT_FOUND_DEFAULTS.description,
homeHref,
homeLabel,
browseHref,
browseLabel,
}: NotFoundProps) {
return (
<NotFoundStage className={className}>
<div className="group relative select-none font-mono font-bold leading-none tracking-tighter text-foreground [font-size:clamp(5rem,18vw,11rem)]">
{/* Chromatic ghost layers, nudged apart on hover. */}
<span
aria-hidden
className="pointer-events-none absolute inset-0 text-[#ff0040] opacity-0 mix-blend-screen transition-[transform,opacity] duration-150 ease-out group-hover:translate-x-[3px] group-hover:opacity-70 motion-reduce:hidden"
>
<Scramble text={code} />
</span>
<span
aria-hidden
className="pointer-events-none absolute inset-0 text-[#00e5ff] opacity-0 mix-blend-screen transition-[transform,opacity] duration-150 ease-out group-hover:-translate-x-[3px] group-hover:opacity-70 motion-reduce:hidden"
>
<Scramble text={code} />
</span>
<h1 className="relative">
<Scramble text={code} />
</h1>
</div>
<div className="flex flex-col items-center gap-2">
<p className="text-lg font-semibold text-foreground">{title}</p>
<p className="max-w-sm text-sm text-muted-foreground">{description}</p>
</div>
<NotFoundActions
homeHref={homeHref}
homeLabel={homeLabel}
browseHref={browseHref}
browseLabel={browseLabel}
/>
</NotFoundStage>
);
}
Install
Magnetic
magnetic.tsxEach digit is cursor-attracted via the Magnetic wrapper and springs back on leave.
Page not found
The page you are looking for moved, vanished, or never existed.
"use client";
import { NotFoundMagnetic } from "@/components/motion/not-found/magnetic";
export function NotFoundMagneticPreview() {
return (
<div className="w-full">
<NotFoundMagnetic />
</div>
);
}
"use client";
import { Magnetic } from "@/components/motion/magnetic";
import { cn } from "@/lib/utils";
import {
NOT_FOUND_DEFAULTS,
NotFoundActions,
NotFoundStage,
type NotFoundProps,
} from "./shared";
export function NotFoundMagnetic({
className,
code = NOT_FOUND_DEFAULTS.code,
title = NOT_FOUND_DEFAULTS.title,
description = NOT_FOUND_DEFAULTS.description,
homeHref,
homeLabel,
browseHref,
browseLabel,
}: NotFoundProps) {
const chars = code.split("");
return (
<NotFoundStage className={className}>
<h1
aria-label={code}
className="flex select-none items-center justify-center font-bold leading-none tracking-tighter text-foreground [font-size:clamp(5rem,18vw,12rem)]"
>
{chars.map((ch, i) => (
<Magnetic
// biome-ignore lint/suspicious/noArrayIndexKey: fixed positional glyphs
key={i}
strength={0.6}
className={cn(i > 0 && "-ml-2")}
>
<span aria-hidden className="inline-block px-1 tabular-nums">
{ch}
</span>
</Magnetic>
))}
</h1>
<div className="flex flex-col items-center gap-2">
<p className="text-lg font-semibold text-foreground">{title}</p>
<p className="max-w-sm text-sm text-muted-foreground">{description}</p>
</div>
<NotFoundActions
homeHref={homeHref}
homeLabel={homeLabel}
browseHref={browseHref}
browseLabel={browseLabel}
/>
</NotFoundStage>
);
}
Install
Spotlight
spotlight.tsxA dark panel where a cursor-tracked spotlight reveals the bright code from a dim base.
Page not found
The page you are looking for moved, vanished, or never existed.
"use client";
import { NotFoundSpotlight } from "@/components/motion/not-found/spotlight";
export function NotFoundSpotlightPreview() {
return (
<div className="w-full">
<NotFoundSpotlight />
</div>
);
}
"use client";
import { useRef } from "react";
import {
motion,
useMotionTemplate,
useMotionValue,
useReducedMotion,
} from "motion/react";
import { useHoverCapable } from "@/lib/hooks/use-hover-capable";
import { cn } from "@/lib/utils";
import {
NOT_FOUND_DEFAULTS,
NotFoundActions,
NotFoundStage,
type NotFoundProps,
} from "./shared";
export function NotFoundSpotlight({
className,
code = NOT_FOUND_DEFAULTS.code,
title = NOT_FOUND_DEFAULTS.title,
description = NOT_FOUND_DEFAULTS.description,
homeHref,
homeLabel,
browseHref,
browseLabel,
}: NotFoundProps) {
const ref = useRef<HTMLDivElement>(null);
const reduce = useReducedMotion();
const canHover = useHoverCapable();
const enabled = !reduce && canHover;
const mx = useMotionValue(50);
const my = useMotionValue(50);
const mask = useMotionTemplate`radial-gradient(220px circle at ${mx}% ${my}%, #000 25%, transparent 72%)`;
const onMove = (e: React.MouseEvent<HTMLDivElement>) => {
const el = ref.current;
if (!el || !enabled) return;
const rect = el.getBoundingClientRect();
mx.set(((e.clientX - rect.left) / rect.width) * 100);
my.set(((e.clientY - rect.top) / rect.height) * 100);
};
return (
<NotFoundStage className={className}>
<motion.div
ref={ref}
onMouseMove={onMove}
className="relative isolate flex aspect-[16/9] w-full max-w-xl items-center justify-center overflow-hidden rounded-3xl border border-border bg-neutral-950"
>
{/* Dim base layer. */}
<span
aria-hidden
className="select-none font-bold leading-none tracking-tighter text-white/10 [font-size:clamp(5rem,16vw,10rem)]"
>
{code}
</span>
{/* Bright layer, revealed only under the spotlight. */}
<motion.h1
aria-label={code}
style={enabled ? { WebkitMaskImage: mask, maskImage: mask } : undefined}
className={cn(
"absolute select-none font-bold leading-none tracking-tighter text-white [font-size:clamp(5rem,16vw,10rem)]",
!enabled && "text-white/90",
)}
>
<span aria-hidden>{code}</span>
</motion.h1>
</motion.div>
<div className="flex flex-col items-center gap-2">
<p className="text-lg font-semibold text-foreground">{title}</p>
<p className="max-w-sm text-sm text-muted-foreground">{description}</p>
</div>
<NotFoundActions
homeHref={homeHref}
homeLabel={homeLabel}
browseHref={browseHref}
browseLabel={browseLabel}
/>
</NotFoundStage>
);
}
Install
Stacked
stacked.tsxA code card over a hidden stack that fans out with a spring on hover.
404
out of the deckPage not found
The page you are looking for moved, vanished, or never existed.
"use client";
import { NotFoundStacked } from "@/components/motion/not-found/stacked";
export function NotFoundStackedPreview() {
return (
<div className="w-full">
<NotFoundStacked />
</div>
);
}
"use client";
import { motion, useReducedMotion } from "motion/react";
import { SPRING_PANEL } from "@/lib/ease";
import { useHoverCapable } from "@/lib/hooks/use-hover-capable";
import {
NOT_FOUND_DEFAULTS,
NotFoundActions,
NotFoundStage,
type NotFoundProps,
} from "./shared";
const CARD =
"absolute inset-0 rounded-3xl border border-border bg-card shadow-sm";
export function NotFoundStacked({
className,
code = NOT_FOUND_DEFAULTS.code,
title = NOT_FOUND_DEFAULTS.title,
description = NOT_FOUND_DEFAULTS.description,
homeHref,
homeLabel,
browseHref,
browseLabel,
}: NotFoundProps) {
const reduce = useReducedMotion();
const canHover = useHoverCapable();
const interactive = !reduce && canHover;
return (
<NotFoundStage className={className}>
<motion.div
initial="rest"
animate="rest"
whileHover={interactive ? "hover" : undefined}
className="relative h-44 w-64"
>
<motion.div
aria-hidden
variants={{ rest: { rotate: 0, x: 0, y: 0 }, hover: { rotate: -9, x: -28, y: 8 } }}
transition={SPRING_PANEL}
className={CARD}
/>
<motion.div
aria-hidden
variants={{ rest: { rotate: 0, x: 0, y: 0 }, hover: { rotate: 9, x: 28, y: 8 } }}
transition={SPRING_PANEL}
className={CARD}
/>
<motion.div
variants={{ rest: { y: 0 }, hover: { y: -6 } }}
transition={SPRING_PANEL}
className="absolute inset-0 flex flex-col items-center justify-center gap-1 rounded-3xl border border-border bg-card shadow-md"
>
<h1 className="select-none font-bold leading-none tracking-tighter text-foreground [font-size:clamp(3.5rem,9vw,5rem)]">
{code}
</h1>
<span className="text-xs font-medium uppercase tracking-wide text-muted-foreground">
out of the deck
</span>
</motion.div>
</motion.div>
<div className="flex flex-col items-center gap-2">
<p className="text-lg font-semibold text-foreground">{title}</p>
<p className="max-w-sm text-sm text-muted-foreground">{description}</p>
</div>
<NotFoundActions
homeHref={homeHref}
homeLabel={homeLabel}
browseHref={browseHref}
browseLabel={browseLabel}
/>
</NotFoundStage>
);
}
Install
Terminal
terminal.tsxA terminal window that types a failed cd command and a 404 status, with a blinking caret.
$ cd /page
cd: no such file or directory: /page
$ status 404
Page not found
The page you are looking for moved, vanished, or never existed.
"use client";
import { NotFoundTerminal } from "@/components/motion/not-found/terminal";
export function NotFoundTerminalPreview() {
return (
<div className="w-full">
<NotFoundTerminal />
</div>
);
}
"use client";
import { TextReveal } from "@/components/motion/text-reveal";
import {
NOT_FOUND_DEFAULTS,
NotFoundActions,
NotFoundStage,
type NotFoundProps,
} from "./shared";
const TYPE_SPRING = { stiffness: 320, damping: 30, mass: 0.6 };
export function NotFoundTerminal({
className,
code = NOT_FOUND_DEFAULTS.code,
title = NOT_FOUND_DEFAULTS.title,
description = NOT_FOUND_DEFAULTS.description,
homeHref,
homeLabel,
browseHref,
browseLabel,
}: NotFoundProps) {
return (
<NotFoundStage className={className}>
<div className="w-full max-w-md overflow-hidden rounded-xl border border-border bg-neutral-950 text-left shadow-lg">
<div className="flex items-center gap-1.5 border-b border-white/10 px-4 py-3">
<span className="h-3 w-3 rounded-full bg-[#ff5f57]" />
<span className="h-3 w-3 rounded-full bg-[#febc2e]" />
<span className="h-3 w-3 rounded-full bg-[#28c840]" />
<span className="ml-2 text-xs text-white/40">~/beui</span>
</div>
<div className="space-y-1.5 p-4 font-mono text-sm leading-relaxed">
<TextReveal
as="p"
split="char"
stagger={0.018}
blur={6}
yOffset={0}
spring={TYPE_SPRING}
className="text-white/80"
text="$ cd /page"
/>
<TextReveal
as="p"
split="char"
stagger={0.012}
delay={0.45}
blur={6}
yOffset={0}
spring={TYPE_SPRING}
className="text-[#ff5f57]"
text="cd: no such file or directory: /page"
/>
<p className="flex items-center text-white/80">
<TextReveal
as="span"
split="char"
stagger={0.018}
delay={1.1}
blur={6}
yOffset={0}
spring={TYPE_SPRING}
text={`$ status ${code}`}
/>
<span className="ml-1 inline-block h-[1.1em] w-[0.55ch] translate-y-[0.12em] bg-white/80 motion-safe:animate-pulse" />
</p>
</div>
</div>
<div className="flex flex-col items-center gap-2">
<p className="text-lg font-semibold text-foreground">{title}</p>
<p className="max-w-sm text-sm text-muted-foreground">{description}</p>
</div>
<NotFoundActions
homeHref={homeHref}
homeLabel={homeLabel}
browseHref={browseHref}
browseLabel={browseLabel}
/>
</NotFoundStage>
);
}
Install
Related components
Multi-chain Swap
Cross-chain swap widget with chain + token selectors, morphing views, animated flip and quote.
Dynamic Island
iOS-style island pill that morphs between live activity views with bouncy shell resize and blur crossfades.
Command Palette
⌘K palette with fuzzy filter, spring-animated active row and glass surface.