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

router.push() vs router.replace() vs redirect()

Client Component でのナビゲーション手段(router.push() / router.replace())と Server 側リダイレクト(redirect())の選び分けガイド。履歴の残り方・実行コンテキスト・典型ユースケースで判断する。

nextjsrouterpushreplaceredirectnavigationhistoryclient-componentserver-component

どういう場面で使うか

  • ·router.push(): ユーザー操作後に新しいページへ移動し、戻るボタンで元のページに戻れるようにしたいとき
  • ·router.replace(): ログイン後・フォーム完了後など、戻るボタンで前の状態に戻れたくないとき / URL を更新しながら履歴を汚したくないとき
  • ·redirect(): Server Component / Server Action でサーバー側の条件(認証チェック・処理結果)に基づいて強制転送するとき

注意点 / Pitfalls

  • ·ログイン後の遷移に router.push() を使うと、戻るボタンでログインページに戻ってしまう(router.replace() が正しい)
  • ·Client Component で redirect() を呼ぶとエラーになる(サーバー専用の throw ベースの関数)
  • ·router.push() / router.replace() 後に Server Component のデータを更新するには router.refresh() を合わせて呼ぶ必要がある
  • ·Server Action の完了後に Client 側で router.push() を呼ぶより、Server Action 内で redirect() を呼ぶほうが自然な場合が多い

補足

push と replace はどちらも Client Component でのクライアントサイドナビゲーション。違いはブラウザ履歴スタックへの積み方だけ。redirect() はサーバー側の throw であり、実行コンテキストが根本的に異なる。

3つの違い

router.push()router.replace()redirect()
使う場所Client ComponentClient ComponentServer Component / Server Action
実行タイミングユーザー操作ユーザー操作サーバー側の条件判定
ブラウザ履歴新しいエントリを追加現在のエントリを上書きページ全体を再ロード(履歴は保持)
戻るボタン前のページに戻れる前のページに戻れない戻ることができる(サーバー側で再チェック)
インポート元next/navigation(hook)next/navigation(hook)next/navigation(関数)

router.push() — 履歴を積むナビゲーション

新しいページへ移動し、ブラウザ履歴に追加します。ユーザーが「戻る」で元の画面に戻れるシナリオに向いています。

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

function SearchButton({ query }: { query: string }) {
  const router = useRouter();
  return (
    <button onClick={() => router.push(`/search?q=${query}`)}>
      検索
    </button>
  );
}

向いている操作:

  • 記事詳細への遷移(一覧に戻りたい)
  • 検索結果ページへの移動
  • ウィザード形式の「次へ」(前のステップに戻れる)

router.replace() — 履歴を上書きするナビゲーション

現在の履歴エントリを置き換えます。戻るボタンで前の状態に戻れたくない場面に向いています。

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

export default 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.replace("/dashboard"); // ログインページに戻れなくする
      router.refresh();             // Server Component を再フェッチ
    }
  }
  return <form onSubmit={handleSubmit}>...</form>;
}

向いている操作:

  • ログイン成功後のダッシュボードへの遷移
  • フォーム送信完了後の完了ページへの遷移
  • タブ切り替え・ページネーションなど「履歴を汚したくない URL 更新」

redirect() — サーバー側の強制転送

Server Component・Server Action・Route Handler で使います。ユーザー操作ではなく、サーバー側の条件判定(認証・データ取得結果)で遷移させます。

// 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"); // throw で処理を中断
  }
  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 の外で呼ぶ
}

典型ユースケースでの使い分け

ユースケース推奨手段理由
ログイン成功後 → ダッシュボードrouter.replace()ログインページに戻れたくない
ログアウト後 → ログインページrouter.replace()ダッシュボードに戻れたくない
記事詳細 → 一覧に戻れる遷移router.push()戻るボタンを活かしたい
フォーム送信完了 → 完了ページrouter.replace()再送信防止・戻れたくない
タブ切り替え・フィルタ変更router.replace()操作のたびに履歴を積まない
未認証ユーザーをログインへredirect()Server Component での認証ガード
Server Action 完了後に遷移redirect()サーバー側処理の一部として完結

誤用しやすいポイント

ログイン後に push を使う

// ❌ 戻るボタンでログインページに戻ってしまう
router.push("/dashboard");

// ✅ replace でログインページを履歴から消す
router.replace("/dashboard");
router.refresh(); // Cookie を Server Component に反映

Client Component で redirect() を呼ぶ

// ❌ Client Component で redirect() はエラーになる
"use client";
export function MyForm() {
  redirect("/done"); // Error: redirect() only works in Server Components
}

// ✅ Client Component では router.replace() または router.push() を使う
"use client";
export function MyForm() {
  const router = useRouter();
  // ...
  router.replace("/done");
}

push / replace 後に Server Component が更新されない

// ❌ Cookie を使う Server Component が古いデータを返し続ける
router.replace("/dashboard");

// ✅ refresh() でサーバーキャッシュを再フェッチ
router.replace("/dashboard");
router.refresh();

宣言的ナビゲーション(Link)と router.push() の使い分けは → Link vs router.push()

Client vs Server の大きい違いは → useRouter().push() vs redirect()

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

router.push() / router.replace() と一緒に使う router.refresh() の役割は → router.refresh()

関連ドキュメント

関連サンプル

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