关于 remix-v2-routing
面向 Remix v2 开发者的路由技能,说明 flat-routes 文件命名、嵌套路由及资源路由规则。通过决策步骤帮助选择布局、URL 结构和路由类型。
主要功能
- 01解释 flat-routes 文件命名规则
- 02配置嵌套与无路径布局
- 03处理动态片段与通配路径
- 04区分 UI 路由和资源路由
- 05提供 v1 到 v2 约定对照
适用场景
- 01规划共享导航的嵌套路由
- 02为登录页设置无路径布局
- 03创建动态 URL 或通配路径
- 04为 JSON、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。Splat 是单个美元符号:$.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
动态片段和 Splat
$name 捕获单个片段;$.tsx 捕获路径剩余部分。Loader 通过 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 值存放在 "*" 下——没有 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)。父级 loader 不会运行,并且必须在 <Link> 中使用 reloadDocument(或改用 <a>)才能触发真正的文档请求。请参阅 references/resource-routes.md。
判定步骤(决策顺序)
按顺序回答。通过表示条件为真;选择同一行的答案并停止。
布局还是扁平 URL
- 此层级是否存在任何共享界面元素(导航、面包屑、侧边栏)?
- 未通过 → 使用普通的点分隔片段(
about.tsx、pricing.tsx);无需布局模块。停止。 - 通过 → 第 1 步。
- 未通过 → 使用普通的点分隔片段(
- 此 URL 是否应与父路径共享界面(导航、面包屑、侧边栏)?
- 通过 → 使用点分隔嵌套(在
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、sitemap、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。停止。
- 通过 → 安装
其他文档
- 约定:请参阅 references/conventions.md,了解完整的文件名语法(
_index、_layout、$param、splat、可选、转义、文件夹 +route.tsx、尾随下划线)。 - 根模块:请参阅 references/root.md,了解
root.tsx脚手架和必需的文档元素。 - 资源路由:请参阅 references/resource-routes.md,了解非 HTML 响应、禁止导出
default的规则、跳过父级 loader,以及reloadDocument。 - v1 → v2 迁移:请参阅 references/v1-migration.md,了解与 v1 的差异(
__double下划线文件夹、index.tsx、@remix-run/v1-route-convention、ignoredRouteFiles)。
v1 与 v2 约定对比
| 关注点 | v1(嵌套文件夹) | v2(扁平路由) |
|---|---|---|
| 索引路由 | index.tsx | _index.tsx |
| 无路径布局 | __auth/(双下划线) | _auth.tsx(单下划线) |
| 嵌套 URL | 文件夹层级 | 文件名中的点分隔符 |
| 动态片段 | $param.tsx | $param.tsx(不变) |
| Splat | $.tsx | $.tsx(不变) |
| 转义字面量 | 不适用 | [.]、[] 方括号 |
| 退出布局 | 移出文件夹 | 尾随 _(foo_.bar.tsx) |
| 文件共置 | 文件夹中的相邻文件 | feature/route.tsx + 同级文件 |
| 回退适配器 | 不适用 | @remix-run/v1-route-convention |