3つの違い
router.push() | router.replace() | redirect() | |
|---|---|---|---|
| 使う場所 | Client Component | Client Component | Server 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()