nextjs-server-navigation について
Next.js 開発者向けのナビゲーションパターンを解説するスキルです。Server Component でのリンク、リダイレクト、Server Action を扱い、クライアント側の方法との違いも示します。
主な機能
- 01Link によるページリンク
- 02redirect() による条件付き遷移
- 03Server Action からの遷移
- 04サーバー側とクライアント側の比較
活用シーン
- 01サーバーレンダリングページへのリンク追加
- 02認証や権限に応じたリダイレクト
- 03フォーム送信後のページ遷移
インストール
コマンドをコピーしてターミナルで実行します。
$
npx skills add https:/ / github.com/ wsimmonds/ claude-nextjs-skills --skill nextjs-server-navigationインストール後、Claude Code で/nextjs-server-navigationを入力して読み込まれたか確認します。
詳細
Next.js:サーバーコンポーネントのナビゲーションパターン
⚠️ 重大なルール
サーバーコンポーネントとクライアントコンポーネントでは、ナビゲーション方法が異なります!
サーバーレンダリングによるナビゲーションが必要な場合(たとえば、別のページへのリンク、チェック後のリダイレクト、ルーティングパターンのデモなど)は、サーバーコンポーネント内で<Link>とredirect()を使ってください。クライアント専用の API が必要な場合を除き、引き続き'use client'は使いません。
パターン
シナリオ: 適切なナビゲーションパターンを示すサーバーコンポーネントを作成します
✅ 正しい方法:
// app/page.tsx (Server Component - NO 'use client'!)
import Link from 'next/link';
export default async function Page() {
return (
<div>
<h1>Home</h1>
<Link href="/dashboard">Go to Dashboard</Link>
<Link href="/profile">View Profile</Link>
</div>
);
}
❌ 間違った方法:
// 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
// ...
}
サーバーでのナビゲーション方法
方法 1:Link コンポーネント(リンクにおすすめ)
// 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 (
<div>
<h1>Welcome</h1>
{/* 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>
);
}
ポイント:
- ✅ サーバーコンポーネントで動作します(
'use client'は不要です) - ✅ 非同期関数にできます
- ✅ データを取得できます
- ✅ フックは不要です
方法 2:redirect() 関数(条件付きリダイレクト用)
// 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 <div>Welcome, {user.name}!</div>;
}
redirect()を使う場面:
- サーバー側のデータに基づく条件付きリダイレクト
- 認証チェック
- 権限の検証
- データに基づくルーティング
方法 3:サーバーアクションを使ったボタン
// app/page.tsx
import { logout } from './actions';
export default async function Page() {
return (
<div>
<h1>Dashboard</h1>
<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');
}
完全な例:ナビゲーションパターン
// 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 (
<div>
<h1>Welcome to Our App</h1>
{/* 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:any型は絶対に使わない
// ❌ 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<HTMLButtonElement>) { ... }
サーバーとクライアントのナビゲーション比較
| 機能 | サーバーコンポーネント | クライアントコンポーネント |
|---|---|---|
<Link> | ✅ はい | ✅ はい |
redirect() | ✅ はい | ❌ いいえ |
useRouter() | ❌ いいえ | ✅ はい |
usePathname() | ❌ いいえ | ✅ はい |
| async function | ✅ はい | ❌ いいえ |
| 'use client' | ❌ いいえ | ✅ はい |
よくある間違いとその回避方法
❌ 間違い 1:ナビゲーションのために'use client'を追加する
// ❌ 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>;
}
❌ 間違い 2:サーバーコンポーネントでuseRouter()を使う
// ❌ 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>;
}
❌ 間違い 3:単純なナビゲーションのためにコンポーネントをクライアント側にする
// ❌ WRONG - Loses server component benefits!
'use client';
export default function Page() {
return (
<div>
<Link href="/dashboard">Dashboard</Link>
</div>
);
}
// ✅ CORRECT - Keep it as a server component!
export default async function Page() {
// Can now fetch data server-side
const data = await fetchData();
return (
<div>
<Link href="/dashboard">Dashboard</Link>
<p>{data.message}</p>
</div>
);
}
高度なパターン
サーバーアクションでのプログラムによるナビゲーション
// app/page.tsx
import { createPost } from './actions';
export default async function Page() {
return (
<form action={createPost}>
<input name="title" required />
<button type="submit">Create Post</button>
</form>
);
}
// 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}`);
}
サーバーコンポーネント内の複数のリンク
// app/page.tsx
import Link from 'next/link';
export default async function NavigationPage() {
const pages = await fetchPages();
return (
<nav>
<h2>Site Navigation</h2>
<ul>
{pages.map((page) => (
<li key={page.id}>
<Link href={`/pages/${page.slug}`}>
{page.title}
</Link>
</li>
))}
</ul>
</nav>
);
}
クイック判断フロー
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()
代わりにクライアント側ナビゲーションを使う場面
次のものが必要な場合に限り、クライアントコンポーネント('use client' + useRouter())を使います。
- クライアントの状態に基づくプログラムによるナビゲーション
- クライアント側のアニメーション後のナビゲーション
- ブラウザー専用 API(window、localStorage)
- React フック(useState、useEffect)
それ以外の場合はすべて、サーバーコンポーネントのナビゲーションを使ってください!
クイックチェックリスト
「ナビゲーションパターンを示す」とある場合:
- サーバーコンポーネントを作成する(
'use client'は付けない) - 'next/link'から
Linkをインポートする - href propを指定した
<Link>コンポーネントを追加する - データを取得する場合、コンポーネントを
asyncのままにする - next/navigationからuseRouterをインポートしない
- 'use client'ディレクティブを追加しない
- 適切なTypeScript型を使う(
anyは使わない)
まとめ
サーバーコンポーネントのナビゲーション:
- ✅ ナビゲーションリンクには
<Link>を使う - ✅ 条件付きリダイレクトには
redirect()を使う - ✅ 必要に応じてコンポーネントをasyncのままにする
- ✅ 'use client'は不要
- ✅ フックは不要
静的リンクには、このパターンのほうがクライアント側ナビゲーションよりシンプルでパフォーマンスに優れています!
コメント
Markdown 対応