` with dynamic iteration
diff --git a/docs/rfcs/parallel-route-slots/examples/component-routes/routes/dashboard.@activity.tsx b/docs/rfcs/parallel-route-slots/examples/component-routes/routes/dashboard.@activity.tsx
new file mode 100644
index 0000000000..fdc86eeea1
--- /dev/null
+++ b/docs/rfcs/parallel-route-slots/examples/component-routes/routes/dashboard.@activity.tsx
@@ -0,0 +1,62 @@
+// @ts-nocheck
+// Example only - this is a conceptual demonstration
+
+import { createSlotRoute } from '@tanstack/react-router'
+
+export const Route = createSlotRoute({
+ path: '/',
+ // Slots render by default - no opt-in needed!
+ // Static data for filtering/grouping in parent (type-safe via module declaration)
+ staticData: {
+ area: 'main',
+ priority: 1,
+ title: 'Recent Activity',
+ collapsible: true,
+ },
+ loader: async () => {
+ const activities = await fetchRecentActivity()
+ return { activities }
+ },
+ component: ActivityWidget,
+})
+
+function ActivityWidget() {
+ const { activities } = Route.useLoaderData()
+
+ return (
+
+ )
+}
+
+async function fetchRecentActivity() {
+ return [
+ {
+ id: '1',
+ user: { name: 'Alice', avatar: '/a.jpg' },
+ action: 'created a new project',
+ timestamp: '5m ago',
+ },
+ {
+ id: '2',
+ user: { name: 'Bob', avatar: '/b.jpg' },
+ action: 'completed a task',
+ timestamp: '12m ago',
+ },
+ {
+ id: '3',
+ user: { name: 'Carol', avatar: '/c.jpg' },
+ action: 'left a comment',
+ timestamp: '1h ago',
+ },
+ ]
+}
diff --git a/docs/rfcs/parallel-route-slots/examples/component-routes/routes/dashboard.@adminPanel.tsx b/docs/rfcs/parallel-route-slots/examples/component-routes/routes/dashboard.@adminPanel.tsx
new file mode 100644
index 0000000000..8635548c92
--- /dev/null
+++ b/docs/rfcs/parallel-route-slots/examples/component-routes/routes/dashboard.@adminPanel.tsx
@@ -0,0 +1,59 @@
+// @ts-nocheck
+// Example only - this is a conceptual demonstration
+
+import { createSlotRoute } from '@tanstack/react-router'
+
+export const Route = createSlotRoute({
+ path: '/',
+ staticData: {
+ area: 'main',
+ priority: 10, // render last in main area
+ title: 'Admin Panel',
+ collapsible: false,
+ },
+ // Opt-out: only render for admin users
+ enabled: ({ context }) => {
+ return context.user.role === 'admin'
+ },
+ loader: async () => {
+ const stats = await fetchAdminStats()
+ return { stats }
+ },
+ component: AdminPanelWidget,
+})
+
+function AdminPanelWidget() {
+ const { stats } = Route.useLoaderData()
+
+ return (
+
+
+
+ {stats.pendingApprovals}
+ Pending Approvals
+
+
+ {stats.flaggedContent}
+ Flagged Content
+
+
+ {stats.activeUsers}
+ Active Users
+
+
+
+
+
+
+
+
+ )
+}
+
+async function fetchAdminStats() {
+ return {
+ pendingApprovals: 12,
+ flaggedContent: 3,
+ activeUsers: 847,
+ }
+}
diff --git a/docs/rfcs/parallel-route-slots/examples/component-routes/routes/dashboard.@header.tsx b/docs/rfcs/parallel-route-slots/examples/component-routes/routes/dashboard.@header.tsx
new file mode 100644
index 0000000000..dd176a17c1
--- /dev/null
+++ b/docs/rfcs/parallel-route-slots/examples/component-routes/routes/dashboard.@header.tsx
@@ -0,0 +1,43 @@
+// @ts-nocheck
+// Example only - this is a conceptual demonstration
+
+import { createSlotRoute, Link } from '@tanstack/react-router'
+
+// Header is explicitly placed, not part of iteration
+// No staticData.area needed since it's rendered via
+export const Route = createSlotRoute({
+ path: '/',
+ // No defaultOpen needed - slots render by default!
+ loader: async ({ context }) => {
+ const user = context.user
+ const notifications = await fetchUnreadCount(user.id)
+ return { user, notifications }
+ },
+ component: DashboardHeader,
+})
+
+function DashboardHeader() {
+ const { user, notifications } = Route.useLoaderData()
+
+ return (
+
+
Dashboard
+
+
+
+ {user.name}
+
+
+ )
+}
+
+async function fetchUnreadCount(userId: string) {
+ return 5
+}
diff --git a/docs/rfcs/parallel-route-slots/examples/component-routes/routes/dashboard.@metrics.tsx b/docs/rfcs/parallel-route-slots/examples/component-routes/routes/dashboard.@metrics.tsx
new file mode 100644
index 0000000000..26d4bd3260
--- /dev/null
+++ b/docs/rfcs/parallel-route-slots/examples/component-routes/routes/dashboard.@metrics.tsx
@@ -0,0 +1,48 @@
+// @ts-nocheck
+// Example only - this is a conceptual demonstration
+
+import { createSlotRoute } from '@tanstack/react-router'
+
+export const Route = createSlotRoute({
+ path: '/',
+ staticData: {
+ area: 'main',
+ priority: 2,
+ title: 'Key Metrics',
+ collapsible: true,
+ },
+ loader: async () => {
+ const metrics = await fetchMetrics()
+ return { metrics }
+ },
+ component: MetricsWidget,
+})
+
+function MetricsWidget() {
+ const { metrics } = Route.useLoaderData()
+
+ return (
+
+ {metrics.map((metric) => (
+
+ {metric.value}
+ {metric.label}
+ 0 ? 'positive' : 'negative'}`}
+ >
+ {metric.change > 0 ? '↑' : '↓'} {Math.abs(metric.change)}%
+
+
+ ))}
+
+ )
+}
+
+async function fetchMetrics() {
+ return [
+ { label: 'Revenue', value: '$12,345', change: 12 },
+ { label: 'Users', value: '1,234', change: 8 },
+ { label: 'Orders', value: '567', change: -3 },
+ { label: 'Conversion', value: '4.2%', change: 5 },
+ ]
+}
diff --git a/docs/rfcs/parallel-route-slots/examples/component-routes/routes/dashboard.@notifications.tsx b/docs/rfcs/parallel-route-slots/examples/component-routes/routes/dashboard.@notifications.tsx
new file mode 100644
index 0000000000..9ef3dc920e
--- /dev/null
+++ b/docs/rfcs/parallel-route-slots/examples/component-routes/routes/dashboard.@notifications.tsx
@@ -0,0 +1,74 @@
+// @ts-nocheck
+// Example only - this is a conceptual demonstration
+
+import { createSlotRoute } from '@tanstack/react-router'
+
+export const Route = createSlotRoute({
+ path: '/',
+ staticData: {
+ area: 'main',
+ priority: 3,
+ title: 'Notifications',
+ collapsible: true,
+ },
+ // Opt-out: disable if user turned off in preferences
+ enabled: ({ context }) => {
+ return context.user.preferences?.showNotificationsWidget !== false
+ },
+ loader: async () => {
+ const notifications = await fetchNotifications()
+ return { notifications }
+ },
+ component: NotificationsWidget,
+})
+
+function NotificationsWidget() {
+ const { notifications } = Route.useLoaderData()
+
+ if (notifications.length === 0) {
+ return No new notifications
+ }
+
+ return (
+
+ {notifications.map((notif) => (
+ -
+ {notif.icon}
+
+
{notif.message}
+
+
+
+ ))}
+
+ )
+}
+
+async function fetchNotifications() {
+ return [
+ {
+ id: '1',
+ icon: '📬',
+ message: 'New message from Alice',
+ time: '2m ago',
+ read: false,
+ },
+ {
+ id: '2',
+ icon: '✅',
+ message: 'Task "Update docs" completed',
+ time: '1h ago',
+ read: false,
+ },
+ {
+ id: '3',
+ icon: '🎉',
+ message: 'You earned a new badge!',
+ time: '3h ago',
+ read: true,
+ },
+ ]
+}
diff --git a/docs/rfcs/parallel-route-slots/examples/component-routes/routes/dashboard.@quickActions.tsx b/docs/rfcs/parallel-route-slots/examples/component-routes/routes/dashboard.@quickActions.tsx
new file mode 100644
index 0000000000..6ff71f868d
--- /dev/null
+++ b/docs/rfcs/parallel-route-slots/examples/component-routes/routes/dashboard.@quickActions.tsx
@@ -0,0 +1,40 @@
+// @ts-nocheck
+// Example only - this is a conceptual demonstration
+
+import { createSlotRoute, Link } from '@tanstack/react-router'
+
+export const Route = createSlotRoute({
+ path: '/',
+ staticData: {
+ area: 'sidebar',
+ priority: 1,
+ },
+ // No loader needed - static content
+ component: QuickActionsWidget,
+})
+
+function QuickActionsWidget() {
+ return (
+
+
Quick Actions
+
+
+
+
+
+
+
+ )
+}
diff --git a/docs/rfcs/parallel-route-slots/examples/component-routes/routes/dashboard.@userCard.tsx b/docs/rfcs/parallel-route-slots/examples/component-routes/routes/dashboard.@userCard.tsx
new file mode 100644
index 0000000000..cf49791ec6
--- /dev/null
+++ b/docs/rfcs/parallel-route-slots/examples/component-routes/routes/dashboard.@userCard.tsx
@@ -0,0 +1,56 @@
+// @ts-nocheck
+// Example only - this is a conceptual demonstration
+
+import { createSlotRoute } from '@tanstack/react-router'
+
+export const Route = createSlotRoute({
+ path: '/',
+ staticData: {
+ area: 'sidebar',
+ priority: 2,
+ },
+ loader: async ({ context }) => {
+ const user = await fetchUserProfile(context.user.id)
+ return { user }
+ },
+ component: UserCardWidget,
+})
+
+function UserCardWidget() {
+ const { user } = Route.useLoaderData()
+
+ return (
+
+

+
{user.name}
+
{user.role}
+
+
+ {user.projects}
+ Projects
+
+
+ {user.tasks}
+ Tasks
+
+
+ {/* Open modal from dashboard - Route.Link has implicit from */}
+
+ View Profile
+
+
+ )
+}
+
+async function fetchUserProfile(userId: string) {
+ return {
+ id: userId,
+ name: 'Jane Doe',
+ role: 'Product Manager',
+ avatar: '/avatars/jane.jpg',
+ projects: 8,
+ tasks: 24,
+ }
+}
diff --git a/docs/rfcs/parallel-route-slots/examples/component-routes/routes/dashboard.tsx b/docs/rfcs/parallel-route-slots/examples/component-routes/routes/dashboard.tsx
new file mode 100644
index 0000000000..331f1ddff5
--- /dev/null
+++ b/docs/rfcs/parallel-route-slots/examples/component-routes/routes/dashboard.tsx
@@ -0,0 +1,81 @@
+// @ts-nocheck
+// Example only - this is a conceptual demonstration
+
+import { createFileRoute, Outlet } from '@tanstack/react-router'
+
+// Slots are NOT declared here - they're discovered from dashboard.@*.tsx files
+// after composition. Route.Outlet and Route.Slots gain type-safe access automatically.
+export const Route = createFileRoute('/dashboard')({
+ component: Dashboard,
+})
+
+function Dashboard() {
+ return (
+
+ {/* Explicitly placed header slot */}
+
+
+
+ {/* Use Route.Slots to dynamically render widgets */}
+
+ {(slots) => {
+ // Filter and group slots by their staticData.area
+ const mainSlots = slots
+ .filter((s) => s.staticData?.area === 'main')
+ .sort(
+ (a, b) =>
+ (a.staticData?.priority ?? 99) -
+ (b.staticData?.priority ?? 99),
+ )
+
+ const sidebarSlots = slots
+ .filter((s) => s.staticData?.area === 'sidebar')
+ .sort(
+ (a, b) =>
+ (a.staticData?.priority ?? 99) -
+ (b.staticData?.priority ?? 99),
+ )
+
+ return (
+ <>
+ {/* Main content area with widget grid */}
+
+ {/* Regular child routes */}
+
+
+ {/* Widget grid */}
+
+ {mainSlots.map((slot) => (
+
+
+
{slot.staticData?.title || slot.name}
+ {slot.staticData?.collapsible && (
+
+ )}
+
+
+
+
+
+ ))}
+
+
+
+ {/* Sidebar with additional widgets */}
+
+ >
+ )
+ }}
+
+
+
+ )
+}
diff --git a/docs/rfcs/parallel-route-slots/examples/dashboard-widgets/README.md b/docs/rfcs/parallel-route-slots/examples/dashboard-widgets/README.md
new file mode 100644
index 0000000000..bc85bbb924
--- /dev/null
+++ b/docs/rfcs/parallel-route-slots/examples/dashboard-widgets/README.md
@@ -0,0 +1,37 @@
+# Dashboard Widgets
+
+Route-scoped slots for a dashboard with multiple independently-loading widgets. Each widget has its own loader that runs in parallel.
+
+## URL Examples
+
+```
+/dashboard # all widgets render at root (clean!)
+/dashboard?@activity=/recent # activity navigated to /recent
+/dashboard?@metrics=/revenue # metrics navigated to /revenue
+/dashboard?@quickActions=false # quick actions explicitly hidden
+```
+
+## File Structure
+
+```
+routes/
+├── __root.tsx
+├── dashboard.tsx # defines scoped slots: activity, metrics, quickActions
+├── dashboard.index.tsx # main dashboard content
+├── dashboard.@activity.tsx # activity widget root
+├── dashboard.@activity.index.tsx
+├── dashboard.@activity.recent.tsx
+├── dashboard.@metrics.tsx # metrics widget root
+├── dashboard.@metrics.index.tsx
+├── dashboard.@metrics.revenue.tsx
+├── dashboard.@metrics.users.tsx
+├── dashboard.@quickActions.tsx # quick actions widget (single route)
+└── index.tsx # home page
+```
+
+## Key Concepts
+
+- Slots defined on `dashboard.tsx` are only available within the dashboard
+- All widget loaders run in parallel with the dashboard loader
+- Each widget can have internal navigation (activity, metrics) or be a single view (quickActions)
+- Widgets can independently suspend/error without affecting others
diff --git a/docs/rfcs/parallel-route-slots/examples/dashboard-widgets/routes/__root.tsx b/docs/rfcs/parallel-route-slots/examples/dashboard-widgets/routes/__root.tsx
new file mode 100644
index 0000000000..df487c2677
--- /dev/null
+++ b/docs/rfcs/parallel-route-slots/examples/dashboard-widgets/routes/__root.tsx
@@ -0,0 +1,24 @@
+// @ts-nocheck
+// Example only - this is a conceptual demonstration
+
+import { createRootRoute, Outlet } from '@tanstack/react-router'
+
+export const Route = createRootRoute({
+ component: RootComponent,
+})
+
+function RootComponent() {
+ return (
+
+
+ Dashboard App
+
+
+
+
+
+
+
+
+ )
+}
diff --git a/docs/rfcs/parallel-route-slots/examples/dashboard-widgets/routes/dashboard.@activity.index.tsx b/docs/rfcs/parallel-route-slots/examples/dashboard-widgets/routes/dashboard.@activity.index.tsx
new file mode 100644
index 0000000000..fcff4ce9a4
--- /dev/null
+++ b/docs/rfcs/parallel-route-slots/examples/dashboard-widgets/routes/dashboard.@activity.index.tsx
@@ -0,0 +1,39 @@
+// @ts-nocheck
+// Example only - this is a conceptual demonstration
+
+import { createSlotRoute } from '@tanstack/react-router'
+
+// Activity index - shows all activity
+export const Route = createSlotRoute({
+ path: '/',
+ loader: async () => {
+ // This runs in PARALLEL with dashboard.loader and other widget loaders
+ const activities = await fetchAllActivities()
+ return { activities }
+ },
+ component: AllActivities,
+})
+
+function AllActivities() {
+ const { activities } = Route.useLoaderData()
+
+ return (
+
+ {activities.map((activity) => (
+ -
+ {activity.user}
+ {activity.action}
+
+
+ ))}
+
+ )
+}
+
+async function fetchAllActivities() {
+ return [
+ { id: '1', user: 'Alice', action: 'created a new project', time: '2m ago' },
+ { id: '2', user: 'Bob', action: 'commented on task', time: '5m ago' },
+ { id: '3', user: 'Carol', action: 'completed milestone', time: '1h ago' },
+ ]
+}
diff --git a/docs/rfcs/parallel-route-slots/examples/dashboard-widgets/routes/dashboard.@activity.tsx b/docs/rfcs/parallel-route-slots/examples/dashboard-widgets/routes/dashboard.@activity.tsx
new file mode 100644
index 0000000000..796c33fe01
--- /dev/null
+++ b/docs/rfcs/parallel-route-slots/examples/dashboard-widgets/routes/dashboard.@activity.tsx
@@ -0,0 +1,30 @@
+// @ts-nocheck
+// Example only - this is a conceptual demonstration
+
+import { createSlotRootRoute, Outlet, Link } from '@tanstack/react-router'
+
+// Activity widget root - wraps all activity views
+export const Route = createSlotRootRoute({
+ component: ActivityWidget,
+})
+
+function ActivityWidget() {
+ return (
+
+
+ Activity
+
+
+
+
+
+
+ )
+}
diff --git a/docs/rfcs/parallel-route-slots/examples/dashboard-widgets/routes/dashboard.@metrics.index.tsx b/docs/rfcs/parallel-route-slots/examples/dashboard-widgets/routes/dashboard.@metrics.index.tsx
new file mode 100644
index 0000000000..7fe9a2c8f7
--- /dev/null
+++ b/docs/rfcs/parallel-route-slots/examples/dashboard-widgets/routes/dashboard.@metrics.index.tsx
@@ -0,0 +1,39 @@
+// @ts-nocheck
+// Example only - this is a conceptual demonstration
+
+import { createSlotRoute } from '@tanstack/react-router'
+
+// Metrics overview
+export const Route = createSlotRoute({
+ path: '/',
+ loader: async () => {
+ const overview = await fetchMetricsOverview()
+ return { overview }
+ },
+ component: MetricsOverview,
+})
+
+function MetricsOverview() {
+ const { overview } = Route.useLoaderData()
+
+ return (
+
+
+ {overview.revenue}
+ Revenue
+
+
+ {overview.users}
+ Users
+
+
+ {overview.orders}
+ Orders
+
+
+ )
+}
+
+async function fetchMetricsOverview() {
+ return { revenue: '$12,345', users: '1,234', orders: '567' }
+}
diff --git a/docs/rfcs/parallel-route-slots/examples/dashboard-widgets/routes/dashboard.@metrics.tsx b/docs/rfcs/parallel-route-slots/examples/dashboard-widgets/routes/dashboard.@metrics.tsx
new file mode 100644
index 0000000000..05dbf9f535
--- /dev/null
+++ b/docs/rfcs/parallel-route-slots/examples/dashboard-widgets/routes/dashboard.@metrics.tsx
@@ -0,0 +1,33 @@
+// @ts-nocheck
+// Example only - this is a conceptual demonstration
+
+import { createSlotRootRoute, Outlet, Link } from '@tanstack/react-router'
+
+// Metrics widget root
+export const Route = createSlotRootRoute({
+ component: MetricsWidget,
+})
+
+function MetricsWidget() {
+ return (
+
+
+ Metrics
+
+
+
+
+
+
+ )
+}
diff --git a/docs/rfcs/parallel-route-slots/examples/dashboard-widgets/routes/dashboard.@quickActions.tsx b/docs/rfcs/parallel-route-slots/examples/dashboard-widgets/routes/dashboard.@quickActions.tsx
new file mode 100644
index 0000000000..fa015bfe8b
--- /dev/null
+++ b/docs/rfcs/parallel-route-slots/examples/dashboard-widgets/routes/dashboard.@quickActions.tsx
@@ -0,0 +1,43 @@
+// @ts-nocheck
+// Example only - this is a conceptual demonstration
+
+import { createSlotRootRoute, Link } from '@tanstack/react-router'
+
+// Quick actions widget - single route, no internal navigation
+export const Route = createSlotRootRoute({
+ loader: async () => {
+ const actions = await fetchQuickActions()
+ return { actions }
+ },
+ component: QuickActionsWidget,
+})
+
+function QuickActionsWidget() {
+ const { actions } = Route.useLoaderData()
+
+ return (
+
+
+
+
+ {actions.map((action) => (
+
+ ))}
+
+
+
+ )
+}
+
+async function fetchQuickActions() {
+ return [
+ { id: '1', icon: '➕', label: 'New Project' },
+ { id: '2', icon: '👤', label: 'Invite User' },
+ { id: '3', icon: '📊', label: 'Generate Report' },
+ ]
+}
diff --git a/docs/rfcs/parallel-route-slots/examples/dashboard-widgets/routes/dashboard.tsx b/docs/rfcs/parallel-route-slots/examples/dashboard-widgets/routes/dashboard.tsx
new file mode 100644
index 0000000000..e8640a367c
--- /dev/null
+++ b/docs/rfcs/parallel-route-slots/examples/dashboard-widgets/routes/dashboard.tsx
@@ -0,0 +1,47 @@
+// @ts-nocheck
+// Example only - this is a conceptual demonstration
+
+import { createFileRoute, Outlet } from '@tanstack/react-router'
+
+// Slots are NOT declared here - they're discovered from dashboard.@*.tsx files
+// after composition. Route.Outlet gains type-safe slot prop automatically.
+export const Route = createFileRoute('/dashboard')({
+ loader: async () => {
+ // Dashboard-level data (user info, permissions, etc.)
+ const user = await fetchCurrentUser()
+ return { user }
+ },
+ component: Dashboard,
+})
+
+function Dashboard() {
+ const { user } = Route.useLoaderData()
+
+ return (
+
+
Welcome back, {user.name}
+
+
+ {/* Left column - Activity feed */}
+
+
+ {/* Main content area */}
+
+
+
+
+ {/* Right column - Metrics and Quick Actions */}
+
+
+
+ )
+}
+
+async function fetchCurrentUser() {
+ return { id: '1', name: 'Jane' }
+}
diff --git a/docs/rfcs/parallel-route-slots/examples/modal-with-navigation/README.md b/docs/rfcs/parallel-route-slots/examples/modal-with-navigation/README.md
new file mode 100644
index 0000000000..ef1e55e303
--- /dev/null
+++ b/docs/rfcs/parallel-route-slots/examples/modal-with-navigation/README.md
@@ -0,0 +1,29 @@
+# Modal with Navigation
+
+A global modal slot that can be opened from anywhere in the app. The modal has its own internal navigation (user profiles with tabs, settings pages, etc.).
+
+## URL Examples
+
+```
+/products # modal closed
+/products?@modal=/ # modal open at index
+/products?@modal=/users/123 # viewing user 123
+/products?@modal=/users/123&@modal.tab=activity # user 123, activity tab
+/products?@modal=/settings # settings view in modal
+/checkout?@modal=/users/123 # modal persists across main navigation
+```
+
+## File Structure
+
+```
+routes/
+├── __root.tsx # defines modal slot, renders Outlet with slot prop
+├── @modal.tsx # modal wrapper (backdrop, close button, animation)
+├── @modal.index.tsx # modal landing/index view
+├── @modal.users.$id.tsx # user profile view
+├── @modal.settings.tsx # settings view
+├── index.tsx # home page
+├── products.tsx # products layout
+├── products.index.tsx # products list
+└── products.$id.tsx # product detail
+```
diff --git a/docs/rfcs/parallel-route-slots/examples/modal-with-navigation/routes/@modal.index.tsx b/docs/rfcs/parallel-route-slots/examples/modal-with-navigation/routes/@modal.index.tsx
new file mode 100644
index 0000000000..e8bb4bf7da
--- /dev/null
+++ b/docs/rfcs/parallel-route-slots/examples/modal-with-navigation/routes/@modal.index.tsx
@@ -0,0 +1,27 @@
+// @ts-nocheck
+// Example only - this is a conceptual demonstration
+
+import { createSlotRoute } from '@tanstack/react-router'
+
+// Modal index - shown when @modal=/
+export const Route = createSlotRoute({
+ path: '/',
+ component: ModalIndex,
+})
+
+function ModalIndex() {
+ return (
+
+
Quick Actions
+ {/* Navigation within the modal - Route.Link has implicit from */}
+
+
+ )
+}
diff --git a/docs/rfcs/parallel-route-slots/examples/modal-with-navigation/routes/@modal.settings.tsx b/docs/rfcs/parallel-route-slots/examples/modal-with-navigation/routes/@modal.settings.tsx
new file mode 100644
index 0000000000..2aff05a577
--- /dev/null
+++ b/docs/rfcs/parallel-route-slots/examples/modal-with-navigation/routes/@modal.settings.tsx
@@ -0,0 +1,73 @@
+// @ts-nocheck
+// Example only - this is a conceptual demonstration
+
+import { createSlotRoute } from '@tanstack/react-router'
+
+// Settings modal - shown when @modal=/settings
+export const Route = createSlotRoute({
+ path: '/settings',
+ loader: async () => {
+ const settings = await fetchSettings()
+ return { settings }
+ },
+ component: SettingsModal,
+})
+
+function SettingsModal() {
+ const { settings } = Route.useLoaderData()
+ const navigate = Route.useNavigate()
+
+ const handleClose = () => {
+ navigate({ slots: { modal: null } })
+ }
+
+ const handleSave = async (formData: FormData) => {
+ await saveSettings(formData)
+ handleClose()
+ }
+
+ return (
+
+ )
+}
+
+// Placeholder functions
+async function fetchSettings() {
+ return { theme: 'system', notifications: true }
+}
+async function saveSettings(data: FormData) {
+ console.log('Saving settings', Object.fromEntries(data))
+}
diff --git a/docs/rfcs/parallel-route-slots/examples/modal-with-navigation/routes/@modal.tsx b/docs/rfcs/parallel-route-slots/examples/modal-with-navigation/routes/@modal.tsx
new file mode 100644
index 0000000000..382c23c7b8
--- /dev/null
+++ b/docs/rfcs/parallel-route-slots/examples/modal-with-navigation/routes/@modal.tsx
@@ -0,0 +1,30 @@
+// @ts-nocheck
+// Example only - this is a conceptual demonstration
+
+import { createSlotRootRoute, Outlet } from '@tanstack/react-router'
+
+// This is the slot's root route - wraps all modal content
+export const Route = createSlotRootRoute({
+ component: ModalWrapper,
+})
+
+function ModalWrapper() {
+ const navigate = Route.useNavigate()
+
+ const handleClose = () => {
+ navigate({ slots: { modal: null } })
+ }
+
+ return (
+
+
e.stopPropagation()}>
+
+
+ {/* Render the matched modal route */}
+
+
+
+ )
+}
diff --git a/docs/rfcs/parallel-route-slots/examples/modal-with-navigation/routes/@modal.users.$id.tsx b/docs/rfcs/parallel-route-slots/examples/modal-with-navigation/routes/@modal.users.$id.tsx
new file mode 100644
index 0000000000..a5f6493b89
--- /dev/null
+++ b/docs/rfcs/parallel-route-slots/examples/modal-with-navigation/routes/@modal.users.$id.tsx
@@ -0,0 +1,84 @@
+// @ts-nocheck
+// Example only - this is a conceptual demonstration
+
+import { createSlotRoute, Link } from '@tanstack/react-router'
+import { z } from 'zod'
+
+// User profile modal - shown when @modal=/users/123
+export const Route = createSlotRoute({
+ path: '/users/$id',
+ validateSearch: z.object({
+ tab: z.enum(['profile', 'activity', 'settings']).default('profile'),
+ }),
+ loader: async ({ params }) => {
+ const user = await fetchUser(params.id)
+ return { user }
+ },
+ component: UserModal,
+})
+
+function UserModal() {
+ const { user } = Route.useLoaderData()
+ const { tab } = Route.useSearch()
+ const params = Route.useParams()
+
+ return (
+
+
+
+ {user.name}
+
+
+ {/* Tab navigation - using fully qualified paths (simpler for single slot nav) */}
+
+
+ {/* Tab content */}
+
+ {tab === 'profile' && }
+ {tab === 'activity' && }
+ {tab === 'settings' && }
+
+
+ )
+}
+
+// Placeholder components
+function UserProfile({ user }) {
+ return Profile for {user.name}
+}
+function UserActivity({ user }) {
+ return Activity for {user.name}
+}
+function UserSettings({ user }) {
+ return Settings for {user.name}
+}
+
+// Placeholder fetch
+async function fetchUser(id: string) {
+ return { id, name: 'John Doe', avatar: '/avatars/john.jpg' }
+}
diff --git a/docs/rfcs/parallel-route-slots/examples/modal-with-navigation/routes/__root.tsx b/docs/rfcs/parallel-route-slots/examples/modal-with-navigation/routes/__root.tsx
new file mode 100644
index 0000000000..aedfc3cd9b
--- /dev/null
+++ b/docs/rfcs/parallel-route-slots/examples/modal-with-navigation/routes/__root.tsx
@@ -0,0 +1,27 @@
+// @ts-nocheck
+// Example only - this is a conceptual demonstration
+
+import { createRootRoute, Outlet } from '@tanstack/react-router'
+
+// Slots are NOT declared here - they're discovered from @modal.tsx files
+// after composition. Route.Outlet gains type-safe slot prop automatically.
+export const Route = createRootRoute({
+ component: RootComponent,
+})
+
+function RootComponent() {
+ return (
+
+
+ My App
+
+
+ {/* Main content */}
+
+
+ {/* Modal slot - rendered on top of everything */}
+
+
+
+ )
+}
diff --git a/docs/rfcs/parallel-route-slots/examples/nested-slots/README.md b/docs/rfcs/parallel-route-slots/examples/nested-slots/README.md
new file mode 100644
index 0000000000..2d14d30f06
--- /dev/null
+++ b/docs/rfcs/parallel-route-slots/examples/nested-slots/README.md
@@ -0,0 +1,34 @@
+# Nested Slots
+
+A modal that contains its own nested confirmation dialog slot. Demonstrates slots within slots.
+
+## URL Examples
+
+```
+/app # no modal
+/app?@modal=/settings # settings modal open
+/app?@modal=/settings&@modal@confirm # confirm dialog open at root
+/app?@modal=/settings&@modal@confirm=/discard # specific confirm action
+```
+
+## File Structure
+
+```
+routes/
+├── __root.tsx # defines global modal slot
+├── @modal.tsx # modal wrapper, defines nested confirm slot
+├── @modal.index.tsx
+├── @modal.settings.tsx
+├── @modal.@confirm.tsx # nested slot root (confirm dialog)
+├── @modal.@confirm.index.tsx
+├── @modal.@confirm.discard.tsx
+├── @modal.@confirm.delete.tsx
+└── index.tsx
+```
+
+## Key Concepts
+
+- `@modal.@confirm` is a slot within the modal slot
+- URL uses nested prefix: `@modal@confirm=/discard`
+- The modal can open confirmation dialogs without closing itself
+- Confirmation dialogs have their own routes for different actions
diff --git a/docs/rfcs/parallel-route-slots/examples/nested-slots/routes/@modal.@confirm.delete.tsx b/docs/rfcs/parallel-route-slots/examples/nested-slots/routes/@modal.@confirm.delete.tsx
new file mode 100644
index 0000000000..86f98477c4
--- /dev/null
+++ b/docs/rfcs/parallel-route-slots/examples/nested-slots/routes/@modal.@confirm.delete.tsx
@@ -0,0 +1,46 @@
+// @ts-nocheck
+// Example only - this is a conceptual demonstration
+
+import { createSlotRoute } from '@tanstack/react-router'
+
+// Confirm delete account dialog
+export const Route = createSlotRoute({
+ path: '/delete',
+ component: DeleteConfirm,
+})
+
+function DeleteConfirm() {
+ const navigate = Route.useNavigate()
+
+ const handleDelete = async () => {
+ await deleteAccount()
+ // Close the entire modal (and its nested slots)
+ navigate({ slots: { modal: null } })
+ // In reality you'd also redirect to a logged-out page
+ }
+
+ const handleCancel = () => {
+ // Close just the confirm dialog, keep modal open
+ navigate({ slots: { modal: { slots: { confirm: null } } } })
+ }
+
+ return (
+
+
Delete Account?
+
+ This action cannot be undone. All your data will be permanently deleted.
+
+
+
+
+
+
+
+ )
+}
+
+async function deleteAccount() {
+ console.log('Account deleted')
+}
diff --git a/docs/rfcs/parallel-route-slots/examples/nested-slots/routes/@modal.@confirm.discard.tsx b/docs/rfcs/parallel-route-slots/examples/nested-slots/routes/@modal.@confirm.discard.tsx
new file mode 100644
index 0000000000..be2ddb5683
--- /dev/null
+++ b/docs/rfcs/parallel-route-slots/examples/nested-slots/routes/@modal.@confirm.discard.tsx
@@ -0,0 +1,38 @@
+// @ts-nocheck
+// Example only - this is a conceptual demonstration
+
+import { createSlotRoute } from '@tanstack/react-router'
+
+// Confirm discard changes dialog
+export const Route = createSlotRoute({
+ path: '/discard',
+ component: DiscardConfirm,
+})
+
+function DiscardConfirm() {
+ const navigate = Route.useNavigate()
+
+ const handleDiscard = () => {
+ // Close the entire modal (and its nested slots)
+ navigate({ slots: { modal: null } })
+ }
+
+ const handleCancel = () => {
+ // Just close the confirm dialog, keep modal open
+ navigate({ slots: { modal: { slots: { confirm: null } } } })
+ }
+
+ return (
+
+
Discard changes?
+
You have unsaved changes. Are you sure you want to discard them?
+
+
+
+
+
+
+ )
+}
diff --git a/docs/rfcs/parallel-route-slots/examples/nested-slots/routes/@modal.@confirm.tsx b/docs/rfcs/parallel-route-slots/examples/nested-slots/routes/@modal.@confirm.tsx
new file mode 100644
index 0000000000..dec6bcee65
--- /dev/null
+++ b/docs/rfcs/parallel-route-slots/examples/nested-slots/routes/@modal.@confirm.tsx
@@ -0,0 +1,26 @@
+// @ts-nocheck
+// Example only - this is a conceptual demonstration
+
+import { createSlotRootRoute, Outlet } from '@tanstack/react-router'
+
+// Nested confirmation dialog slot
+export const Route = createSlotRootRoute({
+ component: ConfirmDialogWrapper,
+})
+
+function ConfirmDialogWrapper() {
+ const navigate = Route.useNavigate()
+
+ const handleClose = () => {
+ // Close just the confirm dialog, keep modal open
+ navigate({ slots: { modal: { slots: { confirm: null } } } })
+ }
+
+ return (
+
+
e.stopPropagation()}>
+
+
+
+ )
+}
diff --git a/docs/rfcs/parallel-route-slots/examples/nested-slots/routes/@modal.settings.tsx b/docs/rfcs/parallel-route-slots/examples/nested-slots/routes/@modal.settings.tsx
new file mode 100644
index 0000000000..597b9f828b
--- /dev/null
+++ b/docs/rfcs/parallel-route-slots/examples/nested-slots/routes/@modal.settings.tsx
@@ -0,0 +1,72 @@
+// @ts-nocheck
+// Example only - this is a conceptual demonstration
+
+import { createSlotRoute, Link } from '@tanstack/react-router'
+import { useState } from 'react'
+
+export const Route = createSlotRoute({
+ path: '/settings',
+ loader: async () => {
+ const settings = await fetchSettings()
+ return { settings }
+ },
+ component: SettingsModal,
+})
+
+function SettingsModal() {
+ const { settings } = Route.useLoaderData()
+ const [hasChanges, setHasChanges] = useState(false)
+ const navigate = Route.useNavigate()
+
+ const handleClose = () => {
+ if (hasChanges) {
+ // Open the nested confirm slot - fully qualified path
+ // Results in URL: ?@modal=/settings&@modal@confirm=/discard
+ navigate({ to: '/@modal/@confirm/discard' })
+ } else {
+ // Close modal - need slots object for null
+ navigate({ slots: { modal: null } })
+ }
+ }
+
+ return (
+
+
Settings
+
+
+
+
+
+
+
+ {/* Direct link to delete confirmation - fully qualified nested slot path */}
+
+ Delete Account
+
+
+
+ )
+}
+
+async function fetchSettings() {
+ return { theme: 'light', notifications: true }
+}
diff --git a/docs/rfcs/parallel-route-slots/examples/nested-slots/routes/@modal.tsx b/docs/rfcs/parallel-route-slots/examples/nested-slots/routes/@modal.tsx
new file mode 100644
index 0000000000..10e2eabea1
--- /dev/null
+++ b/docs/rfcs/parallel-route-slots/examples/nested-slots/routes/@modal.tsx
@@ -0,0 +1,34 @@
+// @ts-nocheck
+// Example only - this is a conceptual demonstration
+
+import { createSlotRootRoute, Outlet } from '@tanstack/react-router'
+
+// Nested slots are NOT declared here - they're discovered from @modal.@confirm.tsx
+// after composition. Route.Outlet gains type-safe slot prop automatically.
+export const Route = createSlotRootRoute({
+ component: ModalWrapper,
+})
+
+function ModalWrapper() {
+ const navigate = Route.useNavigate()
+
+ const handleClose = () => {
+ navigate({ slots: { modal: null } })
+ }
+
+ return (
+
+
e.stopPropagation()}>
+
+
+ {/* Modal content */}
+
+
+ {/* Nested confirmation dialog slot */}
+
+
+
+ )
+}
diff --git a/docs/rfcs/parallel-route-slots/examples/nested-slots/routes/__root.tsx b/docs/rfcs/parallel-route-slots/examples/nested-slots/routes/__root.tsx
new file mode 100644
index 0000000000..d77c123817
--- /dev/null
+++ b/docs/rfcs/parallel-route-slots/examples/nested-slots/routes/__root.tsx
@@ -0,0 +1,21 @@
+// @ts-nocheck
+// Example only - this is a conceptual demonstration
+
+import { createRootRoute, Outlet } from '@tanstack/react-router'
+
+// Slots are NOT declared here - they're discovered from @modal.tsx files
+// after composition. Route.Outlet gains type-safe slot prop automatically.
+export const Route = createRootRoute({
+ component: RootComponent,
+})
+
+function RootComponent() {
+ return (
+
+
+
+
+
+
+ )
+}
diff --git a/docs/rfcs/parallel-route-slots/examples/split-pane-mail/README.md b/docs/rfcs/parallel-route-slots/examples/split-pane-mail/README.md
new file mode 100644
index 0000000000..520220ce88
--- /dev/null
+++ b/docs/rfcs/parallel-route-slots/examples/split-pane-mail/README.md
@@ -0,0 +1,37 @@
+# Split-Pane Mail Client
+
+An email client with two independently navigable panes: a message list and a message preview. Each pane has its own route state.
+
+## URL Examples
+
+```
+/mail # both panes at default
+/mail?@list=/inbox # inbox selected, no preview
+/mail?@list=/inbox&@preview=/msg-123 # inbox with message 123 preview
+/mail?@list=/sent&@preview=/msg-456 # sent folder with message 456 preview
+/mail?@list=/drafts # drafts, preview closed
+```
+
+## File Structure
+
+```
+routes/
+├── __root.tsx
+├── mail.tsx # defines slots: list, preview
+├── mail.@list.tsx # message list wrapper
+├── mail.@list.index.tsx # default (all mail)
+├── mail.@list.inbox.tsx # inbox folder
+├── mail.@list.sent.tsx # sent folder
+├── mail.@list.drafts.tsx # drafts folder
+├── mail.@preview.tsx # preview pane wrapper
+├── mail.@preview.index.tsx # empty state
+├── mail.@preview.$id.tsx # message preview
+└── index.tsx
+```
+
+## Key Concepts
+
+- Two slots that navigate completely independently
+- Selecting a folder doesn't affect which message is previewed
+- Deep linking works: `/mail?@list=/sent&@preview=/msg-789`
+- Each pane loads its own data in parallel
diff --git a/docs/rfcs/parallel-route-slots/examples/split-pane-mail/routes/mail.@list.inbox.tsx b/docs/rfcs/parallel-route-slots/examples/split-pane-mail/routes/mail.@list.inbox.tsx
new file mode 100644
index 0000000000..f309d8f119
--- /dev/null
+++ b/docs/rfcs/parallel-route-slots/examples/split-pane-mail/routes/mail.@list.inbox.tsx
@@ -0,0 +1,48 @@
+// @ts-nocheck
+// Example only - this is a conceptual demonstration
+
+import { createSlotRoute, Link } from '@tanstack/react-router'
+
+export const Route = createSlotRoute({
+ path: '/inbox',
+ loader: async () => {
+ const messages = await fetchInboxMessages()
+ return { messages }
+ },
+ component: InboxList,
+})
+
+function InboxList() {
+ const { messages } = Route.useLoaderData()
+
+ return (
+
+
Inbox
+
+ {messages.map((msg) => (
+ -
+ {/* Clicking a message opens it in the preview slot - fully qualified */}
+
+ {msg.from}
+ {msg.subject}
+
+
+
+ ))}
+
+
+ )
+}
+
+async function fetchInboxMessages() {
+ return [
+ { id: 'msg-1', from: 'Alice', subject: 'Project update', date: '10:30 AM' },
+ { id: 'msg-2', from: 'Bob', subject: 'Re: Meeting notes', date: '9:15 AM' },
+ {
+ id: 'msg-3',
+ from: 'Carol',
+ subject: 'Quick question',
+ date: 'Yesterday',
+ },
+ ]
+}
diff --git a/docs/rfcs/parallel-route-slots/examples/split-pane-mail/routes/mail.@list.tsx b/docs/rfcs/parallel-route-slots/examples/split-pane-mail/routes/mail.@list.tsx
new file mode 100644
index 0000000000..3f0bb818bf
--- /dev/null
+++ b/docs/rfcs/parallel-route-slots/examples/split-pane-mail/routes/mail.@list.tsx
@@ -0,0 +1,16 @@
+// @ts-nocheck
+// Example only - this is a conceptual demonstration
+
+import { createSlotRootRoute, Outlet } from '@tanstack/react-router'
+
+export const Route = createSlotRootRoute({
+ component: ListPane,
+})
+
+function ListPane() {
+ return (
+
+
+
+ )
+}
diff --git a/docs/rfcs/parallel-route-slots/examples/split-pane-mail/routes/mail.@preview.$id.tsx b/docs/rfcs/parallel-route-slots/examples/split-pane-mail/routes/mail.@preview.$id.tsx
new file mode 100644
index 0000000000..46b442da5e
--- /dev/null
+++ b/docs/rfcs/parallel-route-slots/examples/split-pane-mail/routes/mail.@preview.$id.tsx
@@ -0,0 +1,47 @@
+// @ts-nocheck
+// Example only - this is a conceptual demonstration
+
+import { createSlotRoute } from '@tanstack/react-router'
+
+export const Route = createSlotRoute({
+ path: '/$id',
+ loader: async ({ params }) => {
+ const message = await fetchMessage(params.id)
+ return { message }
+ },
+ component: MessagePreview,
+})
+
+function MessagePreview() {
+ const { message } = Route.useLoaderData()
+
+ return (
+
+
+ {message.body}
+
+
+ )
+}
+
+async function fetchMessage(id: string) {
+ return {
+ id,
+ from: 'alice@example.com',
+ to: 'me@example.com',
+ subject: 'Project update',
+ date: 'January 4, 2026 at 10:30 AM',
+ body: 'Hey! Just wanted to give you a quick update on the project...',
+ }
+}
diff --git a/docs/rfcs/parallel-route-slots/examples/split-pane-mail/routes/mail.@preview.index.tsx b/docs/rfcs/parallel-route-slots/examples/split-pane-mail/routes/mail.@preview.index.tsx
new file mode 100644
index 0000000000..097186c10f
--- /dev/null
+++ b/docs/rfcs/parallel-route-slots/examples/split-pane-mail/routes/mail.@preview.index.tsx
@@ -0,0 +1,18 @@
+// @ts-nocheck
+// Example only - this is a conceptual demonstration
+
+import { createSlotRoute } from '@tanstack/react-router'
+
+// Empty state when no message is selected
+export const Route = createSlotRoute({
+ path: '/',
+ component: PreviewEmpty,
+})
+
+function PreviewEmpty() {
+ return (
+
+
Select a message to preview
+
+ )
+}
diff --git a/docs/rfcs/parallel-route-slots/examples/split-pane-mail/routes/mail.@preview.tsx b/docs/rfcs/parallel-route-slots/examples/split-pane-mail/routes/mail.@preview.tsx
new file mode 100644
index 0000000000..764118fa82
--- /dev/null
+++ b/docs/rfcs/parallel-route-slots/examples/split-pane-mail/routes/mail.@preview.tsx
@@ -0,0 +1,31 @@
+// @ts-nocheck
+// Example only - this is a conceptual demonstration
+
+import { createSlotRootRoute, Outlet, useMatch } from '@tanstack/react-router'
+
+export const Route = createSlotRootRoute({
+ component: PreviewPane,
+})
+
+function PreviewPane() {
+ const navigate = Route.useNavigate()
+
+ // Check if we're at something other than the preview root
+ const previewMatch = useMatch({ from: '/mail/@preview', shouldThrow: false })
+ const isAtRoot = previewMatch?.pathname === '/'
+
+ const handleClose = () => {
+ navigate({ slots: { preview: null } })
+ }
+
+ return (
+
+ {previewMatch && !isAtRoot && (
+
+ )}
+
+
+ )
+}
diff --git a/docs/rfcs/parallel-route-slots/examples/split-pane-mail/routes/mail.tsx b/docs/rfcs/parallel-route-slots/examples/split-pane-mail/routes/mail.tsx
new file mode 100644
index 0000000000..0c28fbb861
--- /dev/null
+++ b/docs/rfcs/parallel-route-slots/examples/split-pane-mail/routes/mail.tsx
@@ -0,0 +1,34 @@
+// @ts-nocheck
+// Example only - this is a conceptual demonstration
+
+import { createFileRoute, Link } from '@tanstack/react-router'
+
+// Slots are NOT declared here - they're discovered from mail.@*.tsx files
+// after composition. Route.Outlet gains type-safe slot prop automatically.
+export const Route = createFileRoute('/mail')({
+ component: MailLayout,
+})
+
+function MailLayout() {
+ return (
+
+ {/* Sidebar with folders - using fully qualified paths */}
+
+
+ {/* Message list pane */}
+
+
+
+
+ {/* Preview pane */}
+
+
+
+
+ )
+}