Skip to content

MDX pitfalls

The four failures that cost the most time, and what to do instead.

MDX parses a fenced code block inside a JSX element differently from one at the top level. The failure is confusing because the same block works one line higher up.

Keep fences at the top level:

mdx
<Steps>
<Step title="Install">Add the dependency.</Step>
<Step title="Build">Run the build.</Step>
</Steps>

…with the command as its own top-level block below:

sh
npm install

Rather than nesting the fence inside the <Step>. When a build fails on a page you only added a code block to, this is the first thing to check.

Your frontmatter title is the page’s H1. Writing another one produces a DS-A02 error, because the page would render two competing top-level headings and a screen reader would announce both.

Start body headings at ##. The same diagnostic catches skipped levels — ## straight to ####.

On a site with base set, Reed does not rewrite links you write in prose. /quickstart compiles cleanly and 404s in production; you have to write /docs/quickstart.

This applies to component attributes too — a <Card href="/quickstart"> is wrong in exactly the same way, and is easier to miss because it does not look like a link.

Links inside docs.json follow the opposite rule: Reed adds the prefix there.

Check this mechanically. Eyes do not catch it, and neither does reed check — validating prose links is outside what schema and contract checks cover.

A name outside the catalog is a DS-S06 error. Usually it is a typo, or a component carried over from another platform that Reed has no equivalent for.

The message distinguishes the two cases by telling you it is neither a catalog member nor locally imported. If you genuinely need something outside the catalog, a local import is the escape hatch — but check the catalog first, since the names differ from other platforms more often than the capabilities do.

Read the contract name in the message. It tells you which set of rules Reed judged your element against, which is usually the answer when a prop you are sure is valid is being rejected — the element was mapped through a dialect you did not expect. See Content dialect.