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;
backLabel?: string;
icon?: React.ReactNode;
}
export function NotFoundPage({
className,
homeHref = "/",
title = "404",
description = "Oops! Page not found",
backLabel = "Back to Home",
icon,
}: 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">
The page you are looking for might have been
removed, had its name changed, or is temporarily
unavailable.
</p>
<Link
href={homeHref}
className="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"
>
<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;
backLabel?: string;
icon?: React.ReactNode;
}
export function NotFoundPage({
className,
homeHref = "/",
title = "404",
description = "Oops! Page not found",
backLabel = "Back to Home",
icon,
}: 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">
The page you are looking for might have been
removed, had its name changed, or is temporarily
unavailable.
</p>
<Link
href={homeHref}
className="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"
>
<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
| 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. |
| 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. |
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" />
}
/>
);
}