TOOLS BOX/ガイド/useRouter().push() vs redirect()
Concept

useRouter().push() vs redirect()

Client Component でのナビゲーション(useRouter().push())と Server Component / Server Action でのリダイレクト(redirect())の違いと使い分けを整理するガイド。

nextjsrouterredirectnavigationclient-componentserver-componentuseRouter

どういう場面で使うか

  • ·useRouter().push(): ボタンクリックやフォーム送信後など、ユーザー操作を起点に遷移させるとき(Client Component)
  • ·redirect(): 認証チェック・データ取得結果など、サーバー側の条件で遷移させるとき(Server Component / Server Action)

注意点 / Pitfalls

  • ·Server Component で useRouter() を呼ぶとエラーになる(フックはクライアント専用)
  • ·Client Component で redirect() を呼ぶとエラーになる(サーバー専用の throw ベースの関数)
  • ·Server Action 内で redirect() を try/catch の中で呼ぶとリダイレクトが発動しない

補足

どちらも「別ページへ移動する」操作だが、実行コンテキスト(Server / Client)が根本的に異なる。コンポーネントに 'use client' があるかどうかで使う手段が決まる。

何が違うか

useRouter().push()redirect()
使う場所Client ComponentServer Component / layout / Server Action
インポート元next/navigation(hook)next/navigation(関数)
起動タイミングユーザー操作(クリック・送信など)サーバー側の条件判定(認証・バリデーションなど)
遷移の仕組みクライアントサイドナビゲーションHTTP リダイレクト(例外 throw)
React state保持されるページ全体が再ロードされる
Middleware使えない使えない(Middleware は NextResponse.redirect()

useRouter().push() — Client Component でのナビゲーション

ユーザー操作を起点に遷移させるときに使います。"use client" のあるコンポーネントでのみ使えます。

"use client";
import { useRouter } from "next/navigation";

export function LoginForm() {
  const router = useRouter();

  async function handleSubmit(e: React.FormEvent) {
    e.preventDefault();
    const res = await fetch("/api/login", { method: "POST", ... });
    if (res.ok) {
      router.push("/dashboard"); // ログイン成功後にクライアントナビゲーション
      router.refresh();          // Server Component を再フェッチして Cookie を反映
    }
  }

  return <form onSubmit={handleSubmit}>...</form>;
}

router.refresh() を一緒に呼ぶ理由: router.push() だけではサーバーキャッシュが更新されず、Cookie を使った認証状態が Server Component に反映されないことがあります。ログイン・ログアウト後は router.push()router.refresh() をセットで呼びます。

redirect() — Server Component / Server Action でのリダイレクト

サーバー側の条件(認証状態・データ取得結果)に応じて遷移させるときに使います。

// Server Component: 未認証ならログインページへ
import { redirect } from "next/navigation";
import { cookies } from "next/headers";

export default async function DashboardPage() {
  const cookieStore = await cookies();
  if (!cookieStore.get("session_id")) {
    redirect("/login");
  }
  return <Dashboard />;
}
// Server Action: 処理完了後にリダイレクト
"use server";
import { redirect } from "next/navigation";

export async function createPostAction(formData: FormData) {
  const post = await db.post.create({ data: { ... } });
  redirect(`/posts/${post.id}`); // try/catch の外で呼ぶ
}

使える場所 / 使えない場所

コンポーネントに "use client" があるか?
├── Yes(Client Component)
│   ├── ユーザー操作起点の遷移 → useRouter().push()
│   └── redirect() は使えない(エラーになる)
└── No(Server Component / Server Action)
    ├── 条件チェックで遷移 → redirect()
    └── useRouter() は使えない(フックはクライアント専用)

誤用しやすいポイント

Server Component で useRouter を呼ぶ

// ❌ Server Component でフックは使えない
export default async function Page() {
  const router = useRouter(); // エラー: hooks are not allowed in Server Components
  if (!session) router.push("/login");
}

// ✅ Server Component では redirect() を使う
export default async function Page() {
  if (!session) redirect("/login");
}

Client Component で redirect を呼ぶ

// ❌ Client Component で redirect() は使えない
"use client";
export function MyButton() {
  redirect("/somewhere"); // エラー: redirect() only works in Server Components
}

// ✅ Client Component では useRouter().push() を使う
"use client";
export function MyButton() {
  const router = useRouter();
  return <button onClick={() => router.push("/somewhere")}>移動</button>;
}

Server Action で try/catch の中に redirect() を入れる

// ❌ catch で NEXT_REDIRECT がキャッチされてリダイレクトされない
"use server";
export async function myAction() {
  try {
    await doSomething();
    redirect("/done");
  } catch (e) {
    return { error: "失敗" }; // redirect() の throw もここに来てしまう
  }
}

// ✅ try/catch の外で redirect() を呼ぶ
"use server";
export async function myAction() {
  try {
    await doSomething();
  } catch (e) {
    return { error: "失敗" };
  }
  redirect("/done"); // try/catch の外
}

redirect() / NextResponse.redirect() / permanentRedirect() の全体像は → redirect

Client 側の push / replaceredirect() の実務的な使い分けは → router.push() vs router.replace() vs redirect()

関連ドキュメント

関連サンプル

同じテーマや技術スタックを使った実装例