Nr 04 Pages Vs Nested Routes
title: "Next.js vs Remix — Pages vs Nested Routes Architecture" description: "Compare Next.js page-based routing with Remix nested routing architecture, including layout inheritance, parallel data loading, and route composition." weight: 14 date: 2026-06-28 lastmod: 2026-06-28 tags: [frameworks, react]
Next.js and Remix take different approaches to route composition. Next.js uses page-based routing with optional layouts. Remix uses nested routes with automatic layout inheritance.
What You'll Learn
You will understand the difference between page routing and nested routing, how each affects data loading, layout inheritance, and route behavior.
Why It Matters
Route architecture affects how data is loaded, how layouts render, and how navigation feels. Nested routes in Remix provide automatic parallel data loading that Next.js achieves manually.
Real-World Use
DodaTech's internal CRM uses Remix nested routes so the customer list loads once in the parent route, and each customer detail page loads its own data in a child route without refetching the list.
flowchart TD
subgraph Next[Next.js Pages]
A[/products] --> B[Page loads all data]
A --> C[/products/:id]
C --> D[Page loads its own data]
end
subgraph Remix[Remix Nested]
E[/products] --> F[Parent loader: list data]
E --> G[/products/:id]
F --> H[Parent layout persists]
G --> I[Child loader: detail data]
end
style Next fill:#121212,color:#fff
style Remix fill:#1a1a2e,color:#fff
Next.js Page-Based Routing
In Next.js, each page is independent. When navigating from /products to /products/123, the entire page re-renders, fetching all data again.
// app/products/page.js — loads everything
export default async function ProductsPage() {
const products = await db.products.findAll();
return <ProductGrid products={products} />;
}
// app/products/[id]/page.js — loads everything again
export default async function ProductPage({ params }) {
const product = await db.products.findById(params.id);
return <ProductDetail product={product} />;
}
Expected output: Navigating from the product list to a product detail re-renders the whole page. The product list data is fetched again if you navigate back.
Remix Nested Routes
In Remix, each route segment has its own loader. Parent route data persists when navigating between child routes.
// app/routes/products.jsx
export async function loader() {
return db.products.findAll();
}
export default function ProductsLayout({ children }) {
const products = useLoaderData();
return (
<div style={{ display: 'flex' }}>
<ProductSidebar products={products} />
<main>{children}</main>
</div>
);
}
// app/routes/products.$id.jsx
export async function loader({ params }) {
return db.products.findById(params.id);
}
export default function ProductDetail() {
const product = useLoaderData();
return <ProductView product={product} />;
}
Expected output: Navigating between product details updates only the right panel. The sidebar with the product list persists without refetching.
Parallel Data Loading in Remix
Remix loads data for all matching route segments in parallel. The parent loader and child loader run simultaneously.
// app/routes/users.jsx
export async function loader() {
// Runs in parallel with child loader
return db.users.findAll();
}
// app/routes/users.$id.jsx
export async function loader({ params }) {
// Runs in parallel with parent loader
return db.users.findById(params.id);
}
// app/routes/users.$id.settings.jsx
export async function loader({ params }) {
// Runs in parallel with both parent loaders
return db.settings.findByUserId(params.id);
}
Expected output: When visiting /users/123/settings, all three loaders run simultaneously. Total load time is the slowest single loader, not the sum of all three.
Layout Persistence Across Navigation
Remix preserves parent layouts during child navigation. Only the changed segment re-renders.
Next.js can achieve similar behavior using layout.js, but each page re-fetches its data unless explicitly cached.
Route Composition Patterns
Remix routes compose naturally. Child routes receive parent data through URL params and search params.
Next.js routes are more independent. Sharing data between parent and child routes requires lifting state to layouts or using context.
Common Mistakes
Not leveraging Remix parallel loading: If you have three levels of nested routes, all data loads in parallel automatically. Do not create waterfalls by fetching in useEffect.
Over-fetching in Next.js layouts: Layouts persist but do not prevent child pages from fetching their own data. Use caching to avoid redundant fetches.
Creating unnecessarily deep nesting: Remix encourages nesting but deep nesting (5+ levels) can make the route structure hard to follow.
Forgetting that Remix layouts re-render on parent data changes: If parent data changes, the layout re-renders but child data stays cached.
Not using the Outlet component correctly: In Remix, child routes render where the parent puts
. Forgetting it means children never render.
Practice Questions
- How does Remix handle data loading for nested routes?
Each route segment's loader runs in parallel. Parent and child loaders execute simultaneously when navigation occurs.
- What happens to parent layout data when navigating between child routes in Remix?
Parent layout data persists. Only the child route's data is fetched on navigation. The parent does not re-fetch.
- How does Next.js App Router achieve layout persistence?
Using layout.js files. The layout persists but does not automatically share data with child pages.
- What is the purpose of the Outlet component in Remix?
Outlet renders the child route component. It is placed in the parent layout where children should appear.
- How do you share data between parent and child routes in Next.js?
Use React Context, pass data through props from the layout, or use a shared data fetching utility with caching.
Challenge
Build a customer management interface with a list sidebar and detail view. Implement it in both frameworks and compare the data loading behavior when navigating between customers.
Frequently Asked Questions
Mini Project
Build a three-level nested interface (organization > projects > tasks) in both frameworks. Measure the data loading time for each level and compare the user experience of navigating between tasks.
What's Next
Continue to Data Loading Contrast for a detailed comparison of getServerSideProps vs Remix loaders.
Built by the developers of DodaTech
Doda Browser, DodaZIP & Durga Antivirus Pro