Nested routes in Lovable use React Router's Outlet component to render child routes inside a parent layout. Define the parent route without a trailing path element, place child routes inside it, and add an Outlet component in the parent layout where child pages should appear. This pattern enables shared navigation, sidebars, and headers across related pages.
Why nested routes fail to render child pages in Lovable
Nested routes let you share a common layout (like a sidebar or header) between multiple pages without duplicating code. In React Router, you define a parent route with a layout component, and child routes render inside that layout via the Outlet component. The most common failure is forgetting to add the Outlet component in the parent layout. Without Outlet, React Router has nowhere to render the child route's component — the layout appears but the page content area is blank. Another frequent issue is incorrect route nesting in App.tsx. Child routes must be JSX children of the parent Route element. If they are placed at the same level (siblings instead of children), React Router treats them as independent routes and does not apply the parent layout.
- Missing Outlet component in the parent layout — child routes have nowhere to render
- Routes not properly nested — child routes are siblings instead of children of the parent Route
- Parent route has an element with trailing path that matches too specifically
- Child route paths are absolute instead of relative — they do not inherit the parent prefix
- Index route missing — visiting the parent path shows nothing because no default child is defined
Error messages you might see
Child route content is blank but the layout rendersThe parent layout component does not include an Outlet component. Add <Outlet /> in the location where child page content should appear.
No routes matched location "/dashboard/analytics"The child route for 'analytics' is not nested inside the '/dashboard' parent route. Make it a JSX child of the parent Route element.
Outlet rendered outside of a Route hierarchyThe Outlet component is used in a component that is not rendered by a React Router Route element. Ensure the layout component is the element of a parent Route.
Before you start
- A Lovable project with multiple pages that should share a common layout
- React Router v6 (the default for all Lovable projects)
- An understanding of which pages should share which layout elements
How to fix it
Create a layout component with Outlet
The layout component provides shared UI (sidebar, header) and Outlet marks where child pages render
Create a layout component with Outlet
The layout component provides shared UI (sidebar, header) and Outlet marks where child pages render
Create a new component that contains the shared layout elements (sidebar, header, footer) and includes the Outlet component from react-router-dom where the page content should appear. The Outlet component is the placeholder that React Router fills with the matched child route's component.
// No shared layout — each page duplicates the sidebarfunction Dashboard() { return ( <div className="flex"> <Sidebar /> <main>Dashboard content</main> </div> );}function Analytics() { return ( <div className="flex"> <Sidebar /> <main>Analytics content</main> </div> );}import { Outlet } from "react-router-dom";import Sidebar from "@/components/Sidebar";export default function DashboardLayout() { return ( <div className="flex min-h-screen"> <Sidebar /> <main className="flex-1 p-8"> {/* Child route components render here */} <Outlet /> </main> </div> );}Expected result: The layout renders the sidebar once, and child pages appear in the Outlet area without duplicating the sidebar.
Nest child routes inside the parent route in App.tsx
React Router only renders child components via Outlet when routes are properly nested as JSX children
Nest child routes inside the parent route in App.tsx
React Router only renders child components via Outlet when routes are properly nested as JSX children
Open src/App.tsx. Wrap the child routes inside the parent Route element. The parent Route gets the layout component as its element and the base path. Child routes use relative paths (without the parent prefix). Add an index route for the default child that renders when the user visits the parent path directly.
<Routes> <Route path="/dashboard" element={<Dashboard />} /> <Route path="/dashboard/analytics" element={<Analytics />} /> <Route path="/dashboard/settings" element={<Settings />} /></Routes><Routes> <Route path="/dashboard" element={<DashboardLayout />}> {/* Index route: renders at /dashboard */} <Route index element={<DashboardHome />} /> {/* Child routes: render at /dashboard/analytics etc. */} <Route path="analytics" element={<Analytics />} /> <Route path="settings" element={<DashboardSettings />} /> </Route> <Route path="*" element={<NotFound />} /></Routes>Expected result: Visiting /dashboard shows DashboardHome inside the layout. Visiting /dashboard/analytics shows Analytics inside the same layout.
Use relative paths in nested navigation links
Relative links automatically prepend the parent route path, so your links stay correct even if the parent path changes
Use relative paths in nested navigation links
Relative links automatically prepend the parent route path, so your links stay correct even if the parent path changes
In the sidebar or navigation within the layout, use relative paths for links to child routes. A Link with to='analytics' inside the /dashboard layout navigates to /dashboard/analytics. This is cleaner than hardcoding the full path and makes refactoring easier.
<NavLink to="/dashboard/analytics">Analytics</NavLink><NavLink to="/dashboard/settings">Settings</NavLink>import { NavLink } from "react-router-dom";{/* Relative paths — automatically prepend the parent /dashboard */}<NavLink to="analytics" className={({ isActive }) => isActive ? "bg-accent" : ""}> Analytics</NavLink><NavLink to="settings" className={({ isActive }) => isActive ? "bg-accent" : ""}> Settings</NavLink>Expected result: Navigation links work correctly within the nested layout, with active state highlighting on the current child route.
Add an index route for the parent path
Without an index route, visiting /dashboard directly shows the layout but with an empty Outlet
Add an index route for the parent path
Without an index route, visiting /dashboard directly shows the layout but with an empty Outlet
The index route is a special child that matches when the user visits the parent path exactly (no additional path segment). It acts as the default page for the layout. Without it, navigating to /dashboard shows the sidebar but an empty content area. For complex nested routing with multiple layout levels and shared state, RapidDev's engineers have built these patterns across 600+ Lovable projects.
<Route path="/dashboard" element={<DashboardLayout />}> <Route path="analytics" element={<Analytics />} /></Route><Route path="/dashboard" element={<DashboardLayout />}> {/* Shows at /dashboard (no trailing path) */} <Route index element={<DashboardHome />} /> <Route path="analytics" element={<Analytics />} /></Route>Expected result: Visiting /dashboard renders DashboardHome inside the layout instead of showing empty content.
Complete code example
1import { Outlet, NavLink } from "react-router-dom";2import { LayoutDashboard, BarChart3, Settings, LogOut } from "lucide-react";3import { useAuth } from "@/contexts/AuthContext";45export default function DashboardLayout() {6 const { signOut } = useAuth();78 const linkClass = ({ isActive }: { isActive: boolean }) =>9 `flex items-center gap-3 px-4 py-2 rounded-md transition-colors ${10 isActive11 ? "bg-accent text-accent-foreground font-medium"12 : "text-muted-foreground hover:bg-accent/50"13 }`;1415 return (16 <div className="flex min-h-screen">17 {/* Sidebar — shared across all dashboard child routes */}18 <aside className="w-64 border-r bg-card p-4 flex flex-col">19 <h2 className="text-lg font-semibold px-4 mb-6">Dashboard</h2>20 <nav className="flex flex-col gap-1 flex-1">21 {/* Relative paths — React Router prepends /dashboard */}22 <NavLink to="." end className={linkClass}>23 <LayoutDashboard className="h-4 w-4" />24 Overview25 </NavLink>26 <NavLink to="analytics" className={linkClass}>27 <BarChart3 className="h-4 w-4" />28 Analytics29 </NavLink>30 <NavLink to="settings" className={linkClass}>31 <Settings className="h-4 w-4" />32 Settings33 </NavLink>34 </nav>35 <button36 onClick={signOut}37 className="flex items-center gap-3 px-4 py-2 text-muted-foreground hover:text-foreground"38 >39 <LogOut className="h-4 w-4" />40 Sign out41 </button>42 </aside>4344 {/* Main content — child routes render here via Outlet */}45 <main className="flex-1 p-8 overflow-auto">46 <Outlet />47 </main>48 </div>49 );50}Best practices to prevent this
- Always include an Outlet component in layout components — without it, child routes have nowhere to render
- Use relative paths in child Route definitions (path='analytics' not path='/dashboard/analytics') so they inherit the parent prefix
- Add an index route for every layout to handle the case when the user visits the parent path directly
- Use NavLink with the end prop for the index route link to prevent it from being active on all child routes
- Keep layouts focused — a layout should only contain shared UI like navigation, not page-specific content
- Nest Route elements as JSX children in App.tsx — placing them as siblings creates independent routes that skip the layout
Still stuck?
Copy one of these prompts to get a personalized, step-by-step explanation.
I have a Lovable.dev project and I want to create nested routes where multiple pages share a sidebar layout. Here is my current routing: [paste your App.tsx Routes] I need these pages to share a layout with a sidebar: - /dashboard (overview) - /dashboard/analytics - /dashboard/settings Please help me: 1. Create a DashboardLayout component with Outlet 2. Restructure my routes to be properly nested 3. Add navigation links with active state in the sidebar 4. Add an index route for /dashboard
Restructure the dashboard routes in @src/App.tsx to use nested routing. Create a @src/components/DashboardLayout.tsx with a sidebar containing NavLinks to overview, analytics, and settings. Use Outlet for the content area. Move the existing Dashboard, Analytics, and Settings pages as child routes of the layout route. Add an index route that renders the Dashboard page at /dashboard.
Frequently asked questions
What is the Outlet component in React Router?
Outlet is a placeholder component provided by React Router. When placed in a parent layout component, it renders the matched child route's component. It enables nested routing where multiple pages share a common layout.
Why is my nested route content blank even though the layout shows?
The most common cause is a missing Outlet component in the layout. Check that your layout component includes <Outlet /> where the child page content should appear.
What is an index route?
An index route matches when the user visits the parent path exactly, with no additional path segment. For example, an index route under /dashboard renders at /dashboard itself. Without an index route, the Outlet is empty at the parent path.
Should I use absolute or relative paths in nested routes?
Use relative paths for child routes (path='analytics' not path='/dashboard/analytics'). Relative paths automatically prepend the parent route's path, making the routes easier to refactor if the parent path changes.
Can I nest routes more than one level deep?
Yes. You can nest routes as deep as you need. Each level needs its own layout with an Outlet component. For example: /dashboard renders DashboardLayout, /dashboard/analytics renders AnalyticsLayout inside DashboardLayout, and /dashboard/analytics/weekly renders the content inside AnalyticsLayout.
What if I can't fix this myself?
If your app needs complex nested routing with authentication guards, role-based layouts, and dynamic segments at multiple levels, RapidDev's engineers have built these patterns across 600+ Lovable projects.
Talk to an Expert
Our team has built 600+ apps. Get personalized help with your issue.
Book a free consultation