The problem: prove variables, tags, validation, and partials work together
Lessons 7-12 each covered one mechanism in isolation. Real docs pages combine several at once: a conditional beta banner (variables + {% if %}), a validated callout (Lesson 9-10's callout tag), and a shared footer (Lesson 11's partials). This checkpoint has no new API, it builds a tiny 3-page doc set using all of them, including one page written to fail validation on purpose.
The code, piece by piece
const PAGES = { "index.md": `... {% if $showBetaBanner %} {% callout type="warning" %}...{% /callout %} {% /if %} ...`, "install.md": `... {% callout type="info" %}...{% /callout %} ...`, "broken.md": `... {% callout type="urgent" %}...{% /callout %} ...`,};Three pages: one with a conditional callout gated by a variable, one with a valid callout, and one (broken.md) with a deliberately invalid type="urgent", matching Lesson 10's validation error exactly, so it shows up in this lesson's summary instead of being hidden.
function buildConfig() { const footerAst = Markdoc.parse(footerSource); return { tags: { callout }, variables: { showBetaBanner: true }, partials: { "partials/footer.md": footerAst }, };}One config, built once, reused for all three pages, combining Lesson 7's variables, Lesson 9's tags, and Lesson 11's partials in a single object, exactly the shape a real site's shared config module would take (this is also what Lesson 15 splits across multiple files as the config keeps growing).
const errors = Markdoc.validate(ast, config).filter((e) => e.error.level === "error");Validating every page before writing its output, filtered to level: "error" specifically, mirrors a real CI check: warn-level issues might be acceptable to ship, error-level ones shouldn't be.
Checkpoint
- One shared config, many pages: variables, tags, and partials all live on the same
configobject, built once and reused, the pattern every real Markdoc site follows. - Validation runs per page, independently of rendering: a page with errors still renders (
broken.mdstill produced HTML), but the error-count summary is what a CI gate would act on. - Partials compose with everything else: the footer partial renders identically on every page regardless of what variables or tags that page also used, it's just one more thing
transformresolves.
You've finished the Intermediate tier: variables, functions, custom tags, validation, partials, and nesting. The Advanced tier covers overriding Markdoc's own built-in Markdown rendering, composing configs at scale, targeting React instead of HTML, and a comparison to MDX.