Problem
The fumadocs-mdx migration (#6037) moved docs content to apps/loopover-ui/content/docs/*.mdx, but every page still carries a hand-rolled TanStack route file (src/routes/docs.<slug>.tsx) — 49 of them as of #8114's page — that are near-identical boilerplate: a loader calling getDocPage({ data: { slugs: [<slug>] } }), head/og meta duplicating the .mdx frontmatter's own title/description, and the same DocsPage + Suspense/LoadingState component body. Adding a docs page today means touching 5+ files and regenerating the route tree; two tests pin the route-file count and must be bumped every time.
Area
apps/loopover-ui/src/routes/docs.*.tsx (all of them), src/lib/docs-source.functions.ts, docs-nav.tsx.
Proposal
- Replace the per-page thin routes with ONE dynamic route (
docs.$slug.tsx): loader resolves getDocPage({ data: { slugs: [params.slug] } }), throws notFound() exactly as the per-page loaders do, and head() derives title/description/og/canonical from the loader data (the frontmatter already carries title + description — today's route files just duplicate it by hand).
- Move the one genuinely per-page datum — the
eyebrow group label — into MDX frontmatter (or derive it from docs-nav.tsx's existing group structure, which already maps every slug to a group title; pick ONE source, don't keep both).
- Delete the ~49 per-page route files and retire the two count-pinning assertions (
docs-source-server-isolation.test.ts, docs-routes-loading-state.test.tsx) in favor of asserting the single dynamic route's shape.
- Keep
docs.index.tsx (a real page, not boilerplate) and any docs route that does more than render its MDX — audit for genuinely custom routes before deleting (e.g. pages importing extra interactive components at the ROUTE level rather than inside the MDX).
- After this, adding a docs page = adding one
.mdx file + one nav entry.
Deliverables
Test Coverage Requirements
The touched paths are under apps/**, outside coverage.include, so Codecov does not gate this patch — the existing docs-route test suites (isolation, loading-state, eager-scope) must still pass restructured, and a rendering parity check (same title/description/eyebrow per slug before vs after) is the real acceptance test.
Expected Outcome
One dynamic route serves every MDX docs page; adding a page stops requiring route boilerplate, route-tree regeneration, or test-count bumps.
Links & Resources
Boundaries
Rendering parity is the bar — no visual or meta regressions on any existing page. docs.index.tsx and any genuinely-custom route stay. No fumadocs-ui component shell (the headless posture from #6037 stands).
maintainer-only for now — route-architecture change across the whole docs surface; can be unlocked once scoped against a verified route audit.
Problem
The fumadocs-mdx migration (#6037) moved docs content to
apps/loopover-ui/content/docs/*.mdx, but every page still carries a hand-rolled TanStack route file (src/routes/docs.<slug>.tsx) — 49 of them as of #8114's page — that are near-identical boilerplate: a loader callinggetDocPage({ data: { slugs: [<slug>] } }), head/og meta duplicating the.mdxfrontmatter's own title/description, and the sameDocsPage+Suspense/LoadingStatecomponent body. Adding a docs page today means touching 5+ files and regenerating the route tree; two tests pin the route-file count and must be bumped every time.Area
apps/loopover-ui/src/routes/docs.*.tsx(all of them),src/lib/docs-source.functions.ts,docs-nav.tsx.Proposal
docs.$slug.tsx): loader resolvesgetDocPage({ data: { slugs: [params.slug] } }), throwsnotFound()exactly as the per-page loaders do, andhead()derives title/description/og/canonical from the loader data (the frontmatter already carries title + description — today's route files just duplicate it by hand).eyebrowgroup label — into MDX frontmatter (or derive it fromdocs-nav.tsx's existing group structure, which already maps every slug to a group title; pick ONE source, don't keep both).docs-source-server-isolation.test.ts,docs-routes-loading-state.test.tsx) in favor of asserting the single dynamic route's shape.docs.index.tsx(a real page, not boilerplate) and any docs route that does more than render its MDX — audit for genuinely custom routes before deleting (e.g. pages importing extra interactive components at the ROUTE level rather than inside the MDX)..mdxfile + one nav entry.Deliverables
docs.$slug.tsxdynamic route with loader + derived head meta + notFound handling.docs.*.tsxboilerplate files deleted; route tree regenerated; count-pinning tests replaced with shape assertions on the dynamic route.Test Coverage Requirements
The touched paths are under
apps/**, outside coverage.include, so Codecov does not gate this patch — the existing docs-route test suites (isolation, loading-state, eager-scope) must still pass restructured, and a rendering parity check (same title/description/eyebrow per slug before vs after) is the real acceptance test.Expected Outcome
One dynamic route serves every MDX docs page; adding a page stops requiring route boilerplate, route-tree regeneration, or test-count bumps.
Links & Resources
apps/loopover-ui/src/routes/docs.ai-summaries.tsx(canonical example of the boilerplate being removed)apps/loopover-ui/src/lib/docs-source.functions.ts,apps/loopover-ui/src/components/site/docs-nav.tsxBoundaries
Rendering parity is the bar — no visual or meta regressions on any existing page.
docs.index.tsxand any genuinely-custom route stay. No fumadocs-ui component shell (the headless posture from #6037 stands).maintainer-only for now — route-architecture change across the whole docs surface; can be unlocked once scoped against a verified route audit.