mcpField
N
nextjs-server-navigation

nextjs-server-navigation

github.com/wsimmonds開発109304 回インストール53 週間前に更新
リポジトリ

nextjs-server-navigation について

Next.js 開発者向けのナビゲーションパターンを解説するスキルです。Server Component でのリンク、リダイレクト、Server Action を扱い、クライアント側の方法との違いも示します。

主な機能

  1. 01Link によるページリンク
  2. 02redirect() による条件付き遷移
  3. 03Server Action からの遷移
  4. 04サーバー側とクライアント側の比較

活用シーン

  1. 01サーバーレンダリングページへのリンク追加
  2. 02認証や権限に応じたリダイレクト
  3. 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'は不要
  • ✅ フックは不要

静的リンクには、このパターンのほうがクライアント側ナビゲーションよりシンプルでパフォーマンスに優れています!

コメント