Not Found Page
Animated 404 page with subtle icon motion and reduced-motion support.
Component
404
Oops! Page not found
The page you are looking for might have been removed, had its name changed, or is temporarily unavailable.
Back to Home"use client";
import { motion, useReducedMotion } from "framer-motion";
import { ArrowLeft, Frown } from "lucide-react";
import Link from "next/link";
import { cn } from "@/lib/utils";
interface NotFoundPageProps {
className?: string;
homeHref?: string;
title?: string;
description?: string;
helperText?: string;
backLabel?: string;
icon?: React.ReactNode;
buttonClassName?: string;
}
export function NotFoundPage({
className,
homeHref = "/",
title = "404",
description = "Oops! Page not found",
helperText = "The page you are looking for might have been removed, had its name changed, or is temporarily unavailable.",
backLabel = "Back to Home",
icon,
buttonClassName,
}: NotFoundPageProps) {
const shouldReduceMotion = useReducedMotion();
return (
<motion.div
initial={{
opacity: shouldReduceMotion ? 1 : 0,
y: shouldReduceMotion ? 0 : 20,
}}
animate={{ opacity: 1, y: 0 }}
transition={
shouldReduceMotion
? { duration: 0 }
: { duration: 0.5 }
}
className={cn(
"flex min-h-[60svh] flex-col items-center justify-center space-y-6 text-center",
className,
)}
>
<motion.div
animate={
shouldReduceMotion
? { rotate: 0 }
: { rotate: [0, 5, -5, 0] }
}
transition={
shouldReduceMotion
? { duration: 0 }
: {
duration: 2,
ease: "easeInOut",
repeat: Number.POSITIVE_INFINITY,
}
}
className="inline-block"
>
{icon ?? (
<Frown className="mx-auto h-24 w-24 text-muted-foreground" />
)}
</motion.div>
<h1 className="font-bold text-4xl text-foreground">
{title}
</h1>
<p className="text-muted-foreground text-xl">
{description}
</p>
<p className="mx-auto max-w-md text-muted-foreground">
{helperText}
</p>
<Link
href={homeHref}
className={cn(
"mt-4 inline-flex items-center rounded-md bg-primary px-4 py-2 font-medium text-primary-foreground text-sm transition-colors hover:bg-primary/90",
buttonClassName,
)}
>
<ArrowLeft className="mr-2 h-4 w-4" />
{backLabel}
</Link>
</motion.div>
);
}Installation
pnpm dlx shadcn@latest add "https://pulkitxm.com/components/not-found-page.json"1. Install dependencies
pnpm add framer-motion lucide-react2. Copy the component file
"use client";
import { motion, useReducedMotion } from "framer-motion";
import { ArrowLeft, Frown } from "lucide-react";
import Link from "next/link";
import { cn } from "@/lib/utils";
interface NotFoundPageProps {
className?: string;
homeHref?: string;
title?: string;
description?: string;
helperText?: string;
backLabel?: string;
icon?: React.ReactNode;
buttonClassName?: string;
}
export function NotFoundPage({
className,
homeHref = "/",
title = "404",
description = "Oops! Page not found",
helperText = "The page you are looking for might have been removed, had its name changed, or is temporarily unavailable.",
backLabel = "Back to Home",
icon,
buttonClassName,
}: NotFoundPageProps) {
const shouldReduceMotion = useReducedMotion();
return (
<motion.div
initial={{
opacity: shouldReduceMotion ? 1 : 0,
y: shouldReduceMotion ? 0 : 20,
}}
animate={{ opacity: 1, y: 0 }}
transition={
shouldReduceMotion
? { duration: 0 }
: { duration: 0.5 }
}
className={cn(
"flex min-h-[60svh] flex-col items-center justify-center space-y-6 text-center",
className,
)}
>
<motion.div
animate={
shouldReduceMotion
? { rotate: 0 }
: { rotate: [0, 5, -5, 0] }
}
transition={
shouldReduceMotion
? { duration: 0 }
: {
duration: 2,
ease: "easeInOut",
repeat: Number.POSITIVE_INFINITY,
}
}
className="inline-block"
>
{icon ?? (
<Frown className="mx-auto h-24 w-24 text-muted-foreground" />
)}
</motion.div>
<h1 className="font-bold text-4xl text-foreground">
{title}
</h1>
<p className="text-muted-foreground text-xl">
{description}
</p>
<p className="mx-auto max-w-md text-muted-foreground">
{helperText}
</p>
<Link
href={homeHref}
className={cn(
"mt-4 inline-flex items-center rounded-md bg-primary px-4 py-2 font-medium text-primary-foreground text-sm transition-colors hover:bg-primary/90",
buttonClassName,
)}
>
<ArrowLeft className="mr-2 h-4 w-4" />
{backLabel}
</Link>
</motion.div>
);
}3. Use in app/not-found.tsx
import { NotFoundPage } from "@/components/not-found-page";
export default function NotFound() {
return <NotFoundPage />;
}Usage
Import
Add the NotFoundPage import.
import { NotFoundPage } from "@/components/not-found-page";Use
Use in app/not-found.tsx.
export default function NotFound() {
return <NotFoundPage />;
}Guidelines
- Use in app/not-found.tsx (Next.js App Router) for the 404 route.
- Requires next/link; ensure you are in a Next.js project.
- Respects useReducedMotion; icon wobble is disabled when reduced motion is preferred.
Props
All props are optional unless marked required. Use these to customize every aspect of the component.
| Prop | Type | Default | Description |
|---|---|---|---|
| className | string | — | Additional CSS classes. |
| homeHref | string | "/" | URL for the back/home link. |
| title | string | "404" | Main heading text. |
| description | string | "Oops! Page not found" | Subheading text. |
| helperText | string | "The page you are looking for..." | Helper paragraph text below the description. |
| backLabel | string | "Back to Home" | Label for the back link. |
| icon | React.ReactNode | <Frown /> | Custom icon rendered above the title. Accepts any React element — lucide icon, SVG, or emoji. |
| buttonClassName | string | — | Additional CSS classes for the back/home button. |
Examples
Basic
Use in app/not-found.tsx for Next.js App Router.
404
Oops! Page not found
The page you are looking for might have been removed, had its name changed, or is temporarily unavailable.
Back to Homeimport { NotFoundPage } from "@/components/not-found-page";
export default function NotFound() {
return <NotFoundPage />;
}Custom text
Customize text and home link.
404
Oops! Page not found
The page you are looking for might have been removed, had its name changed, or is temporarily unavailable.
Back to Homeimport { NotFoundPage } from "@/components/not-found-page";
export default function NotFound() {
return (
<NotFoundPage
homeHref="/"
title="404"
description="Page not found"
backLabel="Go home"
/>
);
}Custom icon
Pass any React element as the icon — a lucide icon, custom SVG, or emoji.
404
Oops! Page not found
The page you are looking for might have been removed, had its name changed, or is temporarily unavailable.
Back to Homeimport { Ghost } from "lucide-react";
import { NotFoundPage } from "@/components/not-found-page";
export default function NotFound() {
return (
<NotFoundPage
icon={
<Ghost className="mx-auto h-24 w-24 text-muted-foreground" />
}
/>
);
}