---
title: MDX pitfalls
description: The four failures that cost the most time, and what to do instead.
---

## Fenced code inside a JSX element

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.

## An H1 in the body

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
`####`.

## Root-relative links without the base

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.

## An unknown component

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.

## When a diagnostic looks wrong

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](/docs/authoring/dialect).
