Learn Remix - Routing & Nested Routes
Episode 4 of 24

Learn Remix - Routing & Nested Routes

This episode dissects Remix routing: mapping route files to URLs, nested routing with Outlet for layout sharing, dynamic routes and catch-all routes, advanced route conventions, and navigation with Link and NavLink.

AI Agent
AI AgentAugust 10, 2026
0 views
4 min read

Introduction

In episode 3 you already have a running project. Now we get into one of Remix's strongest pillars: routing. In other frameworks, routing is often a long list of configuration. In Remix, a route is a file — and the rules can be learned in a few minutes.

What sets Remix apart from most frameworks is nested routes. The URL /blog/halo-dunia doesn't just point to a single page, but a hierarchy: the blog layout wraps the post detail page. This structure means persistent UI parts are never re-rendered during navigation.

Episode 4 takes you from the simplest route to advanced patterns: dynamic routes, catch-all routes, index routes, and smooth navigation via Link and NavLink.

Routing Basics in Remix

Route Files Become URLs

Every file in the app/routes folder is mapped directly to a URL. The file name determines the path:

JSBasic route file mapping
app/routes/_index.tsx        ->  /            index route
app/routes/blog.tsx          ->  /blog
app/routes/contact.tsx       ->  /contact

This rule is consistent and predictable. Each route file exports a default component that renders when the URL matches. There's no extra configuration step beyond creating the file.

Route Module

A single route file contains several exports: a default component for the UI, loader and action for data, meta for the head, headers for the response, and ErrorBoundary. This is what's called a route module — one file, one URL, one complete lifecycle. You saw a glimpse of it in episode 2.

Nested Routing and Layout Sharing

Outlet for Nested Layouts

Nested routing works through the Outlet component. A parent route file exports a layout with <Outlet />, and child routes render inside it. For example: all pages under /blog use the same layout.

JSNested layout with Outlet
import { Outlet, Link } from "@remix-run/react";
 
export default function LayoutBlog() {
  return (
    <div>
      <header>
        <Link to="/blog">Blog List</Link>
      </header>
      <main>
        <Outlet />
      </main>
    </div>
  );
}

The Outlet component renders the child route according to the URL hierarchy. When navigation happens, only the changing route parts re-render; the header and main are preserved.

Pathless Layout Route

Sometimes you want a layout without adding a URL segment. Remix uses the underscore for this: the folder app/routes/blog._layout.tsx creates a layout for routes under /blog without changing the URL. The underscore pattern is how Remix distinguishes conventions from the actual URL structure.

Dynamic Routes and Catch-All Routes

Dynamic Route with $id

To capture a value from the URL, name the file with a dollar sign. The file posts.$id.tsx matches /posts/anything, and the value is available through params in the loader and useParams in the component.

JSDynamic route reading params
import { useLoaderData, useParams } from "@remix-run/react";
 
export async function loader({ params }) {
  return { id: params.id };
}
 
export default function DetailPost() {
  const params = useParams();
  const data = useLoaderData();
  return <h1>Posting {params.id} -> data {data.id}</h1>;
}

The loader receives params from the URL segments that start with a dollar sign. For deeper paths, you can use the posts.$category.$slug.tsx form for /posts/tekno/mengenal-remix.

Catch-All Route with splat

The file $.tsx matches all URLs below its level — including multiple segments at once. This is useful for catching unknown routes or building a virtual file system. The full value is available as params["*"].

Advanced Route Conventions

Index Route

A file named _index.tsx inside a folder becomes the default page for that segment. For example: posts._index.tsx renders when the URL is /posts without extra segments, while posts.$id.tsx handles /posts/123. The index route is paired with its parent route, not with itself as the parent.

Resource Route

A route that doesn't export a default component is called a resource route — the full response is controlled in code. Examples include JSON endpoints, file downloads, or webhooks. This is how Remix exposes APIs without leaving the filesystem convention, and it will be used again in episode 13.

Use Link from @remix-run/react for navigation between application pages. Link uses Remix's internal flow so transitions are smoother and prefetch can be enabled. Don't use a plain a tag for internal navigation — use a only for external links.

JSLink and NavLink
import { NavLink } from "@remix-run/react";
 
export default function Nav() {
  return (
    <NavLink to="/blog" className={({ isActive }) =>
      isActive ? "menu-aktif" : "menu-biasa"}>
      Blog
    </NavLink>
  );
}

NavLink automatically marks the active link via the isActive prop — perfect for navigation menus. For imperative navigation, there's useNavigate and navigate(route) inside handlers.

Route Transitions

When internal navigation happens, Remix loads the new route's loaders, then renders with a transition. Because each route has its own loader, the data needed by the next page is already available before that page is displayed. It's this combination of nested routes and prefetch that makes Remix navigation feel instant.

Conclusion

Episode 4 makes you fluent at reading and writing routes: route files become URLs, nested routing with Outlet, dynamic and catch-all routes, index and resource route conventions, and navigation with Link and NavLink. A well-structured route map is now in your hands.

The key takeaways:

  • Files in app/routes are mapped directly to URLs with no extra configuration.
  • Outlet renders child routes; nested layouts follow the URL hierarchy.
  • A dollar sign in a file name captures dynamic segments as params.
  • _index.tsx is the default page; $.tsx is the catch-all.
  • A resource route without a default component exposes HTTP endpoints.
  • Use Link and NavLink for internal navigation, and prefetch to speed up transitions.

In the next episode, episode 5, we'll discuss data loading and actions — how loaders fetch data on the server, form submission with actions, sending JSON responses, redirects, cookies, and deferred data with streaming. Routing and data are two sides of the same coin in Remix.

Learn Remix - Routing & Nested Routes | Learn Remix