Error 404
Original · freefriendly 404 page with illustration and CTA
Copy prompt gives Claude Code, Cursor or v0 a ready-to-paste prompt that recreates this exact component.
npx shadcn@latest add https://webinnoventix.com/r/error-404.json"use client";
import {
useEffect,
useId,
useRef,
useState,
type KeyboardEvent as ReactKeyboardEvent,
type PointerEvent as ReactPointerEvent,
} from "react";
import {
motion,
useMotionValue,
useReducedMotion,
useSpring,
useTransform,
} from "motion/react";
interface Destination {
label: string;
href: string;
desc: string;
}
const DESTINATIONS: Destination[] = [
{ label: "Homepage", href: "/", desc: "Start fresh from the front door" },
{ label: "Pricing & plans", href: "/pricing", desc: "Compare tiers, features and limits" },
{ label: "Documentation", href: "/docs", desc: "Guides, API reference and tutorials" },
{ label: "Blog", href: "/blog", desc: "Latest articles, notes and updates" },
{ label: "Contact support", href: "/contact", desc: "Talk to a real human on the team" },
{ label: "System status", href: "/status", desc: "Live uptime and incident history" },
{ label: "Careers", href: "/careers", desc: "Open roles across every team" },
{ label: "Changelog", href: "/changelog", desc: "Everything we shipped recently" },
];
const REFERENCE_CODE = "REQ-404-9F2C7A1B";
const STARS: { cx: number; cy: number; r: number; delay: number }[] = [
{ cx: 54, cy: 66, r: 1.6, delay: 0 },
{ cx: 120, cy: 40, r: 2.4, delay: 0.6 },
{ cx: 300, cy: 54, r: 1.8, delay: 1.2 },
{ cx: 366, cy: 96, r: 2.6, delay: 0.3 },
{ cx: 388, cy: 210, r: 1.5, delay: 0.9 },
{ cx: 40, cy: 176, r: 2.2, delay: 1.5 },
{ cx: 350, cy: 300, r: 2, delay: 0.2 },
{ cx: 66, cy: 300, r: 1.7, delay: 1.1 },
{ cx: 250, cy: 24, r: 1.4, delay: 1.8 },
{ cx: 180, cy: 92, r: 1.5, delay: 0.75 },
{ cx: 330, cy: 160, r: 1.9, delay: 1.35 },
{ cx: 24, cy: 250, r: 1.6, delay: 0.5 },
];
export default function Error404Page() {
const prefersReduced = useReducedMotion();
const reduce = Boolean(prefersReduced);
const uid = useId();
const inputId = `${uid}-input`;
const listId = `${uid}-list`;
const titleId = `${uid}-title`;
const descId = `${uid}-desc`;
const liveId = `${uid}-live`;
const optionId = (i: number) => `${uid}-opt-${i}`;
const [query, setQuery] = useState("");
const [open, setOpen] = useState(false);
const [activeIndex, setActiveIndex] = useState(-1);
const [copied, setCopied] = useState(false);
const rootRef = useRef<HTMLDivElement | null>(null);
const copyTimer = useRef<ReturnType<typeof setTimeout> | null>(null);
const q = query.trim().toLowerCase();
const results =
q === ""
? DESTINATIONS
: DESTINATIONS.filter((d) =>
`${d.label} ${d.desc}`.toLowerCase().includes(q),
);
// Parallax motion values (pointer-driven, disabled under reduced motion).
const mvx = useMotionValue(0);
const mvy = useMotionValue(0);
const sx = useSpring(mvx, { stiffness: 110, damping: 18, mass: 0.4 });
const sy = useSpring(mvy, { stiffness: 110, damping: 18, mass: 0.4 });
const starsX = useTransform(sx, (v) => v * 0.35);
const starsY = useTransform(sy, (v) => v * 0.35);
const planetX = useTransform(sx, (v) => v * 0.7);
const planetY = useTransform(sy, (v) => v * 0.7);
const astroX = useTransform(sx, (v) => v * 1.15);
const astroY = useTransform(sy, (v) => v * 1.15);
const frontX = useTransform(sx, (v) => v * 1.7);
const frontY = useTransform(sy, (v) => v * 1.7);
const onPointerMove = (e: ReactPointerEvent<HTMLDivElement>) => {
if (reduce) return;
const rect = e.currentTarget.getBoundingClientRect();
const nx = (e.clientX - rect.left) / rect.width - 0.5;
const ny = (e.clientY - rect.top) / rect.height - 0.5;
mvx.set(nx * 36);
mvy.set(ny * 36);
};
const onPointerLeave = () => {
mvx.set(0);
mvy.set(0);
};
// Close the listbox on an outside pointer press.
useEffect(() => {
const handler = (e: globalThis.PointerEvent) => {
if (!rootRef.current) return;
if (!rootRef.current.contains(e.target as Node)) setOpen(false);
};
document.addEventListener("pointerdown", handler);
return () => document.removeEventListener("pointerdown", handler);
}, []);
// Keep the active option scrolled into view.
useEffect(() => {
if (activeIndex < 0) return;
const el = document.getElementById(optionId(activeIndex));
if (el) el.scrollIntoView({ block: "nearest" });
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [activeIndex]);
useEffect(
() => () => {
if (copyTimer.current) clearTimeout(copyTimer.current);
},
[],
);
const go = (href: string) => {
window.location.assign(href);
};
const onInputKeyDown = (e: ReactKeyboardEvent<HTMLInputElement>) => {
if (e.key === "ArrowDown") {
e.preventDefault();
if (!open) setOpen(true);
setActiveIndex((prev) => {
const max = results.length - 1;
if (max < 0) return -1;
return prev < max ? prev + 1 : max;
});
} else if (e.key === "ArrowUp") {
e.preventDefault();
if (!open) setOpen(true);
setActiveIndex((prev) => (prev > 0 ? prev - 1 : 0));
} else if (e.key === "Enter") {
if (open && activeIndex >= 0 && activeIndex < results.length) {
e.preventDefault();
go(results[activeIndex].href);
}
} else if (e.key === "Escape") {
setOpen(false);
setActiveIndex(-1);
}
};
const copyReference = async () => {
try {
await navigator.clipboard.writeText(REFERENCE_CODE);
setCopied(true);
if (copyTimer.current) clearTimeout(copyTimer.current);
copyTimer.current = setTimeout(() => setCopied(false), 2200);
} catch {
setCopied(false);
}
};
const fadeUp = (delay: number) =>
reduce
? { initial: false as const }
: {
initial: { opacity: 0, y: 14 },
animate: { opacity: 1, y: 0 },
transition: { duration: 0.55, delay, ease: [0.16, 1, 0.3, 1] as const },
};
return (
<section className="relative w-full overflow-hidden bg-slate-50 text-slate-900 dark:bg-slate-950 dark:text-slate-100">
<style>{`
@keyframes err404-twinkle {
0%, 100% { opacity: 0.25; transform: scale(0.7); }
50% { opacity: 1; transform: scale(1); }
}
@keyframes err404-float {
0%, 100% { transform: translateY(0px) rotate(-1.5deg); }
50% { transform: translateY(-14px) rotate(1.5deg); }
}
@keyframes err404-drift {
0%, 100% { transform: translateY(0px); }
50% { transform: translateY(8px); }
}
@keyframes err404-orbit {
from { transform: rotate(0deg); }
to { transform: rotate(360deg); }
}
@keyframes err404-blob {
0%, 100% { transform: translate3d(0,0,0) scale(1); }
50% { transform: translate3d(0,-18px,0) scale(1.08); }
}
.err404-twinkle { animation: err404-twinkle 3.2s ease-in-out infinite; }
.err404-float { animation: err404-float 6.5s ease-in-out infinite; transform-box: fill-box; transform-origin: center; }
.err404-drift { animation: err404-drift 9s ease-in-out infinite; transform-box: fill-box; transform-origin: center; }
.err404-orbit { animation: err404-orbit 16s linear infinite; transform-box: view-box; transform-origin: 210px 210px; }
.err404-blob { animation: err404-blob 11s ease-in-out infinite; }
@media (prefers-reduced-motion: reduce) {
.err404-twinkle, .err404-float, .err404-drift, .err404-orbit, .err404-blob {
animation: none !important;
}
}
`}</style>
{/* Atmosphere */}
<div
aria-hidden="true"
className="pointer-events-none absolute inset-0 -z-10 bg-[radial-gradient(70%_55%_at_50%_-10%,rgba(99,102,241,0.14),transparent)]"
/>
<div
aria-hidden="true"
className="err404-blob pointer-events-none absolute -left-24 top-24 -z-10 h-72 w-72 rounded-full bg-indigo-400/20 blur-3xl dark:bg-indigo-500/10"
/>
<div
aria-hidden="true"
className="err404-blob pointer-events-none absolute -right-16 bottom-0 -z-10 h-80 w-80 rounded-full bg-violet-400/20 blur-3xl dark:bg-violet-500/10"
style={{ animationDelay: "3s" }}
/>
<div className="mx-auto flex min-h-screen w-full max-w-6xl items-center px-4 py-16 sm:px-6 sm:py-24 lg:px-8">
<div className="grid w-full items-center gap-12 lg:grid-cols-2 lg:gap-16">
{/* Copy + controls */}
<div className="order-2 lg:order-1">
<motion.div {...fadeUp(0)}>
<span className="inline-flex items-center gap-2 rounded-full border border-indigo-200 bg-indigo-50 px-3 py-1 text-xs font-semibold uppercase tracking-[0.18em] text-indigo-700 dark:border-indigo-500/30 dark:bg-indigo-500/10 dark:text-indigo-300">
<svg
viewBox="0 0 24 24"
className="h-3.5 w-3.5"
fill="none"
stroke="currentColor"
strokeWidth="2"
strokeLinecap="round"
strokeLinejoin="round"
aria-hidden="true"
>
<circle cx="12" cy="12" r="9" />
<path d="M14.5 9.5 11 11l-1.5 3.5L13 13z" />
</svg>
Error 404
</span>
</motion.div>
<motion.h1
{...fadeUp(0.06)}
className="mt-6 text-4xl font-black leading-[1.05] tracking-tight text-slate-900 sm:text-5xl lg:text-6xl dark:text-white"
>
This page took a
<span className="bg-gradient-to-r from-indigo-600 to-violet-600 bg-clip-text text-transparent dark:from-indigo-400 dark:to-violet-400">
{" "}
wrong turn.
</span>
</motion.h1>
<motion.p
{...fadeUp(0.12)}
className="mt-5 max-w-md text-base leading-relaxed text-slate-600 sm:text-lg dark:text-slate-400"
>
The link may be broken, or the page might have drifted off into
orbit. Search for what you need, or jump to a popular destination
below.
</motion.p>
{/* Combobox search */}
<motion.div {...fadeUp(0.18)} className="mt-8">
<label
htmlFor={inputId}
className="mb-2 block text-sm font-medium text-slate-700 dark:text-slate-300"
>
Search the site
</label>
<div ref={rootRef} className="relative">
<div className="relative">
<svg
viewBox="0 0 24 24"
className="pointer-events-none absolute left-3.5 top-1/2 h-5 w-5 -translate-y-1/2 text-slate-400 dark:text-slate-500"
fill="none"
stroke="currentColor"
strokeWidth="2"
strokeLinecap="round"
strokeLinejoin="round"
aria-hidden="true"
>
<circle cx="11" cy="11" r="7" />
<path d="m20 20-3-3" />
</svg>
<input
id={inputId}
type="text"
role="combobox"
aria-expanded={open}
aria-controls={listId}
aria-autocomplete="list"
aria-activedescendant={
open && activeIndex >= 0 ? optionId(activeIndex) : undefined
}
autoComplete="off"
placeholder="Try 'pricing', 'docs' or 'status'…"
value={query}
onChange={(e) => {
setQuery(e.target.value);
setOpen(true);
setActiveIndex(-1);
}}
onFocus={() => setOpen(true)}
onKeyDown={onInputKeyDown}
className="w-full rounded-xl border border-slate-300 bg-white py-3 pl-11 pr-4 text-sm text-slate-900 shadow-sm outline-none transition placeholder:text-slate-400 focus-visible:border-indigo-500 focus-visible:ring-2 focus-visible:ring-indigo-500/40 dark:border-slate-700 dark:bg-slate-900 dark:text-slate-100 dark:placeholder:text-slate-500 dark:focus-visible:border-indigo-400"
/>
</div>
{open && (
<ul
id={listId}
role="listbox"
aria-label="Suggested destinations"
className="absolute z-20 mt-2 max-h-72 w-full overflow-auto rounded-xl border border-slate-200 bg-white p-1.5 shadow-xl shadow-slate-900/10 dark:border-slate-700 dark:bg-slate-900 dark:shadow-black/40"
>
{results.length === 0 ? (
<li
role="status"
className="px-3 py-6 text-center text-sm text-slate-500 dark:text-slate-400"
>
No matches for “{query}”. Try{" "}
<span className="font-medium text-slate-700 dark:text-slate-200">
pricing
</span>{" "}
or{" "}
<span className="font-medium text-slate-700 dark:text-slate-200">
contact
</span>
.
</li>
) : (
results.map((d, i) => {
const active = i === activeIndex;
return (
<li key={d.href} role="none">
<a
id={optionId(i)}
role="option"
aria-selected={active}
href={d.href}
onMouseEnter={() => setActiveIndex(i)}
className={`flex items-center justify-between gap-3 rounded-lg px-3 py-2.5 text-left outline-none transition ${
active
? "bg-indigo-50 dark:bg-indigo-500/15"
: "hover:bg-slate-50 dark:hover:bg-slate-800/60"
}`}
>
<span className="min-w-0">
<span className="block truncate text-sm font-semibold text-slate-900 dark:text-slate-100">
{d.label}
</span>
<span className="block truncate text-xs text-slate-500 dark:text-slate-400">
{d.desc}
</span>
</span>
<svg
viewBox="0 0 24 24"
className={`h-4 w-4 shrink-0 transition ${
active
? "text-indigo-600 dark:text-indigo-400"
: "text-slate-300 dark:text-slate-600"
}`}
fill="none"
stroke="currentColor"
strokeWidth="2"
strokeLinecap="round"
strokeLinejoin="round"
aria-hidden="true"
>
<path d="M5 12h14" />
<path d="m13 6 6 6-6 6" />
</svg>
</a>
</li>
);
})
)}
</ul>
)}
</div>
</motion.div>
{/* CTAs */}
<motion.div
{...fadeUp(0.24)}
className="mt-8 flex flex-wrap items-center gap-3"
>
<a
href="/"
className="inline-flex items-center gap-2 rounded-xl bg-indigo-600 px-5 py-3 text-sm font-semibold text-white shadow-lg shadow-indigo-600/25 transition hover:bg-indigo-500 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-indigo-500 focus-visible:ring-offset-2 focus-visible:ring-offset-slate-50 dark:focus-visible:ring-offset-slate-950"
>
<svg
viewBox="0 0 24 24"
className="h-4 w-4"
fill="none"
stroke="currentColor"
strokeWidth="2"
strokeLinecap="round"
strokeLinejoin="round"
aria-hidden="true"
>
<path d="m3 10.5 9-7 9 7" />
<path d="M5 9.5V20h14V9.5" />
<path d="M10 20v-6h4v6" />
</svg>
Back to homepage
</a>
<a
href="/contact"
className="inline-flex items-center gap-2 rounded-xl border border-slate-300 bg-white px-5 py-3 text-sm font-semibold text-slate-700 transition hover:bg-slate-100 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-indigo-500 focus-visible:ring-offset-2 focus-visible:ring-offset-slate-50 dark:border-slate-700 dark:bg-slate-900 dark:text-slate-200 dark:hover:bg-slate-800 dark:focus-visible:ring-offset-slate-950"
>
Contact support
</a>
<button
type="button"
onClick={() => window.history.back()}
className="inline-flex items-center gap-1.5 rounded-xl px-3 py-3 text-sm font-semibold text-slate-500 transition hover:text-slate-900 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-indigo-500 focus-visible:ring-offset-2 focus-visible:ring-offset-slate-50 dark:text-slate-400 dark:hover:text-white dark:focus-visible:ring-offset-slate-950"
>
<svg
viewBox="0 0 24 24"
className="h-4 w-4"
fill="none"
stroke="currentColor"
strokeWidth="2"
strokeLinecap="round"
strokeLinejoin="round"
aria-hidden="true"
>
<path d="M19 12H5" />
<path d="m11 18-6-6 6-6" />
</svg>
Go back
</button>
</motion.div>
{/* Reference code */}
<motion.div
{...fadeUp(0.3)}
className="mt-8 inline-flex items-center gap-3 rounded-xl border border-slate-200 bg-white/70 px-3.5 py-2.5 backdrop-blur dark:border-slate-800 dark:bg-slate-900/60"
>
<span className="text-xs font-medium text-slate-500 dark:text-slate-400">
Reference
</span>
<code className="font-mono text-xs font-semibold tracking-tight text-slate-800 dark:text-slate-200">
{REFERENCE_CODE}
</code>
<button
type="button"
onClick={() => {
void copyReference();
}}
aria-describedby={liveId}
className={`inline-flex items-center gap-1.5 rounded-lg px-2.5 py-1.5 text-xs font-semibold transition focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-indigo-500 focus-visible:ring-offset-2 focus-visible:ring-offset-white dark:focus-visible:ring-offset-slate-900 ${
copied
? "bg-emerald-100 text-emerald-700 dark:bg-emerald-500/15 dark:text-emerald-300"
: "bg-slate-100 text-slate-600 hover:bg-slate-200 dark:bg-slate-800 dark:text-slate-300 dark:hover:bg-slate-700"
}`}
>
{copied ? (
<>
<svg
viewBox="0 0 24 24"
className="h-3.5 w-3.5"
fill="none"
stroke="currentColor"
strokeWidth="2.5"
strokeLinecap="round"
strokeLinejoin="round"
aria-hidden="true"
>
<path d="m20 6-11 11-5-5" />
</svg>
Copied
</>
) : (
<>
<svg
viewBox="0 0 24 24"
className="h-3.5 w-3.5"
fill="none"
stroke="currentColor"
strokeWidth="2"
strokeLinecap="round"
strokeLinejoin="round"
aria-hidden="true"
>
<rect x="9" y="9" width="12" height="12" rx="2" />
<path d="M5 15V5a2 2 0 0 1 2-2h10" />
</svg>
Copy
</>
)}
</button>
<span id={liveId} role="status" aria-live="polite" className="sr-only">
{copied ? "Reference code copied to clipboard" : ""}
</span>
</motion.div>
</div>
{/* Illustration */}
<div className="order-1 lg:order-2">
<motion.div
{...(reduce
? { initial: false as const }
: {
initial: { opacity: 0, scale: 0.94 },
animate: { opacity: 1, scale: 1 },
transition: { duration: 0.7, ease: [0.16, 1, 0.3, 1] as const },
})}
onPointerMove={onPointerMove}
onPointerLeave={onPointerLeave}
className="relative mx-auto aspect-[4/5] w-full max-w-[440px] overflow-hidden rounded-[2rem] bg-gradient-to-br from-indigo-950 via-violet-950 to-slate-950 ring-1 ring-white/10 shadow-2xl shadow-indigo-950/40"
>
<div
aria-hidden="true"
className="pointer-events-none absolute inset-0 bg-[radial-gradient(55%_45%_at_50%_25%,rgba(129,140,248,0.28),transparent)]"
/>
<svg
viewBox="0 0 420 420"
className="absolute inset-0 h-full w-full"
role="img"
aria-labelledby={`${titleId} ${descId}`}
>
<title id={titleId}>Lost astronaut illustration</title>
<desc id={descId}>
An astronaut drifting through space on a tether beside the
faded number 404 and a ringed planet.
</desc>
<defs>
<radialGradient id={`${uid}-visor`} cx="40%" cy="34%" r="78%">
<stop offset="0%" stopColor="#7dd3fc" />
<stop offset="55%" stopColor="#4338ca" />
<stop offset="100%" stopColor="#1e1b4b" />
</radialGradient>
<radialGradient id={`${uid}-planet`} cx="34%" cy="28%" r="82%">
<stop offset="0%" stopColor="#a78bfa" />
<stop offset="60%" stopColor="#6d28d9" />
<stop offset="100%" stopColor="#4c1d95" />
</radialGradient>
</defs>
{/* Back layer: faint 404 + far stars */}
<motion.g style={{ x: starsX, y: starsY }}>
<text
x="210"
y="256"
textAnchor="middle"
fontSize="196"
fontWeight="800"
fill="#ffffff"
opacity="0.055"
>
404
</text>
{STARS.slice(0, 6).map((s, i) => (
<circle
key={`bs-${i}`}
className="err404-twinkle"
cx={s.cx}
cy={s.cy}
r={s.r}
fill="#ffffff"
style={{ animationDelay: `${s.delay}s` }}
/>
))}
</motion.g>
{/* Planet layer */}
<motion.g style={{ x: planetX, y: planetY }}>
<g className="err404-drift">
<circle cx="84" cy="352" r="74" fill={`url(#${uid}-planet)`} />
<ellipse
cx="84"
cy="352"
rx="100"
ry="21"
fill="none"
stroke="#818cf8"
strokeWidth="4"
opacity="0.5"
transform="rotate(-18 84 352)"
/>
<circle cx="66" cy="330" r="11" fill="#ffffff" opacity="0.12" />
<circle cx="104" cy="368" r="7" fill="#ffffff" opacity="0.1" />
<circle cx="52" cy="360" r="5" fill="#ffffff" opacity="0.1" />
</g>
</motion.g>
{/* Astronaut layer */}
<motion.g style={{ x: astroX, y: astroY }}>
<path
d="M210 34 C 252 84, 176 128, 206 168"
stroke="#94a3b8"
strokeWidth="3"
fill="none"
strokeLinecap="round"
opacity="0.65"
/>
<g className="err404-float">
{/* antenna */}
<line x1="210" y1="120" x2="210" y2="105" stroke="#94a3b8" strokeWidth="3" />
<circle
className="err404-twinkle"
cx="210"
cy="101"
r="4"
fill="#34d399"
/>
{/* backpack */}
<rect x="185" y="150" width="50" height="84" rx="16" fill="#cbd5e1" />
{/* arms */}
<rect
x="156"
y="196"
width="32"
height="16"
rx="8"
fill="#f1f5f9"
transform="rotate(-20 172 204)"
/>
<rect
x="232"
y="196"
width="32"
height="16"
rx="8"
fill="#f1f5f9"
transform="rotate(20 248 204)"
/>
<circle cx="156" cy="212" r="9" fill="#6366f1" />
<circle cx="264" cy="212" r="9" fill="#6366f1" />
{/* legs */}
<rect
x="188"
y="252"
width="18"
height="36"
rx="9"
fill="#f1f5f9"
transform="rotate(9 197 270)"
/>
<rect
x="214"
y="252"
width="18"
height="36"
rx="9"
fill="#f1f5f9"
transform="rotate(-9 223 270)"
/>
<rect
x="182"
y="280"
width="24"
height="14"
rx="7"
fill="#6366f1"
transform="rotate(9 194 287)"
/>
<rect
x="214"
y="280"
width="24"
height="14"
rx="7"
fill="#6366f1"
transform="rotate(-9 226 287)"
/>
{/* body */}
<rect x="181" y="188" width="58" height="74" rx="20" fill="#f8fafc" />
<rect x="197" y="208" width="26" height="18" rx="5" fill="#4f46e5" />
<circle cx="204" cy="217" r="2.6" fill="#a5b4fc" />
<circle cx="216" cy="217" r="2.6" fill="#34d399" />
{/* helmet */}
<circle cx="210" cy="158" r="41" fill="#f8fafc" />
<ellipse cx="210" cy="160" rx="29" ry="27" fill={`url(#${uid}-visor)`} />
<ellipse cx="198" cy="149" rx="9" ry="6" fill="#ffffff" opacity="0.55" />
<circle cx="222" cy="170" r="3" fill="#ffffff" opacity="0.4" />
</g>
</motion.g>
{/* Front layer: near stars + orbiting speck */}
<motion.g style={{ x: frontX, y: frontY }}>
{STARS.slice(6).map((s, i) => (
<circle
key={`fs-${i}`}
className="err404-twinkle"
cx={s.cx}
cy={s.cy}
r={s.r}
fill="#ffffff"
style={{ animationDelay: `${s.delay}s` }}
/>
))}
<g className="err404-orbit">
<circle cx="210" cy="52" r="5" fill="#fbbf24" />
</g>
</motion.g>
</svg>
</motion.div>
</div>
</div>
</div>
</section>
);
}Dependencies
Licence
Built by Web Innoventix. Free for personal and commercial use, no attribution required.
Built by Web Innoventix
Need a custom interface, a full website, or SEO that gets you cited by AI? We design, build and rank it end to end.
Get a free quoteSimilar components
Browse all →
Error 500
Original500 server error page

Error 403
Original403 access denied page

Error Offline
Originaloffline / no-connection state

Error Maintenance
Originalscheduled maintenance page

Error Coming Soon
Originalcoming soon page with countdown and email

Error Under Construction
Originalunder construction page

Error Search Empty
Originalno search results page

Error Expired
Originallink/session expired page

Error Generic
Originalgeneric error page with retry

Spotlight Hero
OriginalA centred hero with a soft radial spotlight, badge and dual call-to-action.

Split Hero
OriginalA two-column hero pairing a headline and CTAs with a product mock and social proof.

Gradient Spotlight Hero
OriginalA minimal centred hero with a soft gradient-mesh backdrop, announcement pill and dual call-to-action buttons.

