Skip to content

Tags

Tags are a syntactic extension of standard Markdown. You can use native Markdoc tags, like tables, conditionals, and partials, or create custom React components.

Similar to React components and HTML elements, tags are composable, and you can customize them with attributes.

{% if true %}

{% callout type="note" %}
Tags are composable!
{% /callout %}

{% else /%}

{% callout type="warning" %}
Tags aren't composable!
{% /callout %}

{% /if %}

Create a custom tag

To extend Markdoc with a custom tag, first, create a tag definition. In this example, you're creating a callout tag:

// ./schema/Callout.markdoc.js

export const callout = {
  render: 'Callout',
  description: 'Display the enclosed content in a callout box',
  children: ['paragraph', 'tag', 'list'],
  attributes: {
    type: {
      type: String,
      default: 'note',
      matches: ['caution', 'check', 'note', 'warning'],
      errorLevel: 'critical',
      description:
        'Controls the color and icon of the callout. Can be: "caution", "check", "note", "warning"'
    },
    title: {
      type: String,
      description: 'The title displayed at the top of the callout'
    }
  }
};

Then, pass the tag definition to your Config object:

import { callout } from './schema/Callout.markdoc';

const config = {
  tags: {
    callout
  }
};

const doc = `
# My first custom tag
`;

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

const children = Markdoc.renderers.react(content, React, { components });

Next, pass your content to the Markdoc renderer. If you want to render a React component, specify which component should render this type of tag in the components mapping.

import * as React from 'react';
import { Icon } from './Icon';

function Callout({ title, icon, children }) {
  return (
    <div className="callout">
      <div className="content">
        <Icon icon={icon} />
        <div className="copy">
          <span className="title">{title}</span>
          <span>{children}</span>
        </div>
      </div>
    </div>
  );
}

return Markdoc.renderers.react(content, React, {
  components: {
    // The key here is the same string as `tag` in the previous step
    Callout: Callout
  }
});

Now you can use your custom tag in your Markdoc content.

{% callout title="Hey you!" icon="note" %}
I have a message for you
{% /callout %}
Information Circle
Hey you!

I have a message for you

Options

These are the optional fields you can use to customize your Tag:

OptionTypeDescription
renderstringName of the output (for example, HTML tag, React component name) to render
childrenstring[]Specifies which tag or node types can be rendered as children of this tag. Used in schema validation.
attributes{ [string]: SchemaAttribute }Specifies which values (and their types) can be passed to this tag.
transform
(Ast.Node, ?Options) =>
  | RenderableTreeNode
  | RenderableTreeNode[]
  | null
Customize the Markdoc transform function for this tag, returning the custom output you want to eventually render. This is called during the transform step.
validate
(Node, ?Options) => ValidationError[];
Extend Markdoc validation. Used to validate that the content meets validation requirements. This is called during the validate step
selfClosingbooleanSpecifies whether a tag can contain children (false) or not (true). Used in schema validation.

Built-in tags

Markdoc comes out-of-the-box with four built-in tags: if, else, table, and partial.

If/Else

Dynamically render content when specific conditions are met using the {% if %} and {% else /%} tags. Markdoc uses conditionals with variables and functions.

Warning

Unlike JavaScript, Markdoc only considers undefined, null, and false to be falsey.

Use the if tag to render content when a condition evaluates to true.

This is shown no matter what.

{% if $myFunVar %}
Only appear if $myFunVar!
{% /if %}

Use the else tag to render alternate content when the if condition isn't met. You can use multiple else statements, and the final else tag triggers when none of the other conditions are met.

{% if $myFunVar %}
Only appear if $myFunVar!
{% else /%}
This appears if not $myFunVar!
{% /if %}

{% if $myFunVar %}
Only appear if $myFunVar!
{% else $otherFunVar /%}
This appears if not $myFunVar and $otherFunVar!
{% else /%}
This appears if not $myFunVar and not $otherFunVar
{% /if %}

Table

While Markdoc supports CommonMark tables, it also supports a list based syntax that allows for easy injection of rich content, like bulleted lists and code samples.

Basic table

A basic Markdoc table uses list syntax with each row separated by three dashes ---.

{% table %}
* Heading 1
* Heading 2
---
* Row 1 Cell 1
* Row 1 Cell 2
---
* Row 2 Cell 1
* Row 2 cell 2
{% /table %}

Table with rich content

Markdoc tables support rich text, including code samples and lists.

{% table %}
* Foo
* Bar
* Baz
---
*
  ```
  puts "Some code here."
  ```
*
  {% list type="checkmark" %}
  * Bulleted list in table
  * Second item in bulleted list
  {% /list %}
* Text in a table
---
*
  A "loose" list with

  multiple line items
* Test 2
* Test 3
---
* Test 1
* A cell that spans two columns {% colspan=2 %}
{% /table %}

Table without headings

{% table %}
---
* foo
* bar
---
* foo
* bar
{% /table %}

Set column and row span

Explicitly set column and row span.

{% table %}
---
* foo
* bar
---
* foo {% colspan=2 %}
{% /table %}

Text alignment

{% table %}
* Column 1 {% align="center" %}
* Column 2
* Column 3 {% align="right" %}
---
* foo
* bar
* baz
---
* foo
* bar {% align="right" %}
* baz
---
* foo {% align="center" %}
* bar
* baz
{% /table %}

Table caveats

Markdoc uses the table tag to locate places to parse the Markdown list syntax as a table, but it uses the table node to render the actual table elements. To customize how the default table renders, you need to register a custom a table node.

import { nodes } from '@markdoc/markdoc';

const config = {
  nodes: {
    table: {
      ...nodes.table,
      render: 'Table' // your custom component goes here
    }
  }
};

Partial

Markdoc uses partials to reuse content across docs. The content is stored in a separate markdown file, and it's referenced from the file attribute in the partial tag, which includes the corresponding piece of content.

Here is an example of including the header.md file as a partial.

{% partial file="header.md" /%}

For more information on partials, check out the full partials docs.

Next steps