何が違うか
useRouter().push() | redirect() | |
|---|---|---|
| 使う場所 | Client Component | Server 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 / replace と redirect() の実務的な使い分けは → router.push() vs router.replace() vs redirect()