第65章:Parallel Routes の考え方(ダッシュボードに強い)🪟✨
ダッシュボードって、「メイン」「右の通知」「左のメニュー」みたいに、1画面に複数のエリアが並びがちだよね😊 Parallel Routes(パラレルルート)は、その複数エリアを “それぞれ別ルート”として同時に描画できる仕組みだよ〜!🧩✨ (Next.js)
1) Parallel Routes ってなに?🧠💡
-
ふつうのルーティング:
/dashboardは 1つのページ(ツリー)で構成される -
Parallel Routes:
/dashboardの中で 複数のページ(ツリー)を同時に表示できる- 例:
teamパネルとanalyticsパネルを同じ画面で並べる🪟🪟 (Next.js)
- 例:
そして、Parallel Routes は “スロット(slot)” という考え方で作るよ👇
スロットは @folder で作って、同じ階層の layout.tsx に props として渡されるのがポイント!🎁 (Next.js)
2) イメージ図(スロットが layout に刺さる)🧩➡️🧱
3) 最重要ルール3つだけ覚えよ〜📌✨
✅ ルール①:@xxx フォルダが “スロット” 🌟
@teamとか@analyticsとかね! (Next.js)
✅ ルール②:スロット名は URLに出ない😳
@team/membersを作っても、URLは.../membersみたいに見える(@teamはURLに入らない) (Next.js)
✅ ルール③:default.tsx は “保険” 🛟
「そのURLに対するページがスロット側に無い」時、 リロード(F5)すると default が必要になることがあるよ!無いと 404 になることも😱 (Next.js)
4) ハンズオン:ミニダッシュボードを作ろう〜!🏗️🎉
フォルダ構成(まずこれを作る)📁✨
app/
dashboard/
dashboard.module.css
layout.tsx
page.tsx
settings/
page.tsx
@team/
page.tsx
default.tsx
@analytics/
page.tsx
loading.tsx
error.tsx
default.tsx
4-1) app/dashboard/layout.tsx(3カラムで並べる)🪟🪟🪟
import styles from './dashboard.module.css'
export default function DashboardLayout({
children,
team,
analytics,
}: {
children: React.ReactNode
team: React.ReactNode
analytics: React.ReactNode
}) {
return (
<div className={styles.wrap}>
<header className={styles.header}>
<h1 className={styles.title}>Dashboard 🪟✨</h1>
<p className={styles.sub}>Parallel Routes で 3エリア同時表示 💖</p>
</header>
<div className={styles.grid}>
<main className={styles.main}>
<h2 className={styles.h2}>Main 🧡</h2>
{children}
</main>
<aside className={styles.panel}>
<h2 className={styles.h2}>Team 👥</h2>
{team}
</aside>
<aside className={styles.panel}>
<h2 className={styles.h2}>Analytics 📊</h2>
{analytics}
</aside>
</div>
</div>
)
}
4-2) app/dashboard/dashboard.module.css(見た目かる〜く)💅✨
.wrap {
padding: 16px;
font-family: system-ui, -apple-system, "Segoe UI", sans-serif;
}
.header {
margin-bottom: 12px;
}
.title {
margin: 0;
font-size: 22px;
}
.sub {
margin: 4px 0 0;
color: #666;
}
.grid {
display: grid;
grid-template-columns: 1.6fr 1fr 1fr;
gap: 12px;
align-items: start;
}
.main, .panel {
border: 1px solid #ddd;
border-radius: 12px;
padding: 12px;
background: #fff;
}
.h2 {
margin: 0 0 8px;
font-size: 16px;
}
4-3) app/dashboard/page.tsx(メイン枠:/dashboard)🏠✨
import Link from 'next/link'
export default function DashboardPage() {
return (
<div>
<p>ここはメインエリアだよ〜😊🧡</p>
<ul>
<li>
<Link href="/dashboard/settings">設定ページへ ⚙️➡️</Link>
</li>
<li>
<Link href="/dashboard">ダッシュボードTOPへ 🏠</Link>
</li>
</ul>
<p style={{ marginTop: 12 }}>
💡 右の2つ(Team/Analytics)は、同時に別スロットで描画されてるよ🪟✨
</p>
</div>
)
}
4-4) @team スロット(常に出したいパネル)👥✨
app/dashboard/@team/page.tsx
export default function TeamPanel() {
return (
<div>
<p>チームの今日の予定だよ〜📅✨</p>
<ul>
<li>ゼミ:13:00〜 📚</li>
<li>ミーティング:16:00〜 🧑💻</li>
</ul>
</div>
)
}
app/dashboard/@team/default.tsx
export default function TeamDefault() {
return <p>Teamパネルはこの画面では省略だよ🙂🛟</p>
}
4-5) @analytics スロット(わざと “遅い” パネル)📊⏳
app/dashboard/@analytics/page.tsx
function sleep(ms: number) {
return new Promise((r) => setTimeout(r, ms))
}
export default async function AnalyticsPanel() {
// デモ用:わざと遅くするよ〜⏳(本番で多用はしないでね)
await sleep(1200)
return (
<div>
<p>アクセス解析だよ〜📊✨</p>
<ul>
<li>PV:1,234 👀</li>
<li>UU:456 🙋♀️</li>
<li>直帰率:38% 🏃♀️</li>
</ul>
</div>
)
}
app/dashboard/@analytics/loading.tsx(このスロットだけローディング出せる!)🫧
export default function AnalyticsLoading() {
return <p>解析データ読み込み中…⏳📊</p>
}
app/dashboard/@analytics/error.tsx(このスロットだけエラーUI)🧯
'use client'
export default function AnalyticsError({
error,
reset,
}: {
error: Error
reset: () => void
}) {
return (
<div>
<p>うわっ…解析パネルでエラー🥺🧯</p>
<p style={{ color: '#666' }}>{error.message}</p>
<button onClick={() => reset()}>もう一回やる🔁✨</button>
</div>
)
}
app/dashboard/@analytics/default.tsx(保険🛟)
※ default は「初回ロードやリロード時に、合わないスロットをどうする?」のための fallback だよ〜! (Next.js)
export default function AnalyticsDefault() {
return <p>この画面では解析パネルはお休み〜🙂🛟</p>
}
4-6) /dashboard/settings(メインだけ切り替える)⚙️✨
app/dashboard/settings/page.tsx
import Link from 'next/link'
export default function DashboardSettings() {
return (
<div>
<p>設定ページだよ〜⚙️✨</p>
<p>ここではメインだけが差し替わるイメージ!😊</p>
<Link href="/dashboard">戻る🏠⬅️</Link>
</div>
)
}
5) 動かして確認しよ〜!🧪💻
-
npm run devで起動 ▶️ -
http://localhost:3000/dashboardを開く🏠 -
右の Analytics が 遅れて出る(loading が先に出る)⏳📊
-
「設定ページへ⚙️➡️」を押して
.../dashboard/settingsに移動 -
そのまま F5でリロードしてみてね
- スロット側に該当ページが無い時、
default.tsxが表示されることがあるよ🛟✨ (Next.js)
- スロット側に該当ページが無い時、
6) 「え、なんで便利なの?」まとめ🧁✨
Parallel Routes がダッシュボードに強い理由はこれ👇
- ✅ 同じ画面に複数エリアを同時表示できる(team と analytics を並べる、みたいに) (Next.js)
- ✅ スロットごとに loading / error を分けられる(解析だけ待つ、解析だけ落ちても他は生きる) (Next.js)
- ✅ クライアント遷移(Linkで移動)では、スロットごとに “表示状態” を保持しやすい(挙動の違いは docs の Behavior 参照) (Next.js)
7) ちょい注意ポイント⚠️🐣
- スロットは URLに出ないから、フォルダを見て「どこが表示に刺さるか」を意識すると迷子になりにくいよ🗺️✨ (Next.js)
- 同じ階層で、片方だけ静的・片方だけ動的…みたいな混在はできないケースがあるよ(同レベルのスロットはまとめて扱われる)🧊⚡ (Next.js)
8) ミニ確認クイズ(3問)📝💖
@analyticsは URL に出る?出ない?😳- スロットの中身は
layout.tsxに何として渡される?🎁 - リロードしたとき、合わないスロットの保険になるファイルは?🛟
(答え:1) 出ない 2) props 3) default.tsx / default.js) (Next.js)