React Forum
Blog & Guide Responses
Feb 16, 2023 · 04:00 PM

[Guide] Build a File-Based CMS (File Routes as Content Map) - implementation notes

Started by Talia Moss
guidecmsfile routescontent mapeditor motionoverridesroute docsderived previewsevidence

The File-Based CMS guide makes a strong claim: treat file routes as the content map, and treat editing as component-route motion layered on top. The part I keep thinking about is how it forces you to be explicit about overrides, preview derivation, and what your editor route doc is actually allowed to mutate.

Read the guide

How are you mapping file routes to stable content identity (path-only, frontmatter id, or a separate manifest)? Where did you put "override" logic so it's narratable: in the route doc, in a vault, or inside an editor hook? Did you store derived previews, and if so what is your invalidation story when a file changes shape? How do you keep the editor motion calm when the file tree itself is changing underneath you?

Comments (20)

Back to latest
Arman Qadir
Feb 21, 2023 · 06:20 AM

We tried path-only identity and it was fine until we started renaming folders.

The guide's "content map" idea worked better once we introduced a manifest that assigns ids, and file routes become the *index* rather than the id.

Mei Tan
Feb 24, 2023 · 09:10 AM

Overrides belong in the route doc for us, because that's the only place we can attach evidence without leaking editor concerns into every component.

We render data-content-id + data-override-source on the shell, and support can tell what's happening from a screenshot.

Evan Brooks
Mar 02, 2023 · 04:40 AM

We logged override resolution the same way we log routing posture. It made PR reviews calmer because people could see what changed in behavior, not just code:

txt
[tips] contentMap path=/docs/intro.mdx contentId=doc:intro
[tips] overrideResolve winner=draft loser=published reason=editor:activeDraft
[tips] previewDerive key=doc:intro rev=42 reason=file:edit

If your CMS can't narrate overrides, it will feel haunted.

Nina Kapoor
Mar 06, 2023 · 04:12 PM

Concrete alternative: keep file routes as content map, but do *not* store derived previews.

We derive previews on demand and cache at the boundary, because stored previews kept going stale when editors toggled overrides quickly.

Talia Moss
Mar 07, 2023 · 06:30 PM

That matches my concern too. The guide's "store derived previews" is appealing for debuggability, but it can become a second system.

Did you still store an evidence key (hash/rev) so the UI can show what preview it's rendering?

Nina Kapoor
Mar 08, 2023 · 08:05 AM

Yep: we store a preview evidence token (hash + source), not the preview itself.

Support reads the token from the UI; devs re-derive if needed.

Liam Osei
Mar 19, 2023 · 11:30 AM

The best part of file routes as content map is it makes navigation predictable. The worst part is it makes you confront what "move content" means.

Once content identity is explicit, all the messy product questions show up: do links follow id? do routes redirect? does the editor keep selection when a node moves?

Marisol Vega
Apr 02, 2023 · 05:20 AM

We used a small route doc to standardize the mapping and keep it testable:

ts
type ContentRef = { contentId: string; path: string; kind: 'page' | 'snippet' };

export function mapFileRouteToContent(path: string): ContentRef {
  const normalized = path.replace(/\\/g, "/");
  if (normalized.startsWith("/snippets/")) return { kind: "snippet", contentId: `sn:${normalized}`, path: normalized };
  return { kind: "page", contentId: `pg:${normalized}`, path: normalized };
}

Then the editor motion is just "edit ContentRef" instead of "edit whatever route I'm on".

Grace McCoy
Apr 16, 2023 · 10:40 AM

We anchored our CMS implementation on the docs routing vocabulary.

Related: Routing and Navigation and Architecture Guides.

Rafael Soto
May 07, 2023 · 07:07 AM

Long-form: overrides are basically "shadow routes". If you don't treat them as first-class, you're going to debug them forever.

We ended up with a rule: any override must declare (1) source, (2) scope, (3) precedence, and (4) evidence. If you can't write those down, you can't ship the override.

The editor route doc is where we keep those declarations, because it's the one place that can see both file-route identity and editor motion.

Tomas Rivera
May 22, 2023 · 10:10 PM

We got burned by previews derived from partially invalid content (missing fields).

The fix was to give preview derivation a lane (ok/error) and render that lane as evidence so editors knew when a preview was a guess.

Dmitri Kline
Jun 10, 2023 · 09:30 AM

If file tree changes are frequent, editor motion needs to be resilient. We used a simple pattern: editor targets the content id, not the route path.

When a node moves, routing re-resolves the path for that id and navigates. It feels like over-engineering until your first big refactor.

Amina Farouk
Jul 02, 2023 · 12:00 PM

One detail from the guide I liked: treat preview output as a derived artifact with a revision.

Even if you don't store the preview body, storing the revision gives you a stable narrative when something looks wrong.

Priya Liao
Jul 22, 2023 · 05:15 AM

We do keep stored previews, but only for chrome-level summaries (card title + excerpt + status).

Full preview renders are still derived on demand, because stored HTML previews created too many invalidation paths.

Jasper Nguyen
Aug 13, 2023 · 01:13 PM

Long-form counterpoint: file routes as content map is great until you need non-tree relationships (tags, relations, backlinks).

We kept file routes as the spine, but we built a relational index as a vault. The vault is computed from the file tree and treated as the contract surface for queries.

Rowan Pierce
Sep 04, 2023 · 04:50 AM

I think the guide implicitly assumes that authors only edit one thing at a time.

As soon as you need multi-file edits, the editor route doc becomes a transaction boundary, and you need a log story for it.

Harper Iqbal
Sep 19, 2023 · 07:19 PM

We added a "content map" debug panel that prints the resolution chain for the current page:

txt
[tips] resolve path=/docs/intro -> contentId=doc:intro
[tips] source published rev=41
[tips] source draft rev=42 (active) precedence=draft

It made onboarding new editors way easier because they could see *why* they were seeing what they were seeing.

Benji Rios
Oct 12, 2023 · 12:30 PM

If you're adopting this, invest in route-flow tests. It's the only way to keep the editor motion sane as you add features.

We assert on evidence lines rather than internal state. It's boring, but stable.

Kaitlyn Wells
Nov 03, 2023 · 03:03 AM

We had to decide whether overrides should be allowed to change routing itself (new paths). We said no.

Overrides can change content fields, but not the map. If overrides change the map, you lose the whole benefit.

Soren Malik
Nov 21, 2023 · 11:11 AM

A tiny thing that mattered: normalize paths and render normalized path as evidence.

Half the bugs were "works on Windows, broken on CI" because of separators and casing.

Lina Ortega
Dec 13, 2023 · 04:16 PM

If you lean into the guide, make your content map observable. You'll never regret it.

The moment you ship editor motion without observability, you're basically waiting to debug a ghost story.

Salma Qureshi
Dec 29, 2023 · 09:45 AM

My takeaway: treat "file routes" as a product contract, not an implementation detail.

Once you do, everything else (overrides, previews, editor motion) becomes explainable instead of magical.