remix-v2-routing について
Remix v2 開発者向けのルーティングスキルです。フラットルートのファイル名、ネストされたルート、リソースルートを解説し、レイアウトや URL 構造、ルート種別の選択を支援します。
主な機能
- 01フラットルートのファイル命名規則
- 02ネスト型・パスレスレイアウトの設定
- 03動的セグメントとスプラットの扱い
- 04UI ルートとリソースルートの区別
- 05v1 と v2 の規約比較
活用シーン
- 01共通ナビゲーションを持つルートの設計
- 02ログインページ用のパスレスレイアウト
- 03動的 URL やスプラットルートの作成
- 04JSON、PDF、Webhook 用リソースルートの作成
インストール
コマンドをコピーしてターミナルで実行します。
npx skills add https:/ / github.com/ existential-birds/ beagle --skill remix-v2-routingインストール後、Claude Code で/remix-v2-routingを入力して読み込まれたか確認します。
同梱ファイル
SKILL.md と一緒にインストールされる参考資料やスクリプト。
references/conventions.mdresource-routes.mdroot.mdv1-migration.md詳細
Remix v2 のルーティング
クイックリファレンス
Flat-routes v2 のファイル名規則(すべてのファイルは 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)
インポート — 常に @remix-run/react を使用し、react-router-dom は使用しないでください:
import { Outlet, Link, useLoaderData, useParams } from "@remix-run/react";
import type { LoaderFunctionArgs } from "@remix-run/node"; // or /cloudflare, /deno
ファイル名の規則
ファイル名のドットは、URL のスラッシュと親子のネストを作成します。アンダースコアのプレフィックスは、パスレスセグメント(_auth.tsx)とインデックスルート(_index.tsx)を示します。末尾のアンダースコア(concerts_.mine.tsx)を付けると、URL のネストは維持したままレイアウトのネストから除外されます。角括弧で文字をエスケープできます:sitemap[.]xml.tsx。スプラットには単一のドル記号を使います:$.tsx は params["*"] の下でパスの残りを公開します。オプショナルセグメントは丸括弧で囲みます:($lang)。
完全な表と例外については、references/conventions.md を参照してください。
ネストされたレイアウト
親モジュール(concerts.tsx)は <Outlet /> をレンダリングします。子ルート(concerts.$city.tsx、concerts._index.tsx)は、ドット区切りのファイル名に基づいて自動的にその中にマウントされます。
// 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>;
}
URL に影響しないレイアウトには、単一のアンダースコアをプレフィックスとして付けます:
// 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
動的セグメントとスプラット
$name は単一のセグメントをキャプチャし、$.tsx はパスの残りをキャプチャします。ローダーは 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>;
}
スプラットの値は "*" の下に格納されます。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" } });
}
ルートモジュール
app/root.tsx は必須の唯一のルートです。ドキュメントシェルを管理し、<Meta />、<Links />、<Outlet />、<ScrollRestoration />、<Scripts />、および(開発時は)<LiveReload /> をレンダリングする必要があります。references/root.md を参照してください。
リソースルート
default エクスポートのないルートモジュールは リソースルート です。生の Response オブジェクト(PDF、JSON、RSS、Webhook)を返します。親のローダーは実行されません。また、実際のドキュメントリクエストを発生させるには、<Link> に reloadDocument を指定するか、<a> に置き換える必要があります。references/resource-routes.md を参照してください。
判断の分岐(順序に沿った判断)
順番に答えてください。合格は条件が真であることを意味します。同じ行にある回答を選び、そこで終了してください。
レイアウトとフラット URL
- この階層に共通の UI(ナビゲーション、パンくずリスト、サイドバー)はありますか?
- 不合格 → プレーンなドット区切りのセグメント(
about.tsx、pricing.tsx)を使用します。レイアウトモジュールは不要です。終了。 - 合格 → ステップ 1。
- 不合格 → プレーンなドット区切りのセグメント(
- この URL は親パスと UI(ナビゲーション、パンくずリスト、サイドバー)を共有しますか?
- 合格 → ドット区切りのネストを使用します(
concerts.tsxの下にconcerts.$city.tsx)。終了。 - 不合格 → ステップ 2。
- 合格 → ドット区切りのネストを使用します(
- URL は単にネストしているだけで、スタンドアロンとしてレンダリングする必要がありますか?
- 合格 → 末尾にアンダースコアを付けます(
concerts_.mine.tsx)。終了。 - 不合格 → ステップ 3。
- 合格 → 末尾にアンダースコアを付けます(
- 親の URL セグメントなしで、ラッパーレイアウトが必要ですか?
- 合格 → 単一のアンダースコアを使ったパスレスの親を使用します(
_auth.tsx+_auth.login.tsx)。終了。
- 合格 → 単一のアンダースコアを使ったパスレスの親を使用します(
UI ルートとリソースルート
- この URL はユーザー向けの HTML をレンダリングすることがありますか?
- 合格 →
defaultコンポーネントをエクスポートします。UI ルートです。終了。 - 不合格 → ステップ 2。
- 合格 →
- JSON、PDF、RSS、サイトマップ、Webhook、またはその他の生の
Responseを返しますか?- 合格 →
defaultエクスポートを省略すると、モジュールはリソースルートになります。これを指す<Link>にはreloadDocumentを使用します。終了。
- 合格 →
_index.tsx と index.tsx
- flat-routes を使う Remix v2 ですか?
- 合格 →
_index.tsx(先頭にアンダースコア)を使用します。終了。 - 不合格 → ステップ 2 — v1 を使用しているか、v1 のフォールバックアダプターを使用しています。
- 合格 →
- v1 のネストされたフォルダーツリーを維持する必要がありますか?
- 合格 →
@remix-run/v1-route-conventionをインストールし、remix.config.jsで設定します。references/v1-migration.md を参照してください。終了。
- 合格 →
追加ドキュメント
- 規則:完全なファイル名の文法(
_index、_layout、$param、スプラット、オプショナル、エスケープ、フォルダー +route.tsx、末尾のアンダースコア)については、references/conventions.md を参照してください。 - ルートモジュール:
root.tsxのスキャフォールドと必須のドキュメント要素については、references/root.md を参照してください。 - リソースルート:HTML 以外のレスポンス、
defaultエクスポートを使用しないルール、親ローダーのスキップ、reloadDocumentについては、references/resource-routes.md を参照してください。 - v1 から v2 への移行:v1 からの変更点(
__doubleアンダースコアのフォルダー、index.tsx、@remix-run/v1-route-convention、ignoredRouteFiles)については、references/v1-migration.md を参照してください。
v1 と v2 の規則の比較
| 懸案事項 | v1(ネストされたフォルダー) | v2(フラットルート) |
|---|---|---|
| インデックスルート | index.tsx | _index.tsx |
| パスレスレイアウト | __auth/(アンダースコア2つ) | _auth.tsx(1つ) |
| ネストされた URL | フォルダー階層 | ファイル名内のドット区切り |
| 動的セグメント | $param.tsx | $param.tsx(変更なし) |
| スプラット | $.tsx | $.tsx(変更なし) |
| リテラルのエスケープ | 該当なし | [.]、[] の角括弧 |
| レイアウトの適用除外 | フォルダーの外に移動 | 末尾の _(foo_.bar.tsx) |
| 同じ場所への配置 | フォルダー内の隣接ファイル | feature/route.tsx + 同階層のファイル |
| フォールバックアダプター | 該当なし | @remix-run/v1-route-convention |