Skip to content

Phases of rendering

Markdoc has three phases of rendering: parse, transform, and render. Each phase operates on the output of the previous phases.

Markdoc also includes a validate function, that you can run separately from the render phases to confirm the Markdoc document is valid.
See the validation docs for more info.

Parse

parse(string) => AstNode

Parse transforms a raw string into an abstract syntax tree (AST) representing your Markdoc document. The AST contains information about your content, including where each piece of content exists within the document.

An example AST would look like this:

{
  "type": "document",
  "attributes": {},
  "children": [
    {
      "type": "paragraph",
        "attributes": {},
        "children": [...],
        "lines": [0, 2],
        "location": {
          "start": {
            "line": 0
          },
          "end": {
            "line": 2
          }
        },
        "errors": [],
        "inline": false,
    }
  ],
  "lines": [],
  "errors": [],
  "inline": false
}

Check this out for yourself in the developer playground.

AST node instances also include helpful functions, like walk, which can be useful for traversing and mutating your AST.

const ast = Markdoc.parse(document);

for (const node of ast.walk()) {
  // do something with each node
}

Transform

transform(AstNode | AstNode[], ?Config) => RenderableTreeNode | RenderableTreeNode[]

Transform takes an abstract syntax tree and transforms it into a renderable tree, a serializable intermediate representation of what will eventually be rendered. This object is useful for computing things like a table of contents, or passing over the wire to your client.

The transform step is also responsible for resolving variables into static, scalar values (string, boolean, object, and so on.).

An example renderable tree will look like this:

{
  "name": "article",
  "attributes": {},
  "children": [
    {
      "name": "h1",
      "attributes": {},
      "children": ["Header"],
      "$$mdtag": true
    }
  ],
  "$$mdtag": true
}

You can see a more advanced renderable tree in the developer playground.

Render

Render takes in a renderable tree and converts it into rendered output. For html, that means creating an HTML document as a string. For react, it means creating a React element.

You can create your own renderer by creating a function that takes in a renderable tree as a parameter and returns your desired output.

An example html output would look like this:

<h1>Getting started</h1>

<p>Run this command to install the Markdoc library:</p>

React

renderers.react(RenderableTreeNode | RenderableTreeNode[]) => React.Node

Markdoc supports rendering React out-of-the-box. You can see the React renderer in action in the developer playground.


To render React, first create a renderable tree from your document calling transform. You can do this from the server or client.

const tags = {
  callout: {
    render: 'Callout',
    attributes: {}
  }
};

const doc = `
{% callout %}
Attention, over here!
{% /callout %}
`;

const ast = Markdoc.parse(doc);
const content = Markdoc.transform(ast, { tags });


Then, call Markdoc.renderers.react with the renderable tree from your client application. Along with content and React, you'll need to provide the components object as an argument. The components object specifies a mapping from your tags and nodes to the corresponding React component.

import Markdoc from '@markdoc/markdoc';
import React from 'react'; // or 'preact'

function Callout({ children }) {
  return <div className="callout">{children}</div>;
}

function MyApp() {
  return Markdoc.renderers.react(content, React, {
    components: {
      Callout: Callout
    }
  });
}

Rendered output

Information Circle

Attention, over here!

HTML

renderers.html(RenderableTreeNode | RenderableTreeNode[]) => string

Markdoc supports rendering HTML out-of-the-box. Because the HTML renderer outputs your HTML document as a string, you can use Web Components with no extra configuration.


To render HTML, first create a renderable tree from your content by calling transform:

const doc = `
# Getting started

Run this command to install the Markdoc library:
`;

const ast = Markdoc.parse(doc);
const content = Markdoc.transform(ast);


Then, call Markdoc.renderers.html with your renderable tree, which will create the corresponding HTML document.

const express = require('express');
const Markdoc = require('@markdoc/markdoc');

const app = express();

app.get('/docs/getting-started', (req, res) => {
  res.setHeader('Content-Type', 'text/html');
  res.send(`
    <!DOCTYPE html>
    <html>
      <body>
        ${Markdoc.renderers.html(content)}
      </body>
    </html>
  `);
});
<!DOCTYPE html>
<html>
  <body>
    <h1>Getting started</h1>
    <p>Run this command to install the Markdoc library:</p>
  </body>
</html>

Create your own

Because Markdoc renderers are regular functions, you can create a custom one for whatever your use case requires.
Try creating your own for Vue, Svelte, Spectacle, or anything else you can think of.

Next steps