The problem: which one should you actually reach for?

Markdoc isn't the only way to add components to Markdown. MDX is the other well-known option: Markdown that can contain literal JSX, compiled straight into a React component. They look superficially similar (both let you write a component tag or a Markdoc tag inside a Markdown-ish file), but they solve the problem very differently, and that difference matters most around one question: who is allowed to author content?

This course doesn't install an MDX compiler (@mdx-js/mdx) just for this comparison, mixing an "arbitrary code execution by design" tool into a learning repo isn't worth it for one lesson. Instead, this lesson runs a concrete Markdoc check that has no MDX equivalent, and compares the rest as a table.

The code, piece by piece

const untrustedInput = "{% callout type=\"<script>alert(1)</script>\" %}";
const errors = Markdoc.validate(ast, config);

This simulates an untrusted author (a CMS contributor, a community PR) submitting a suspicious-looking attribute value. Markdoc.validate never executes anything, it's a pure data check against callout's matches schema (Lesson 10), and it rejects the value because the script-tag string isn't "info", "warning", or "error", the string is just data, whether or not it looks like a script tag doesn't matter to the check.

MDX has no equivalent point in its pipeline: an .mdx file's embedded JSX and expressions are the content and are code, there's no "validate the data before running anything" step, because there's no separation between content and code to validate.

Markdoc vs. MDX

  • Output shape: Markdoc produces data, a plain render tree (Tag objects + strings). MDX produces code, a compiled React component with arbitrary JS included.
  • Untrusted authors: Markdoc is safe by default, tags are schema-checked, no arbitrary JS execution. MDX is risky, embedded JSX/expressions run as real code at build/render time.
  • Validation before render: Markdoc has Markdoc.validate(), checking attributes/structure with zero execution. MDX has no built-in equivalent, invalid JSX is a compile error, not a content error.
  • Multiple output targets: Markdoc can target the same render tree to HTML (Lesson 4) or React (Lesson 16). MDX compiles straight to one target, usually a React/JS component.
  • Learning curve: Markdoc is a small tag/attribute syntax on top of Markdown. MDX requires Markdown syntax plus real JSX/JS knowledge to author pages.

Neither is strictly better. If every author on a docs site is a trusted engineer who wants full component power and doesn't mind coupling content to React specifically, MDX's directness is a real advantage. If content comes from many authors, non-engineers, a CMS, or community contributions, and you want to validate or target multiple output formats from one source, Markdoc's schema-checked, data-first design is the better fit, which is exactly why Stripe (many external doc contributors, strict review requirements) built and uses it.

Checkpoint

  • Markdoc is data, MDX is code: a Markdoc render tree is inspectable, serializable, and safe to build from untrusted input; MDX compiles content directly into executable JavaScript.
  • Validation is Markdoc's structural advantage: Markdoc.validate() catches bad content before render, with zero code execution, MDX has no equivalent, because there's no content/code separation to check.
  • Choose based on who authors the content: many/untrusted/non-engineer authors favor Markdoc; a small trusted engineering team wanting full component power favors MDX.

If anything here still feels unclear, ask before moving to Lesson 18, this course's capstone project.