Next.js: Server Component Navigation Pattern ⚠️ CRITICAL RULE
Server Components use DIFFERENT navigation methods than Client Components!
When requirements call for server-rendered navigation—for example, linking to other pages, redirecting after a check, or demonstrating routing patterns—prefer <Link> and redirect() within Server Components. You still avoid 'use client' unless a client-only API is involved.
The Pattern
Scenario: build a server component that demonstrates proper navigation patterns
✅ CORRECT Solution:
// app/page.tsx (Server Component - NO 'use client'!) import Link from 'next/link';
export default async function Page() { return (
Home
<Link href="/dashboard">Go to Dashboard</Link> <Link href="/profile">View Profile</Link>❌ WRONG Solution:
// app/page.tsx 'use client'; // ❌ NO! Server components don't need this for navigation!
import { useRouter } from 'next/navigation'; // ❌ Wrong for server components
export default function Page() { const router = useRouter(); // ❌ This is client-side navigation // ... }
Server Navigation Methods Method 1: Link Component (Recommended for Links) // app/page.tsx import Link from 'next/link';
export default async function Page() { // Can still fetch data - this is a server component! const data = await fetchData();
return (
Welcome
{/* Simple navigation link */}
<Link href="/about">About Us</Link>
{/* Dynamic link */}
<Link href={`/products/${data.productId}`}>View Product</Link>
{/* Link with styling */}
<Link href="/dashboard" className="btn-primary">
Dashboard
</Link>
</div>
); }
Key Points:
✅ Works in Server Components (no 'use client' needed) ✅ Can be async function ✅ Can fetch data ✅ No hooks required Method 2: redirect() Function (For Conditional Redirects) // app/profile/page.tsx import { redirect } from 'next/navigation'; import { cookies } from 'next/headers';
export default async function ProfilePage() { // Check authentication const cookieStore = await cookies(); const session = cookieStore.get('session');
// Redirect if not authenticated if (!session) { redirect('/login'); }
// Fetch user data const user = await fetchUser(session.value);
return
When to use redirect():
Conditional redirects based on server-side data Authentication checks Permission validation Data-based routing Method 3: Button with Server Action // app/page.tsx import { logout } from './actions';
export default async function Page() { return (
Dashboard
<form action={logout}>
<button type="submit">Logout</button>
</form>
</div>
); }
// app/actions.ts 'use server';
import { redirect } from 'next/navigation';
export async function logout() { // Clear session await clearSession();
// Redirect to login page redirect('/login'); }
Complete Example: Navigation Patterns // app/page.tsx - Demonstrates multiple navigation patterns
import Link from 'next/link'; import { redirect } from 'next/navigation'; import { headers } from 'next/headers';
export default async function HomePage() { // Server-side logic const headersList = await headers(); const userAgent = headersList.get('user-agent');
// Conditional redirect example if (userAgent?.includes('bot')) { redirect('/bot-page'); }
return (
Welcome to Our App
{/* Navigation Links */}
<nav>
<Link href="/about">About</Link>
<Link href="/products">Products</Link>
<Link href="/contact">Contact</Link>
</nav>
{/* Button-style link */}
<Link href="/get-started" className="button">
Get Started
</Link>
{/* Dynamic link */}
<Link href={`/user/${123}`}>View Profile</Link>
</div>
); }
TypeScript: NEVER Use any Type // ❌ WRONG function handleClick(e: any) { ... }
// ✅ CORRECT - Not needed in server components! // Server components don't have onClick handlers
// For client components with handlers:
'use client';
function handleClick(e: React.MouseEvent
Server vs Client Navigation Comparison Feature Server Component Client Component <Link> ✅ Yes ✅ Yes redirect() ✅ Yes ❌ No useRouter() ❌ No ✅ Yes usePathname() ❌ No ✅ Yes async function ✅ Yes ❌ No 'use client' ❌ No ✅ Yes Common Mistakes to Avoid ❌ Mistake 1: Adding 'use client' for Navigation // ❌ WRONG 'use client'; // Don't add this just for navigation!
import Link from 'next/link';
export default function Page() { return <Link href="/about">About</Link>; }
// ✅ CORRECT import Link from 'next/link';
// No 'use client' needed! export default async function Page() { return <Link href="/about">About</Link>; }
❌ Mistake 2: Using useRouter() in Server Component // ❌ WRONG import { useRouter } from 'next/navigation'; // This is for CLIENT components!
export default async function Page() { const router = useRouter(); // ERROR! Can't use hooks in server components // ... }
// ✅ CORRECT - Use Link or redirect() import Link from 'next/link'; import { redirect } from 'next/navigation';
export default async function Page() { // Conditional redirect const shouldRedirect = await checkSomething(); if (shouldRedirect) { redirect('/other-page'); }
// Or navigation links return <Link href="/other-page">Go</Link>; }
❌ Mistake 3: Making Component Client-Side for Simple Navigation // ❌ WRONG - Loses server component benefits! 'use client';
export default function Page() { return (
// ✅ CORRECT - Keep it as a server component! export default async function Page() { // Can now fetch data server-side const data = await fetchData();
return (
{data.message}
Advanced Patterns Programmatic Navigation in Server Actions // app/page.tsx import { createPost } from './actions';
export default async function Page() { return (
); }// app/actions.ts 'use server';
import { redirect } from 'next/navigation';
export async function createPost(formData: FormData) { const title = formData.get('title') as string;
// Save to database const post = await db.posts.create({ title });
// Redirect to the new post
redirect(/posts/${post.id});
}
Multiple Links in Server Component // app/page.tsx import Link from 'next/link';
export default async function NavigationPage() { const pages = await fetchPages();
return ( ); }
Quick Decision Tree Need navigation in a component? │ ├─ Is it a Server Component (no 'use client')? │ ├─ Static link → Use <Link> │ ├─ Conditional redirect → Use redirect() │ └─ Form submission → Server Action with redirect() │ └─ Is it a Client Component ('use client')? ├─ Link → Use <Link> (works in both!) └─ Programmatic → Use useRouter()
When to Use Client-Side Navigation Instead
Use Client Components ('use client' + useRouter()) ONLY when you need:
Programmatic navigation based on client state Navigation after client-side animations Browser-only APIs (window, localStorage) React hooks (useState, useEffect)
For everything else, use Server Component navigation!
Quick Checklist
When you see "demonstrates navigation patterns":
Create a server component (no 'use client') Import Link from 'next/link' Add <Link> components with href prop Keep component as async if fetching data Do NOT import useRouter from next/navigation Do NOT add 'use client' directive Use proper TypeScript types (no any) Summary
Server Component Navigation:
✅ Use <Link> for navigation links ✅ Use redirect() for conditional redirects ✅ Keep component async if needed ✅ No 'use client' required ✅ No hooks needed
This pattern is simpler and more performant than client-side navigation for static links!
← 返回排行榜