เส้นทางแบบขนานพร้อมสล็อตที่มีชื่อและเซกเมนต์เริ่มต้น
เรนเดอร์ต้นไม้เส้นทางอิสระหลายต้นพร้อมกันด้วยรูปแบบ @slot และ default.tsx
เส้นทางแบบขนานพร้อมสล็อตที่มีชื่อและเซกเมนต์เริ่มต้น เป็นบทเรียน Next.js 15 Fullstack (App Router + Server Actions) ฟรีบน CoddyKit นี่คือบทเรียนที่ 1 จากทั้งหมด 4 บทเรียน คุณสามารถอ่านบทเรียนทั้งหมดด้านล่างฟรี — จากนั้นลองปฏิบัติด้วยตัวคุณเองในเบราว์เซอร์พร้อมตัวแก้ไขโค้ดในตัวและติวเตอร์ AI ตลอด 24/7 บทเรียนนี้เป็นส่วนหนึ่งของเส้นทางการเรียน 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.
เรียนรู้ TypeScript ด้วย AI tutor — ฟรี
เขียนและเรียกใช้โค้ดจริงในเบราว์เซอร์ของคุณ รับความช่วยเหลือทันทีจาก AI tutor 24/7 และเรียนรู้ต่อจากที่คุณหยุดบนเว็บหรือในแอป
- คอร์ส
- 22
- บทเรียน
- 88
คำถามที่พบบ่อย
บทเรียน “เส้นทางแบบขนานพร้อมสล็อตที่มีชื่อและเซกเมนต์เริ่มต้น” ฟรีหรือไม่
ใช่ — ข้อความเต็มของ “เส้นทางแบบขนานพร้อมสล็อตที่มีชื่อและเซกเมนต์เริ่มต้น” ฟรีให้อ่านที่นี่บนเว็บ เพื่อปฏิบัติแบบโต้ตอบ (ตัวแก้ไขโค้ดในตัวและติวเตอร์ AI ตลอด 24/7) และปลดล็อคส่วนที่เหลือของคอร์ส 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 ตลอด 24/7 ตอบคำถามของคุณขณะที่คุณไปผ่านบทเรียน
คุณต้องมีประสบการณ์ก่อนที่จะเริ่มเรียน Next.js 15 Fullstack (App Router + Server Actions) หรือไม่
ไม่จำเป็นต้องมีประสบการณ์มาก่อน Next.js 15 Fullstack (App Router + Server Actions) บน CoddyKit ออกแบบมาสำหรับผู้เริ่มต้นไปจนถึงผู้เรียนขั้นสูง คุณสามารถเริ่มต้นที่นี่หรือเริ่มจากตัวแรกและเรียนด้วยความเร็วของคุณเอง นี่คือบทเรียนที่ 1 จากทั้งหมด 4 บทเรียน
บทเรียน “เส้นทางแบบขนานพร้อมสล็อตที่มีชื่อและเซกเมนต์เริ่มต้น” ใช้เวลานานแค่ไหน
บทเรียน CoddyKit ส่วนใหญ่ใช้เวลาประมาณ 5–10 นาที แต่ละบทเรียนจึงสั้นและเป็นแบบโต้ตอบ คุณสามารถก้าวหน้าอย่างต่อเนื่องและกลับมาเรียนต่อจากตรงที่เพิ่งหยุดบนเว็บและแอปได้เลย
ฉันเขียนและรันโค้ดในบทเรียน Next.js 15 Fullstack (App Router + Server Actions) นี้ได้ไหม
ได้ บทเรียน Next.js 15 Fullstack (App Router + Server Actions) ทุกบทมีตัวแก้ไขโค้ดในตัว คุณจึงเขียนและรันโค้ดจริงได้เลยในเบราว์เซอร์ และได้รับข้อเสนอแนะจาก AI ในทันที — ไม่ต้องติดตั้งในเครื่องของคุณ
บทเรียนทั้งหมดในหลักสูตรนี้
- เส้นทางแบบขนานพร้อมสล็อตที่มีชื่อและเซกเมนต์เริ่มต้น
- เส้นทางแทรกเพื่อประสบการณ์โมดัลร่วมกัน
- การเรนเดอร์สล็อตแบบมีเงื่อนไขสำหรับแดชบอร์ดและแท็บ
- การสร้างขั้นตอนโมดัลรูปภาพด้วยการนำทางแบบนุ่มนวล