Skip to content

Fix links that work in reed dev but 404 in production

Reed never rewrites links in prose or component hrefs. On a site with a base, you write the prefix yourself.

A link inside a page points at the wrong URL on the deployed site. The page exists, the build passed, reed check passed — and clicking the link lands on a 404. Typically:

  • the site sets base: "/docs" in docs.json;
  • a page contains [Quickstart](/quickstart) or <Card href="/quickstart">;
  • the deployed link goes to https://example.com/quickstart, which does not exist, because the page lives at https://example.com/docs/quickstart.

There is no warning at any stage. Nothing in reed check or reed build inspects the targets of links you write in MDX.

Reed does not rewrite links you write in prose, and it does not rewrite href attributes on components. A link target reaches the HTML exactly as you typed it.

base prefixes the links Reed builds itself — the sidebar, asset references, the redirect pages — but a link inside your MDX is yours. On a site with base: "/docs", every internal link has to be written /docs/<pageId>. Writing /quickstart compiles clean and 404s.

Links inside docs.json are split. Footer column links (and the footer status link), redirects and head.link entries are root-relative without the base, and Reed adds it. Writing /docs/quickstart there produces /docs/docs/quickstart. Navbar links are the exception: Reed passes navbar-links URLs through verbatim, so a root-relative navbar link has to include the base, exactly like a link in your MDX.

Where you write the link What you write on a base: "/docs" site
Page prose: [text](...) /docs/quickstart
Component attribute: href="..." /docs/quickstart
docs.json: navbar-links /docs/quickstart
docs.json: footer columns, redirects, head.link /quickstart

If you moved a site from no base to base: "/docs", every root-relative link in your content broke at once, silently.

Add the base prefix to every internal link in your MDX. The path after the prefix is the page id — the same string you list in navigation:

mdx
See the [Quickstart](/docs/quickstart) and [site and base](/docs/site/addressing).

A quick way to find offenders on a /docs site:

sh
grep -rnE '\]\(/|href=./' content --include='*.mdx' | grep -v '/docs/'

In docs.json, footer, redirect and head.link URLs that include the base should lose it; navbar links that lack it should gain it.

Reading links by eye does not scale, and the next one will slip through. Reed’s own documentation fails its build on this mistake with a short script, scripts/check-links.mjs, run before reed build. It:

  • walks every .mdx file under content/ and builds the set of page ids from the file paths;
  • matches both link forms MDX has: markdown destinations ](/path) and JSX attributes href="/path" (checking only the first would leave every <Card> link unvalidated);
  • reports any root-relative link that does not start with /docs/, and any /docs/... link whose page id does not exist;
  • scans fenced code blocks too, because readers copy from them — only inline code spans are skipped, so a page can still show a wrong link as a counter-example;
  • exits non-zero on the first problem set, so it can gate a build.

It runs as the first step of scripts/build.sh:

sh
echo "==> check content links"
node "$REPO_ROOT/scripts/check-links.mjs"

Copy the idea into your own project: set the base constant to yours, and run it in CI before reed build. A failing link check costs seconds; a 404 in production costs readers.

Absolute http(s) URLs are passed through verbatim everywhere and need no prefix. Avoid relative links such as ./sibling — Reed does not resolve them against the source file, so they resolve against whatever URL the browser is on.

Run the check, then build and look at the emitted HTML for the link you fixed:

sh
node scripts/check-links.mjs
reed build --out dist
grep -o 'href=./docs/quickstart.' dist/index.html | head -3

Then confirm on the deployed site that the target answers 200, not 404:

sh
curl -s -o /dev/null -w "%{http_code}\n" https://example.com/docs/quickstart

reed dev is not proof either way — it serves on its own origin and port, so test the links against a real build.