Now liveThe Skillselion MCP - thousands of ranked skills, loaded into your agent mid-task. No install.Get it →
existential-birds avatar

Remix V2 Routing

  • 27 installs
  • 74 repo stars
  • Updated July 21, 2026
  • existential-birds/beagle

Helps with ai & agent building tasks.

About

remix-v2-routing is a Claude Code skill for ai & agent building. It helps solo builders move faster with AI-assisted development.

  • remix-v2-routing
  • AI & Agent Building
  • AI-coding skill

Remix V2 Routing by the numbers

  • 27 all-time installs (skills.sh)
  • Ranked #9,601 of 16,546 AI & Agent Building skills by installs in the Skillselion catalog
  • Data as of Jul 28, 2026 (Skillselion catalog sync)
npx skills add https://github.com/existential-birds/beagle --skill remix-v2-routing

Add your badge

Show developers this skill is listed on Skillselion. Paste this into your README.

Listed on Skillselion
Installs27
repo stars74
Last updatedJuly 21, 2026
Repositoryexistential-birds/beagle

What it does

Helps with ai & agent building tasks.

Files

SKILL.mdMarkdownGitHub ↗

Remix v2 Routing

Quick Reference

Flat-routes v2 filename rules (all files live in app/routes/):

_index.tsx                 → /
concerts.tsx               → /concerts                (acts as layout when dotted children exist; otherwise leaf for /concerts)
concerts._index.tsx        → /concerts                (renders under layout)
concerts.$city.tsx         → /concerts/:city          params.city
concerts.trending.tsx      → /concerts/trending
_auth.tsx + _auth.login.tsx → /login (pathless layout, no URL segment)
files.$.tsx                → /files/*                 params["*"]
($lang)._index.tsx         → / and /en (or /fr etc.) — optional segment
sitemap[.]xml.tsx          → /sitemap.xml             (escape literal)
concerts_.mine.tsx         → /concerts/mine           (opts out of layout)
dashboard/route.tsx        → /dashboard               (folder + route.tsx)
reports.$id[.pdf].tsx      → /reports/:id.pdf         (no default export = resource)

Imports — always use `@remix-run/react`, never `react-router-dom`:

import { Outlet, Link, useLoaderData, useParams } from "@remix-run/react";
import type { LoaderFunctionArgs } from "@remix-run/node"; // or /cloudflare, /deno

File Naming Conventions

Dots in filenames create URL slashes and parent/child nesting. Underscore prefix marks pathless segments (_auth.tsx) and index routes (_index.tsx). Trailing underscore (concerts_.mine.tsx) opts out of layout nesting while keeping the URL nested. Brackets escape literal characters: sitemap[.]xml.tsx. Splat is the single dollar sign: $.tsx exposes the rest of the path under params["*"]. Optional segments are wrapped in parens: ($lang).

See references/conventions.md for the full table and edge cases.

Nested Layouts

A parent module (concerts.tsx) renders <Outlet />; child routes (concerts.$city.tsx, concerts._index.tsx) mount inside it automatically based on the dot-delimited filename.

// app/routes/concerts.tsx
import { Outlet } from "@remix-run/react";

export default function ConcertsLayout() {
  return (
    <section>
      <nav>{/* concerts subnav */}</nav>
      <Outlet />
    </section>
  );
}
// app/routes/concerts._index.tsx  (renders at exactly /concerts)
export default function ConcertsIndex() {
  return <h1>Browse concerts</h1>;
}

For a layout with no URL contribution, prefix with a single underscore:

// app/routes/_auth.tsx          → wraps /login, /signup; no URL segment
// app/routes/_auth.login.tsx    → /login   (inherits _auth layout)
// app/routes/_auth.signup.tsx   → /signup

Dynamic Segments and Splats

$name captures a single segment; $.tsx captures the rest of the path. Loader receives values via params:

// app/routes/concerts.$city.tsx
import type { LoaderFunctionArgs } from "@remix-run/node";
import { json } from "@remix-run/node";
import { useLoaderData } from "@remix-run/react";

