带命名插槽与默认片段的并行路由
使用 @slot 约定和 default.tsx,同时渲染多个相互独立的路由树。
带命名插槽与默认片段的并行路由 是 CoddyKit 上的免费 Next.js 15 Fullstack (App Router + Server Actions) 课时。 这是第 1 节课,共 4 节。 你可以在下方免费阅读本课时的完整内容 — 然后在浏览器中使用内置代码编辑器和全天候 AI 导师进行实践。 这是 Next.js 15 Fullstack (App Router + Server Actions) 学习路径的一部分,你的进度在网页和 CoddyKit 应用中同步。 Next.js 15 Fullstack (App Router + Server Actions) 课程共包含 4 节课。
本课时的部分内容尚未翻译,以英文显示。
What Are Parallel Routes?
Parallel Routes is a Next.js App Router feature that lets you render multiple independent route segments simultaneously inside a single layout. Each segment lives in its own named slot and can navigate, load, and error independently.
- Think of a dashboard with a sidebar, a main panel, and a notification feed — all loaded in parallel, each with its own loading/error states.
- Parallel routes are defined using the
@foldernaming convention inside a route segment directory. - Each slot becomes a prop on the parent
layout.tsx.
This is fundamentally different from nested layouts: parallel slots share the same URL but render separate route trees side by side.
The @slot Convention
To create a parallel slot, prefix a folder with @ inside any route segment. Next.js treats these as named slots rather than URL segments.
app/dashboard/@analytics/page.tsx→ slot namedanalyticsapp/dashboard/@team/page.tsx→ slot namedteam- The
@foldername does not appear in the URL — visiting/dashboardrenders all matching slots.
A typical parallel-routes directory tree looks like this:
// Directory layout (not runnable — filesystem structure)
// app/
// dashboard/
// layout.tsx <- receives @analytics and @team as props
// page.tsx <- default slot content (children)
// @analytics/
// page.tsx
// loading.tsx
// @team/
// page.tsx
// error.tsxWriting the Parallel Layout
The parent layout.tsx receives each named slot as a React prop alongside the standard children prop. You can position them anywhere in the JSX.
- TypeScript types for slots are just
React.ReactNode. - Slots are completely independent — one can suspend while another is already rendered.
- The
childrenprop itself is an implicit slot namedchildren.
// app/dashboard/layout.tsx
import React from 'react';
interface DashboardLayoutProps {
children: React.ReactNode;
analytics: React.ReactNode;
team: React.ReactNode;
}
export default function DashboardLayout({
children,
analytics,
team,
}: DashboardLayoutProps) {
return (
<div className="dashboard-grid">
<main className="col-span-2">{children}</main>
<aside className="analytics-panel">{analytics}</aside>
<aside className="team-panel">{team}</aside>
</div>
);
}Creating Slot Pages
Each named slot needs its own page.tsx. These pages are standard Server Components (or Client Components) — there is nothing special about their internal structure.
- Slots can have their own
loading.tsx,error.tsx, and nested routes. - Data fetching inside a slot is independent: one slot can
awaita slow database call without blocking the others.
// app/dashboard/@analytics/page.tsx
import { getAnalyticsSummary } from '@/lib/analytics';
export default async function AnalyticsSlot() {
const summary = await getAnalyticsSummary();
return (
<section>
<h2 className="text-lg font-semibold">Analytics</h2>
<p>Total visits: <strong>{summary.totalVisits}</strong></p>
<p>Conversion rate: <strong>{summary.conversionRate}%</strong></p>
</section>
);
}
// app/dashboard/@team/page.tsx
import { getTeamMembers } from '@/lib/team';
export default async function TeamSlot() {
const members = await getTeamMembers();
return (
<ul>
{members.map((m) => (
<li key={m.id}>{m.name} — {m.role}</li>
))}
</ul>
);
}Independent Loading States
Each slot can ship its own loading.tsx. Next.js wraps it in a Suspense boundary automatically, so slots stream in independently — fast slots appear immediately while slow ones show a skeleton.
- This eliminates the waterfall problem common in single-tree layouts.
- Users see meaningful UI much sooner because partial content renders first.
// app/dashboard/@analytics/loading.tsx
export default function AnalyticsLoading() {
return (
<div className="animate-pulse space-y-2">
<div className="h-4 bg-gray-200 rounded w-1/2" />
<div className="h-4 bg-gray-200 rounded w-3/4" />
<div className="h-4 bg-gray-200 rounded w-2/3" />
</div>
);
}
// app/dashboard/@team/loading.tsx
export default function TeamLoading() {
return (
<ul className="space-y-2">
{Array.from({ length: 4 }).map((_, i) => (
<li key={i} className="h-4 bg-gray-200 rounded animate-pulse" />
))}
</ul>
);
}The Problem: Soft Navigation and Missing Slots
Parallel routes shine during hard navigation (full page load), but soft navigation (client-side link clicks) reveals a problem.
When a user navigates to a sub-route — for example /dashboard/settings — Next.js looks for a matching page in every active slot. If a slot has no page for /settings, it becomes unmatched.
- An unmatched slot does not simply disappear — Next.js needs to know what to render.
- Without a fallback, Next.js renders a 404 for the entire page.
- The solution is
default.tsx.
Rescuing Unmatched Slots with default.tsx
default.tsx is the fallback file Next.js renders for a slot when it has no matching page.tsx for the current URL. It acts like a catch-all within the slot's subtree.
- Place
default.tsxat the root of each slot:@analytics/default.tsx. - During soft navigation to an unmatched sub-route, Next.js renders
default.tsxinstead of 404-ing. default.tsxreceives the same params as a page; you can use it to show the slot's normal content unchanged, or a meaningful placeholder.
// app/dashboard/@analytics/default.tsx
// Rendered when the current URL has no matching page in this slot.
import { getAnalyticsSummary } from '@/lib/analytics';
export default async function AnalyticsDefault() {
// Reuse the same data as the main analytics page so the slot
// stays visible during navigation to other sub-routes.
const summary = await getAnalyticsSummary();
return (
<section>
<h2 className="text-lg font-semibold">Analytics</h2>
<p>Total visits: <strong>{summary.totalVisits}</strong></p>
</section>
);
}default.tsx for the Root children Slot
The implicit children slot (i.e. page.tsx at the segment root) also needs a default.tsx when parallel slots are present. Without it, navigating to a named slot's sub-route that doesn't match children will 404.
- Add
app/dashboard/default.tsxalongsideapp/dashboard/page.tsx. - It typically re-renders the same content as
page.tsx, or a neutral empty state.
// app/dashboard/default.tsx
// Fallback for the implicit 'children' slot during soft navigation.
export default function DashboardDefault() {
return (
<div>
<h1 className="text-2xl font-bold">Dashboard</h1>
<p className="text-gray-500">Select an item to get started.</p>
</div>
);
}Nested Routes Inside Slots
Named slots support their own nested route hierarchies. You can add [id] dynamic segments, route groups, or additional layouts entirely inside a slot — all without affecting the URL segments of sibling slots.
- Example:
@analytics/chart/[chartId]/page.tsxrenders at/dashboard/chart/42only inside the analytics slot. - The parent layout and other slots remain untouched.
- Each nested level inside a slot can have its own
loading.tsx,error.tsx, anddefault.tsx.
// app/dashboard/@analytics/chart/[chartId]/page.tsx
interface Props {
params: Promise<{ chartId: string }>;
}
export default async function ChartDetailPage({ params }: Props) {
const { chartId } = await params;
// In a real app, fetch chart data here
return (
<div>
<h3>Chart #{chartId}</h3>
<p>Detailed breakdown for chart {chartId}.</p>
</div>
);
}
// app/dashboard/@analytics/chart/[chartId]/default.tsx
export default function ChartDefault() {
return <p className="text-sm text-gray-400">No chart selected.</p>;
}Practical Pattern: Modal Alongside Page
A classic use-case for parallel routes is rendering a modal in one slot while the main content stays visible in another — without losing the current scroll position or unmounting components.
- Create an
@modalslot with adefault.tsxthat returnsnull(nothing visible by default). - When a user clicks a trigger, Next.js navigates to the modal's page, filling the slot.
- The main
childrenslot continues rendering unchanged.
// app/dashboard/@modal/default.tsx
// Renders nothing when no modal route is matched.
export default function ModalDefault() {
return null;
}
// app/dashboard/@modal/user/[id]/page.tsx
interface Props {
params: Promise<{ id: string }>;
}
export default async function UserModal({ params }: Props) {
const { id } = await params;
return (
<div className="fixed inset-0 bg-black/50 flex items-center justify-center">
<div className="bg-white rounded-lg p-6 w-96">
<h2 className="text-xl font-bold">User #{id}</h2>
<p>Profile details would appear here.</p>
</div>
</div>
);
}TypeScript Tip: Strongly-Typed Slot Props
As the number of slots grows, keep your layout props explicit with a dedicated TypeScript interface. This makes refactoring safer and IDE autocompletion more helpful.
- Slot props are always
React.ReactNode— they accept any renderable value Next.js injects. - You can combine parallel slots with
searchParamsor routeparamsin the same layout signature. - For shared slot configuration (e.g. theming), pass context via a React Context Provider inside the layout rather than prop-drilling through each slot page.
// app/dashboard/layout.tsx — full typed version
import React from 'react';
import { DashboardNav } from '@/components/DashboardNav';
interface DashboardLayoutProps {
children: React.ReactNode; // implicit slot
analytics: React.ReactNode; // @analytics slot
team: React.ReactNode; // @team slot
modal: React.ReactNode; // @modal slot
params: Promise<{ orgId: string }>;
}
export default async function DashboardLayout({
children,
analytics,
team,
modal,
params,
}: DashboardLayoutProps) {
const { orgId } = await params;
return (
<>
<DashboardNav orgId={orgId} />
<div className="grid grid-cols-3 gap-4 p-4">
<div className="col-span-2">{children}</div>
<div className="space-y-4">
{analytics}
{team}
</div>
</div>
{modal}
</>
);
}Knowledge Check: When Is default.tsx Required?
Test your understanding of the default.tsx file in parallel routes.
Recap: Parallel Routes with Named Slots and Default Segments
Here is what you learned in this lesson:
- Parallel Routes let you render multiple independent route trees in the same layout simultaneously, each in its own
@slotfolder. - Named slots use the
@folderNameconvention and become props (React.ReactNode) on the parentlayout.tsx. - Each slot is truly independent: it can have its own
loading.tsx,error.tsx, nested routes, and data fetching without blocking siblings. default.tsxis the critical fallback file. Without it, soft navigation to a URL that does not match a slot produces a 404. Place adefault.tsxin every slot — including the implicitchildrenslot — to prevent this.- Common patterns include dashboard multi-panels (analytics + team side by side) and modal-alongside-page where the
@modalslot'sdefault.tsxreturnsnull. - Keep layout props strongly typed with a TypeScript interface as the slot count grows.
With parallel routes and default.tsx in place, your layouts become resilient, independently streaming, and far easier to reason about than a single monolithic route tree.
常见问题解答
「带命名插槽与默认片段的并行路由」课时是免费的吗?
是的 — 「带命名插槽与默认片段的并行路由」的完整文本可在网页上免费阅读。要进行交互式练习(内置代码编辑器和全天候 AI 导师)并解锁 Next.js 15 Fullstack (App Router + Server Actions) 课程的其余内容,请升级到 CoddyKit PRO。 Next.js 15 Fullstack (App Router + Server Actions) 课程共包含 4 节课。
「带命名插槽与默认片段的并行路由」这节课中我会学到什么?
使用 @slot 约定和 default.tsx,同时渲染多个相互独立的路由树。 你通过在浏览器中直接运行的动手代码来练习 Next.js 15 Fullstack (App Router + Server Actions),全天候 AI 导师会在你学习这节课的过程中回答你的问题。
学习 Next.js 15 Fullstack (App Router + Server Actions) 需要有经验吗?
无需任何先前经验。CoddyKit 上的 Next.js 15 Fullstack (App Router + Server Actions) 课程适合初学者到高级学习者,你可以从这里开始或从头开始,按照自己的节奏学习。 这是第 1 节课,共 4 节。
「带命名插槽与默认片段的并行路由」课时需要多长时间?
大多数 CoddyKit 课程大约需要 5–10 分钟。每节课都很精短且互动,所以你能稳步进步,并在网页和应用中从离开的地方继续。
我能在这节 Next.js 15 Fullstack (App Router + Server Actions) 课中编写并运行代码吗?
能。每节 Next.js 15 Fullstack (App Router + Server Actions) 课都包含内置代码编辑器,你可以在浏览器中直接编写并运行真实代码,并获得即时 AI 反馈 — 无需本地设置。
此课程中的所有课时
- 带命名插槽与默认片段的并行路由
- 用于共享模态体验的拦截路由
- 用于仪表板与选项卡的条件插槽渲染
- 使用软导航构建照片模态流程