implementing-parallel-routes
npx skills add https://github.com/djankies/claude-configs --skill implementing-parallel-routes
Agent 安装分布
Skill 文档
Parallel Routes in Next.js 16
Concept
Parallel routes allow you to simultaneously render multiple pages within the same layout. Each route is defined in a “slot” using the @folder naming convention.
Key characteristics:
- Slots are defined with
@prefix (e.g.,@team,@analytics) - Each slot is passed as a prop to the parent layout
- Slots render independently and can have their own loading/error states
- Navigation within one slot doesn’t affect other slots
Basic Structure
app/
âââ @team/
â âââ page.tsx
â âââ default.tsx
âââ @analytics/
â âââ page.tsx
â âââ default.tsx
âââ layout.tsx
âââ page.tsx
The layout receives slots as props:
export default function Layout({
children,
team,
analytics,
}: {
children: React.ReactNode
team: React.ReactNode
analytics: React.ReactNode
}) {
return (
<div>
<div>{children}</div>
<div className="grid grid-cols-2 gap-4">
<div>{team}</div>
<div>{analytics}</div>
</div>
</div>
)
}
default.tsx Requirement
CRITICAL: Every slot MUST have a default.tsx file.
When navigating to a route that doesn’t match the current slot, Next.js will render the default.tsx fallback. Without it, you’ll get a 404 error.
export default function Default() {
return null
}
Common error:
Error: The default export is not a React Component in route: /@team
Solution: Add default.tsx to every @slot directory.
Navigation Behavior
Parallel routes handle navigation differently than normal routes:
Soft Navigation
When navigating using <Link> or router.push():
- Active slots maintain their current state
- Only the children segment updates
- Slots remain “mounted”
Hard Navigation
When navigating via browser refresh or direct URL entry:
- All slots reset to their default state
default.tsxfiles render for unmatched routes- Each slot independently resolves its route
Example scenario:
app/
âââ @modal/
â âââ login/
â â âââ page.tsx
â âââ default.tsx
âââ layout.tsx
âââ page.tsx
- User visits
/â@modalrendersdefault.tsx - User clicks link to
/loginâ@modalrenderslogin/page.tsx - User refreshes page â URL changes back to
/,@modalrendersdefault.tsx
This is why intercepting routes typically use parallel routes for modals.
Slot Matching
Slots match based on the current URL segment:
app/
âââ dashboard/
â âââ @sidebar/
â â âââ settings/
â â â âââ page.tsx
â â âââ default.tsx
â âââ settings/
â â âââ page.tsx
â âââ layout.tsx
âââ page.tsx
At /dashboard/settings:
dashboard/settings/page.tsxrenders inchildrendashboard/@sidebar/settings/page.tsxrenders insidebarslot- If
@sidebar/settings/page.tsxdoesn’t exist,@sidebar/default.tsxrenders
Conditional Rendering
You can conditionally render slots:
export default function Layout({
children,
modal,
auth,
}: {
children: React.ReactNode
modal: React.ReactNode
auth: React.ReactNode
}) {
const session = await getSession()
return (
<div>
{children}
{modal}
{!session && auth}
</div>
)
}
Common Gotchas
1. Missing default.tsx
Problem: 404 errors when navigating
Solution: Add default.tsx to every slot directory
2. Slot not rendering
Problem: Slot prop is undefined in layout
Solution: Check slot name matches @folder name (case-sensitive)
3. Unexpected resets
Problem: Slot resets to default on navigation
Solution: Ensure target route exists in slot, or use default.tsx intentionally
4. Nested layouts
Problem: Slots not accessible in nested layouts Solution: Slots only pass to immediate parent layout, not descendants
5. Route groups with slots
Problem: Confusion about where to place @slot folders
Solution: Place slots at the same level as the layout that consumes them
app/
âââ (marketing)/
â âââ @hero/
â â âââ page.tsx
â âââ layout.tsx
â âââ page.tsx
Practical Example: Modal Pattern
app/
âââ @modal/
â âââ (.)photo/
â â âââ [id]/
â â âââ page.tsx
â âââ default.tsx
âââ photo/
â âââ [id]/
â âââ page.tsx
âââ layout.tsx
âââ page.tsx
Layout with modal slot:
export default function Layout({
children,
modal,
}: {
children: React.ReactNode
modal: React.ReactNode
}) {
return (
<>
{children}
{modal}
</>
)
}
Modal slot default:
export default function Default() {
return null
}
Intercepted route renders in modal:
export default function PhotoModal({ params }: { params: { id: string } }) {
return (
<dialog open>
<img src={`/photos/${params.id}.jpg`} />
</dialog>
)
}
Direct route renders full page:
export default function PhotoPage({ params }: { params: { id: string } }) {
return (
<main>
<img src={`/photos/${params.id}.jpg`} />
</main>
)
}
When to Use Parallel Routes
Good use cases:
- Dashboard layouts with independent panels
- Modal/drawer patterns with intercepting routes
- Split views with independent navigation
- A/B testing different UI sections
- Conditional sidebar/navigation rendering
Avoid when:
- Simple component composition suffices
- You need parent-child data flow
- Navigation should be tightly coupled
- You’re just trying to organize files (use route groups instead)
References
- Next.js Docs: https://nextjs.org/docs/app/building-your-application/routing/parallel-routes
- Intercepting Routes: nextjs-16 skill
ROUTING-intercepting-routes - Route Groups: nextjs-16 skill
ROUTING-route-groups