Complete Guide to Back Button Navigation in Next.js
Learn the different approaches to implement back button navigation in Next.js, from simple router.back() to fallback strategies for direct page access.
Back button navigation sounds simple, but in Next.js there are nuances you need to understand to get it right.
The Core Methods
Next.js provides two main approaches for programmatic navigation:
1. Using router.back()
The most common approach uses the useRouter hook from next/navigation:
"use client";
import { useRouter } from "next/navigation";
export const BackButton = () => {
const router = useRouter();
return (
<button onClick={() => router.back()}>
Go Back
</button>
);
};
Key points:
- Requires
"use client"directive since hooks only work in client components - Uses browser's history stack, just like clicking the browser's back button
- If there's no history (user opened page directly), it does nothing
2. Using window.history.back()
For simple cases without needing the router:
"use client";
export const BackButton = () => {
return (
<button onClick={() => window.history.back()}>
Go Back
</button>
);
};
This is functionally equivalent to router.back() but doesn't require importing the router.
The Direct Access Problem
What happens when someone opens your blog post directly from a search result? There's no history to go back to.
Solution: Fallback Navigation
"use client";
import { useRouter } from "next/navigation";
type BackButtonProps = {
fallbackUrl?: string;
};
export const BackButton = ({ fallbackUrl = "/" }: BackButtonProps) => {
const router = useRouter();
const handleBack = () => {
// Check if there's history to go back to
if (window.history.length > 1) {
router.back();
} else {
router.push(fallbackUrl);
}
};
return (
<button onClick={handleBack}>
Go Back
</button>
);
};
Note:
window.history.length > 1isn't foolproof—some browsers initialize with a length of 1 or 2. For critical flows, consider tracking navigation state yourself.
Server vs Client Components
Since useRouter requires client components, you have two patterns:
Pattern A: Dedicated Client Component
Keep your page as a server component, create a small client component just for the button:
// components/back-button.tsx
"use client";
import { useRouter } from "next/navigation";
export const BackButton = ({ label }: { label: string }) => {
const router = useRouter();
return (
<button onClick={() => router.back()}>
{label}
</button>
);
};
// app/blog/[slug]/page.tsx (server component)
import { BackButton } from "@/components/back-button";
export default function BlogPost() {
return (
<article>
<BackButton label="Back to blog" />
{/* rest of content */}
</article>
);
}
Pattern B: Hardcoded Link
If you always want to go to a specific page, just use a regular Link:
import Link from "next/link";
export default function BlogPost() {
return (
<article>
<Link href="/blog">Back to blog</Link>
{/* rest of content */}
</article>
);
}
When to use which:
- Use
router.back()when users might arrive from multiple entry points - Use
Linkwhen the destination is always the same and you want consistent behavior
Styling the Back Button
A minimal, accessible back button with hover animation:
"use client";
import { useRouter } from "next/navigation";
import { ArrowLeft } from "lucide-react";
export const BackButton = ({ label }: { label: string }) => {
const router = useRouter();
return (
<button
onClick={() => router.back()}
className="group flex items-center gap-2 text-sm text-muted-foreground hover:text-foreground transition-colors"
>
<ArrowLeft className="w-4 h-4 transition-transform group-hover:-translate-x-1" />
{label}
</button>
);
};
Common Pitfalls
- Forgetting
"use client"- Router hooks don't work in server components - No fallback - Users opening pages directly will have nowhere to go back to
- Breaking SSG - Using
routerduring render breaks static generation; only use in event handlers - Hydration mismatches - Don't conditionally render based on
window.historyduring initial render
Quick Reference
| Method | Use Case |
|---|---|
router.back() | Dynamic back navigation with Next.js features |
window.history.back() | Simple back without router import |
router.push(url) | Fallback when no history exists |
Link href="" | Fixed destination navigation |
That's the complete picture. For most cases, a simple router.back() in a client component does the job.