はじめに
Next.js 15のApp Routerでは、useRouter
フック(next/navigation
からインポート)を使用してプログラムによるページナビゲーションを実現できます。しかし、push
、replace
、back
、refresh
など複数のメソッドがあり、どの場面でどれを使うべきか迷うことがあります。
本記事では、それぞれの関数の違いと適切な使い分けについて、実践的なコード例とともに解説します。
結論
先に結論を言っておくと
-
エラーハンドリングでは基本的に
replace
を使用 -
通常のナビゲーションでは
push
を使用
です。
useRouterの基本的な使い方
まず、基本的な使い方を確認します。
App Routerではnext/navigation
からインポートし、Client Componentで使用します。
"use client";
import { useRouter } from 'next/navigation';
export default function NavigationExample() {
const router = useRouter();
return (
<div>
<button onClick={() => router.push('/about')}>
Aboutページへ
</button>
</div>
);
}
各関数の詳細と使い分け
1. router.push(href)
動作
新しいページをブラウザ履歴スタックに追加
特徴
戻るボタンで前のページに戻ることができる
// 基本的な使用法
router.push('/devices');
// クエリパラメータ付き
router.push('/devices?status=active');
// オブジェクト形式(推奨しない - 文字列推奨)
router.push({
pathname: '/devices',
query: { status: 'active' }
});
// スクロール制御
router.push('/dashboard', { scroll: false });
使用場面
- 通常のページ遷移
- ユーザーが戻る可能性のあるナビゲーション
- 詳細ページや一覧ページへの遷移
2. router.replace(href)
動作
現在のページを新しいページで置き換え
特徴
戻るボタンで置き換え前のページには戻れない
// エラー時のリダイレクト
if (isError) {
router.replace('/dealers'); // エラーページに戻れない
}
// ログイン成功後
if (loginSuccess) {
router.replace('/dashboard'); // ログインページに戻れない
}
// 無効なIDの場合
if (!isValidId) {
router.replace('/not-found');
}
使用場面
- エラー時のリダイレクト
- 認証後のリダイレクト
- 無効なURLからの修正
- フォーム送信完了後の遷移
3. router.back()
動作
ブラウザ履歴の1つ前に戻る
特徴
window.history.back()
と同等
// キャンセルボタン
const handleCancel = () => {
router.back();
};
// モーダルの閉じるボタン
const handleCloseModal = () => {
router.back();
};
使用場面
- キャンセルボタンの実装
- モーダルの閉じる処理
- 戻るボタンの実装
4. router.forward()
動作
ブラウザ履歴の1つ後に進む
特徴
window.history.forward()
と同等(使用頻度は低い)
// 進むボタン(カスタムナビゲーション)
const handleForward = () => {
router.forward();
};
5. router.refresh()
動作
現在のページを再読み込み
特徴
Server Componentsの再実行を行う
// データ更新後の再読み込み
const handleUpdate = async () => {
await updateData();
router.refresh(); // Server Componentsを再実行
};
// 手動リフレッシュボタン
const handleRefresh = () => {
router.refresh();
};
使用場面
- データ更新後のページ再読み込み
- キャッシュをクリアしたい場合
- Server Componentsを再実行したい場合
実践的な使い分け例
エラーハンドリングでの使い分け
export const DealerShowContainer = () => {
const router = useRouter();
const params = useParams();
const dealerId = Number(params.id);
// 無効なID → replace(戻らせない)
useEffect(() => {
if (Number.isNaN(dealerId) || dealerId <= 0) {
toast.error("無効な販売店IDです");
router.replace("/dealers"); // ❌ pushだと無効なページに戻れてしまう
}
}, [dealerId, router]);
// APIエラー → replace(エラーページに戻らせない)
const { data, isError } = useDealerShow({
onError: () => {
toast.error("データの取得に失敗しました");
router.replace("/dealers"); // ❌ pushだとエラーページに戻れてしまう
}
});
// 編集ページへの遷移 → push(通常のナビゲーション)
const handleEdit = () => {
router.push(`/dealers/${dealerId}/edit`); // ✅ 戻れるようにする
};
return (
<div>
<button onClick={handleEdit}>編集</button>
</div>
);
};
フォーム処理での使い分け
export const CreateForm = () => {
const router = useRouter();
const handleSubmit = async (data: FormData) => {
try {
await createDealer(data);
// 作成成功 → replace(フォームに戻らせない)
router.replace('/dealers?created=true');
} catch (error) {
// エラー時はそのまま(フォームを表示し続ける)
toast.error("作成に失敗しました");
}
};
const handleCancel = () => {
// キャンセル → back(前のページに戻る)
router.back();
};
return (
<form onSubmit={handleSubmit}>
{/* フォーム内容 */}
<button type="button" onClick={handleCancel}>
キャンセル
</button>
<button type="submit">作成</button>
</form>
);
};
まとめ
-
push
- 通常のページ遷移(戻れる)
-
replace
- エラー時、認証後、無効状態からのリダイレクト(戻れない)
-
back
- 前のページに戻る
-
refresh
- ページの再読み込み
エラーハンドリングでは基本的にreplace
を使用し、ユーザーがエラー状態のページに戻れないようにするのがベストプラクティスです。通常のナビゲーションではpush
を使用し、ユーザーが期待する戻るボタンの動作を保持しましょう。