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.
Symptom
Section titled “Symptom”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"indocs.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 athttps://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.
Correct the links
Section titled “Correct the links”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:
See the [Quickstart](/docs/quickstart) and [site and base](/docs/site/addressing).A quick way to find offenders on a /docs site:
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.
Check them mechanically
Section titled “Check them mechanically”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
.mdxfile undercontent/and builds the set of page ids from the file paths; - matches both link forms MDX has: markdown destinations
](/path)and JSX attributeshref="/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:
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.
Verify
Section titled “Verify”Run the check, then build and look at the emitted HTML for the link you fixed:
node scripts/check-links.mjsreed build --out distgrep -o 'href=./docs/quickstart.' dist/index.html | head -3Then confirm on the deployed site that the target answers 200, not 404:
curl -s -o /dev/null -w "%{http_code}\n" https://example.com/docs/quickstartreed dev is not proof either way — it serves on its own origin and port, so test the
links against a real build.
Related
Section titled “Related”- Links — the authoring rule in the page reference
- site and base — both link rules side by side
- How to link documentation pages effectively
- Redirects — when a page moves rather than a link breaking