export async function loader({ params }: LoaderFunctionArgs) {
  if (!params.city) throw new Response("Not found", { status: 404 });
  return json({ city: params.city });
}

export default function City() {
  const { city } = useLoaderData<typeof loader>();
  return <h1>{city}</h1>;
}

Splat values live under "*" — there is no params.splat:

// app/routes/files.$.tsx
export async function loader({ params }: LoaderFunctionArgs) {
  const rest = params["*"]; // bracket access only
  return new Response(await readBlob(rest), { headers: { "Content-Type": "application/octet-stream" } });
}

Root Module

app/root.tsx is the only required route. It owns the document shell and must render <Meta />, <Links />, <Outlet />, <ScrollRestoration />, <Scripts />, and (during dev) <LiveReload />. See references/root.md.

Resource Routes

A route module without a default export is a resource route — it returns raw Response objects (PDF, JSON, RSS, webhooks). Parent loaders do not run, and <Link> must use reloadDocument (or be replaced with <a>) to trigger a real document request. See references/resource-routes.md.

Gates (decision sequencing)

Answer in order. Pass means the condition is true; pick the answer on the same line and stop.

Layout vs flat URL

0. Is there shared chrome at all (nav, breadcrumbs, sidebar) at this level?

  • Fail → Use plain dotted segments (about.tsx, pricing.tsx); no layout module needed. Stop.
  • Pass → Step 1.

1. Should this URL share UI (nav, breadcrumbs, sidebar) with a parent path?

  • Pass → Use dot-delimited nesting (concerts.$city.tsx under concerts.tsx). Stop.
  • Fail → Step 2.

2. *Does the URL just happen to be nested but should render standalone*?

  • Pass → Trailing underscore (concerts_.mine.tsx). Stop.
  • Fail → Step 3.

3. Need a wrapper layout but no parent URL segment?

  • Pass → Single-underscore pathless parent (_auth.tsx + _auth.login.tsx). Stop.

UI route vs resource route

1. Does this URL ever render HTML to a user?

  • Pass → Export a default component. UI route. Stop.
  • Fail → Step 2.

2. Returns JSON, PDF, RSS, sitemap, webhook, or other raw `Response`?

  • Pass → Omit default export — module becomes a resource route. Use reloadDocument on any <Link> pointing to it. Stop.

_index.tsx vs index.tsx

1. On Remix v2 with flat-routes?

  • Pass → _index.tsx (leading underscore). Stop.
  • Fail → Step 2 — you're on v1 (or using the v1 fallback adapter).

2. Need to keep a v1 nested-folder tree alive?

  • Pass → Install @remix-run/v1-route-convention and wire it in remix.config.js. See references/v1-migration.md. Stop.

Additional Documentation

  • Conventions: See references/conventions.md for the full filename grammar (_index, _layout, $param, splat, optional, escape, folder + route.tsx, trailing underscore).
  • Root module: See references/root.md for the root.tsx scaffold and the required document elements.
  • Resource routes: See references/resource-routes.md for non-HTML responses, the no-default-export rule, parent-loader skipping, and reloadDocument.
  • v1 → v2 migration: See references/v1-migration.md for differences from v1 (__double underscore folders, index.tsx, @remix-run/v1-route-convention, ignoredRouteFiles).

v1 vs v2 Convention Comparison

Concernv1 (nested folders)v2 (flat routes)
Index routeindex.tsx_index.tsx
Pathless layout__auth/ (double underscore)_auth.tsx (single)
Nested URLfolder hierarchydot delimiter in filename
Dynamic segment$param.tsx$param.tsx (unchanged)
Splat$.tsx$.tsx (unchanged)
Escape literaln/a[.], [] brackets
Opt-out of layoutmove out of foldertrailing _ (foo_.bar.tsx)
Co-locationadjacent files in folderfeature/route.tsx + siblings
Fallback adaptern/a@remix-run/v1-route-convention

Related skills

This week in AI coding

Five minutes, every Monday - the tools, releases and tactics for developers.

unsubscribe anytime.