Documentation Index
Fetch the complete documentation index at: https://mintlify.com/remix-run/react-router/llms.txt
Use this file to discover all available pages before exploring further.
Nested Routes
Nested routes allow you to compose complex UIs by nesting routes inside parent routes. Child routes render inside their parent’s <Outlet /> component, creating a hierarchical layout structure.
Basic Nested Routes
Define child routes using the children parameter:
// app/routes.ts
import { route } from "@react-router/dev/routes";
export default [
route("dashboard", "routes/dashboard.tsx", [
route("overview", "routes/dashboard/overview.tsx"),
route("analytics", "routes/dashboard/analytics.tsx"),
route("settings", "routes/dashboard/settings.tsx"),
]),
];
This creates the following URL structure:
/dashboard → renders dashboard.tsx
/dashboard/overview → renders dashboard.tsx with overview.tsx in the outlet
/dashboard/analytics → renders dashboard.tsx with analytics.tsx in the outlet
/dashboard/settings → renders dashboard.tsx with settings.tsx in the outlet
Parent Route Component
The parent route must render an <Outlet /> where child routes appear:
// app/routes/dashboard.tsx
import { Outlet, NavLink } from "react-router";
export default function Dashboard() {
return (
<div className="dashboard">
<nav>
<NavLink to="overview">Overview</NavLink>
<NavLink to="analytics">Analytics</NavLink>
<NavLink to="settings">Settings</NavLink>
</nav>
<main>
<Outlet />
</main>
</div>
);
}
Multi-Level Nesting
Routes can be nested to any depth:
// app/routes.ts
import { route, index } from "@react-router/dev/routes";
export default [
route("app", "routes/app.tsx", [
route("projects", "routes/app/projects.tsx", [
index("routes/app/projects/list.tsx"),
route(":id", "routes/app/projects/detail.tsx", [
route("edit", "routes/app/projects/edit.tsx"),
route("settings", "routes/app/projects/settings.tsx"),
]),
]),
]),
];
URL structure:
/app/projects → app.tsx > projects.tsx > list.tsx
/app/projects/123 → app.tsx > projects.tsx > detail.tsx
/app/projects/123/edit → app.tsx > projects.tsx > detail.tsx > edit.tsx
Shared Layouts with Nested Routes
Nested routes are perfect for shared layouts:
// app/routes.ts
import { route, layout } from "@react-router/dev/routes";
export default [
layout("routes/marketing-layout.tsx", [
route("about", "routes/about.tsx"),
route("contact", "routes/contact.tsx"),
route("pricing", "routes/pricing.tsx"),
]),
layout("routes/app-layout.tsx", [
route("dashboard", "routes/dashboard.tsx"),
route("profile", "routes/profile.tsx"),
]),
];
Pathless Layout Routes
Use layout() to create routes that don’t add URL segments:
// app/routes.ts
import { route, layout, index } from "@react-router/dev/routes";
export default [
route("account", "routes/account.tsx", [
layout("routes/account/private-layout.tsx", [
route("orders", "routes/account/orders.tsx"),
route("profile", "routes/account/profile.tsx"),
]),
layout("routes/account/public-layout.tsx", [
route("login", "routes/account/login.tsx"),
route("signup", "routes/account/signup.tsx"),
]),
]),
];
Both layouts share the /account URL prefix but provide different UI wrappers.
Data Loading in Nested Routes
Each route in the hierarchy can load its own data:
// app/routes/dashboard.tsx
import type { Route } from "./+types/dashboard";
export async function loader({ request }: Route.LoaderArgs) {
return { user: await getUser(request) };
}
export default function Dashboard({ loaderData }: Route.ComponentProps) {
return (
<div>
<h1>Welcome, {loaderData.user.name}</h1>
<Outlet />
</div>
);
}
// app/routes/dashboard/analytics.tsx
import type { Route } from "./+types/dashboard/analytics";
export async function loader() {
return { stats: await getAnalytics() };
}
export default function Analytics({ loaderData }: Route.ComponentProps) {
return <div>Stats: {loaderData.stats}</div>;
}
Both loaders run in parallel when navigating to /dashboard/analytics.
Accessing Parent Data
Child routes can access parent route data using useMatches():
// app/routes/dashboard/analytics.tsx
import { useMatches } from "react-router";
export default function Analytics() {
const matches = useMatches();
const dashboardData = matches.find(m => m.id === "routes/dashboard")?.data;
return <div>User: {dashboardData.user.name}</div>;
}
Nested Navigation
Use relative paths in links within nested routes:
// app/routes/dashboard.tsx
import { NavLink, Outlet } from "react-router";
export default function Dashboard() {
return (
<div>
<nav>
{/* Relative to /dashboard */}
<NavLink to="overview">Overview</NavLink>
<NavLink to="analytics">Analytics</NavLink>
{/* Absolute path */}
<NavLink to="/dashboard/settings">Settings</NavLink>
{/* Go up one level */}
<NavLink to="..">Back to Home</NavLink>
</nav>
<Outlet />
</div>
);
}
File-Based Nested Routes
When using flatRoutes(), use dot notation for nesting:
app/routes/
├── dashboard.tsx # /dashboard
├── dashboard.overview.tsx # /dashboard/overview
├── dashboard.analytics.tsx # /dashboard/analytics
└── dashboard.settings.tsx # /dashboard/settings
See File Conventions for more details.
Opt-Out of Parent Layout
In file-based routing, use trailing underscore to skip parent segments:
app/routes/
├── app.tsx # /app
├── app.dashboard.tsx # /app/dashboard
└── app_.settings.tsx # /settings (skips /app)
Or in manual config:
export default [
route("app", "routes/app.tsx", [
route("dashboard", "routes/dashboard.tsx"),
]),
// Settings is NOT nested under /app
route("settings", "routes/settings.tsx"),
];
Error Boundaries in Nested Routes
Each route can export an error boundary:
// app/routes/dashboard.tsx
import { useRouteError } from "react-router";
export function ErrorBoundary() {
const error = useRouteError();
return (
<div>
<h1>Dashboard Error</h1>
<p>{error.message}</p>
</div>
);
}
export default function Dashboard() {
return <Outlet />;
}
Errors in child routes will bubble up to the nearest parent error boundary.
Best Practices
- Shallow hierarchies: Keep nesting to 3-4 levels maximum
- Logical grouping: Nest routes that share UI, not just URL structure
- Parallel loading: Leverage React Router’s automatic parallel data loading
- Layout reuse: Use pathless layouts to share UI without affecting URLs
- Independent routes: Don’t nest routes that don’t share layout just for URL structure