概要
Parallel Routes を使うと、同一の URL で複数の独立したページコンテンツを並列表示できる。
@slotName ディレクトリ配下に page.tsx を置き、親の layout.tsx でスロットとして受け取る。
ダッシュボードで複数のウィジェットを独立したセグメントとして管理するケースに向いている。
インストール
# 追加インストールは不要
ディレクトリ構成
src/app/dashboard/
├── layout.tsx ← @analytics と @team を受け取る
├── page.tsx → /dashboard のメインコンテンツ
├── @analytics/
│ ├── page.tsx → analytics スロット(/dashboard でレンダリング)
│ └── default.tsx ← スロットが一致しない場合のフォールバック
└── @team/
├── page.tsx → team スロット(/dashboard でレンダリング)
└── default.tsx
実装例
// src/app/dashboard/layout.tsx
type Props = {
children: React.ReactNode;
analytics: React.ReactNode; // @analytics スロット
team: React.ReactNode; // @team スロット
};
export default function DashboardLayout({ children, analytics, team }: Props) {
return (
<div className="p-6 space-y-6">
{/* メインコンテンツ */}
<section>{children}</section>
{/* Parallel Routes のスロット */}
<div className="grid grid-cols-2 gap-6">
<div className="rounded border p-4">{analytics}</div>
<div className="rounded border p-4">{team}</div>
</div>
</div>
);
}
// src/app/dashboard/page.tsx
export default function DashboardPage() {
return <h1 className="text-lg font-bold">ダッシュボード</h1>;
}
// src/app/dashboard/@analytics/page.tsx
export default function AnalyticsSlot() {
return (
<div>
<h2 className="mb-2 text-sm font-medium text-gray-600">アナリティクス</h2>
<p className="text-2xl font-bold">1,234</p>
<p className="text-xs text-gray-400">先月比 +12%</p>
</div>
);
}
// src/app/dashboard/@team/page.tsx
export default function TeamSlot() {
return (
<div>
<h2 className="mb-2 text-sm font-medium text-gray-600">チームメンバー</h2>
<ul className="space-y-1 text-sm">
<li>田中 太郎</li>
<li>鈴木 花子</li>
</ul>
</div>
);
}
// src/app/dashboard/@analytics/default.tsx(フォールバック)
export default function AnalyticsDefault() {
return null;
}
ポイント
@slotNameディレクトリ名がスロット名になり、親layout.tsxの props として受け取る- スロットは URL に影響しない(
/dashboardのまま) default.tsxを置かないとスロットが一致しない URL で 404 になる- 各スロットは独立してナビゲーションできるため、スロットごとに
loading.tsx/error.tsxを配置できる - Route Groups と組み合わせてレイアウトをさらに細かく分けることも可能