The problem: one source document, many contexts
A docs site often needs to render the same page slightly differently depending on context: a staging banner that shouldn't appear in production, an SDK name that changes per language tab, a value pulled from frontmatter (Lesson 5 already used this mechanism without naming it). Rewriting the source file per context doesn't scale. Markdoc's answer is variables: {% $name %} references a value that's resolved at transform time, not baked into the source.
The code, piece by piece
const SOURCE = `...Environment: {% $env %}...{% if $showBanner %}...{% /if %}...Hello, {% $name %}.`;{% $env %}, {% $showBanner %}, and {% $name %} are all variable references, the $ prefix is what marks something as a variable reference instead of a literal tag name.
const ast = Markdoc.parse(SOURCE);const renderTree = Markdoc.transform(ast, { variables });Parsing happens once and doesn't touch variables at all, Markdoc.parse doesn't know or care that $env will later resolve to anything, it just records "there's a variable reference here." Resolution happens entirely in transform, against whatever config.variables you pass, which is why the same parsed Ast can be transformed twice with two different variable sets and produce two different render trees.
renderFor("staging", { env: "staging", showBanner: true, name: "Andrei" });renderFor("production", { env: "production", showBanner: false, name: "Andrei" });Same source, two calls, two different config.variables objects, two different HTML outputs. In a real site this is how one Markdoc source file backs multiple environments, locales, or audience segments.
Checkpoint
{% $name %}: a variable reference in Markdoc source, resolved againstconfig.variablesat transform time, not parse time.- One source, many renders: the same
Astcan be transformed repeatedly with differentvariables, producing different render trees and output each time, no re-parsing needed. {% if %}reacts structurally, not textually: a falsy variable makes the guarded block vanish from the render tree entirely, it isn't rendered-then-hidden, it's just never added, Lesson 9 covers the mechanics.
If anything here still feels unclear, ask before moving to Lesson 8.