Editing with MDX Components

Last modified: October 29th, 2023

Working with a specific static site generator?
Customize CloudCannon's documentation to suit your SSG.

Great! We'll show you documentation relevant to .
You can change this any time using the dropdown in the navigation bar.

CloudCannon supports embedding rich Snippets in Markdown content when using the CloudCannon Content Editor. Once configured, Snippets in your content will be presented as blocks in rich text views, with the ability to add them as Snippets via the toolbar:

The CloudCannon Content Editor toolbar with a cursor hovering over the "Snippet" button

To start configuring Components in your MDX content, a Snippet configuration must be imported using the _snippets_imports key in your CloudCannon global configuration file.

cloudcannon.config.yaml
copied
_snippets_imports:
  mdx: true
cloudcannon.config.json
copied
{
  "_snippets_imports": {
    "mdx": true
  }
}
cloudcannon.config.js
copied
module.exports = {
  _snippets_imports: {
    mdx: true
  }
};

Enabling snippets in the toolbar#

By default, CloudCannon will show the snippet toolbar action in the content editor if snippets are available.

If you have already customized which options are available via _editables in your CloudCannon config, you will need to include snippet: true for Snippets to be available. See the Editables options documentation for more details.

Custom MDX Components#

After importing the mdx Snippets configurations, any custom components in your content will be displayed in the Content Editor as an Unknown Component. This allows an editor to move and delete a Component, but prevents editing of its arguments or content:

A screenshot of an unknown Snippet displayed as a block in the CloudCannon Content Editor

To enable editing and provide the custom component in the toolbar, your custom component must be configured using the _snippets object in your CloudCannon global config file. CloudCannon Snippets can be built from scratch to support nearly any syntax or SSG, but importing a Snippet configuration provides a set of snippet templates for common use cases in MDX.

To help illustrate configuring custom Snippets, we will first cover a few examples. First, let's look a custom Tint component on our MDX site that takes a string and applies a color to it:

post.md
copied
# My Post

<Tint tint_color="#ff0000">This text should be red</Tint>

The first thing we need to do is decide which Snippet template to use. Looking at the list of snippet templates further down this page, we see that MDX has two templates mdx_component and mdx_paired_component. Our component has child content so we should configure this snippet using the mdx_paired_component template. A full example configuration for this Snippet thus might look like the following:

cloudcannon.config.yaml
copied
_snippets_imports:
  mdx: true
_snippets:
  mdx_tint:
    template: mdx_paired_component
    inline: true
    preview:
      text: Tint
    definitions:
      component_name: tint
      content_key: inner_text
      named_args:
        - editor_key: color
          source_key: tint_color
          type: string
    _inputs:
      tint_color:
        type: color
      inner_text:
        comment: This text will be tinted
cloudcannon.config.json
copied
{
  "_snippets_imports": {
    "mdx": true
  },
  "_snippets": {
    "mdx_tint": {
      "template": "mdx_paired_component",
      "inline": true,
      "preview": {
        "text": "Tint"
      },
      "definitions": {
        "component_name": "tint",
        "content_key": "inner_text",
        "named_args": [
          {
            "editor_key": "color",
            "source_key": "tint_color",
            "type": "string"
          }
        ]
      },
      "_inputs": {
        "tint_color": {
          "type": "color"
        },
        "inner_text": {
          "comment": "This text will be tinted"
        }
      }
    }
  }
}
cloudcannon.config.js
copied
module.exports = {
  _snippets_imports: {
    mdx: true
  },
  _snippets: {
    mdx_tint: {
      template: "mdx_paired_component",
      inline: true,
      preview: {
        text: "Tint"
      },
      definitions: {
        component_name: "tint",
        content_key: "inner_text",
        named_args: [
          {
            editor_key: "color",
            source_key: "tint_color",
            type: "string"
          }
        ]
      },
      _inputs: {
        tint_color: {
          type: "color"
        },
        inner_text: {
          comment: "This text will be tinted"
        }
      }
    }
  }
};

Each snippet definition lives under a top level key, mdx_tint in this example. This is the unique name that CloudCannon uses to identify this snippet, but is otherwise unused in the component configuration itself.

We specify the template that this snippet should inherit from, and also specify that it is an inline snippet, since it can live anywhere in the content (such as in the middle of a sentence).

In the preview object we configure how this snippet is displayed while editing, using the CloudCannon card preview options.

In definitions we need to specify some values that are required for the template we picked. For the mdx_paired_component template, we need to specify:

  • The component_name — in this case we're configuring our tint component.
  • The content_key — this controls the key that CloudCannon will use to edit the text in between the start and end tags of the component.
  • The named_args — for our tint component we need to configure that the tint_color should be captured with the key color

Finally, we can specify any other keys from the CloudCannon configuration cascade here. In this example, we configure the inputs for the keys that this snippet will generate. With that in place, we can now add and edit our tint component anywhere on our site.

A screenshot of the "tint" component in the Content Editor, with the editing panel open showing the snippet data

Next, let's quickly look at a component with a different syntax:

post.md
copied
# My Post

<Callout type="info" message="This is my shortcode"/>

This time, we have an unpaired component that takes type and message argument keys. This syntax matches the mdx_component template.

In CloudCannon, we could configure this component using the following global configuration:

cloudcannon.config.yaml
copied
_snippets:
  callout:
    template: mdx_component
    inline: false
    preview:
      text: Callout
    definitions:
      component_name: callout
      named_args:
        - source_key: type
          editor_key: label
          type: string
        - editor_key: message
          type: string
    _inputs:
      label:
        type: select
        options:
          values:
            - info
            - warning
            - error
cloudcannon.config.json
copied
{
  "_snippets": {
    "callout": {
      "template": "mdx_component",
      "inline": false,
      "preview": {
        "text": "Callout"
      },
      "definitions": {
        "component_name": "callout",
        "named_args": [
          {
            "source_key": "type",
            "editor_key": "label",
            "type": "string"
          },
          {
            "editor_key": "message",
            "type": "string"
          }
        ]
      },
      "_inputs": {
        "label": {
          "type": "select",
          "options": {
            "values": [
              "info",
              "warning",
              "error"
            ]
          }
        }
      }
    }
  }
}
cloudcannon.config.js
copied
module.exports = {
  _snippets: {
    callout: {
      template: "mdx_component",
      inline: false,
      preview: {
        text: "Callout"
      },
      definitions: {
        component_name: "callout",
        named_args: [
          {
            source_key: "type",
            editor_key: "label",
            type: "string"
          },
          {
            editor_key: "message",
            type: "string"
          }
        ]
      },
      _inputs: {
        label: {
          type: "select",
          options: {
            values: [
              "info",
              "warning",
              "error"
            ]
          }
        }
      }
    }
  }
};

This should now be familiar, but with a few changes:

  • We want this snippet to be a block-level element in the editor, so we set inline to false.
  • Since this component does not have an end tag, we have no content_key

In this example, we want the key in our component to be type, but tell CloudCannon to treat that value as the label key, which we then configure using our inputs configuration.

A screenshot of the "callout" component in the Content Editor, with the editing panel open showing the snippet data

Snippet options#

The following options are available for MDX component snippets:

template - String#

The template that this snippet should inherit, out of the available MDX Component Templates.

definitions - Object#

The variables required for the selected template.

inline - Boolean#

Whether this component can appear inline (within a sentence). Defaults to false, which will treat this component as a block-level element in the content editor.

preview - Object#

A preview definition for displaying this component in the CloudCannon editor. See the preview options documentation.

_inputs - Object#

Input configurations for the keys contained in this component. See the input configuration documentation.

MDX Component Templates#

The first step to configure your custom component is to identify which component template to use, as each component template requires a set of definitions keys to be configured. The following component templates are available:

Component with named arguments#

example.md
copied
<CustomComponent arg1="my arg1" arg2="my arg2"/>

Example config

cloudcannon.config.yaml
copied
_snippets:
  custom_snippet:
    template: mdx_component
    inline: false
    preview:
      text: My Custom Component
    definitions:
      component_name: CustomComponent
      named_args:
        - editor_key: arg1
          type: string
        - editor_key: arg2
          type: string
cloudcannon.config.json
copied
{
  "_snippets": {
    "custom_snippet": {
      "template": "mdx_component",
      "inline": false,
      "preview": {
        "text": "My Custom Component"
      },
      "definitions": {
        "component_name": "CustomComponent",
        "named_args": [
          {
            "editor_key": "arg1",
            "type": "string"
          },
          {
            "editor_key": "arg2",
            "type": "string"
          }
        ]
      }
    }
  }
}
cloudcannon.config.js
copied
module.exports = {
  _snippets: {
    custom_snippet: {
      template: "mdx_component",
      inline: false,
      preview: {
        text: "My Custom Component"
      },
      definitions: {
        component_name: "CustomComponent",
        named_args: [
          {
            editor_key: "arg1",
            type: "string"
          },
          {
            editor_key: "arg2",
            type: "string"
          }
        ]
      }
    }
  }
};

Definitions

component_name - String#

The name of your component, as used in your MDX content files.

named_args - Array of argument options#

A list of each key-value pair the shortcode takes.

Paired component with named arguments#

example.md
copied
<CustomComponent arg1="my arg1" arg2="my arg2"> content </CustomShortcode>

Example config

cloudcannon.config.yaml
copied
_snippets:
  custom_snippet:
    template: mdx_paired_component
    inline: false
    preview:
      text: My Custom Component
    definitions:
      component_name: CustomComponent
      content_key: custom_key
      named_args:
        - editor_key: arg1
          type: string
        - editor_key: arg2
          type: string
cloudcannon.config.json
copied
{
  "_snippets": {
    "custom_snippet": {
      "template": "mdx_paired_component",
      "inline": false,
      "preview": {
        "text": "My Custom Component"
      },
      "definitions": {
        "component_name": "CustomComponent",
        "content_key": "custom_key",
        "named_args": [
          {
            "editor_key": "arg1",
            "type": "string"
          },
          {
            "editor_key": "arg2",
            "type": "string"
          }
        ]
      }
    }
  }
}
cloudcannon.config.js
copied
module.exports = {
  _snippets: {
    custom_snippet: {
      template: "mdx_paired_component",
      inline: false,
      preview: {
        text: "My Custom Component"
      },
      definitions: {
        component_name: "CustomComponent",
        content_key: "custom_key",
        named_args: [
          {
            editor_key: "arg1",
            type: "string"
          },
          {
            editor_key: "arg2",
            type: "string"
          }
        ]
      }
    }
  }
};

Definitions

component_name - String#

The name of your shortcode, as used in your MDX content files.

content_key - String#

The key to use in the data panel for editing the inner contents of the component.

named_args - Array of argument options#

A list of each key-value pair the shortcode takes.

Argument options#

The following options are available for named argument option objects:

editor_key - String#

Determines the key that will be used for this argument in the CloudCannon data panel.

source_key - String#

Determines the key of the key-value pair as it appears in the shortcode.

Will default to the editor_key if unset.

default - Any#

The default value that should be used for this argument when creating a new snippet in the CloudCannon editor.

type - String#

Restrict this argument to parse as the specified type. Useful to ensure booleans get parsed as the boolean value, rather than a string such as "true".

One of:

  • string
  • boolean
  • number
  • array
optional - Boolean#

Whether this argument is required for the shortcode. If false, shortcodes in your templates missing this argument will not match this snippet definition.

Defaults to false.

remove_empty - Boolean#

Whether this argument should be omitted from the output shortcode if the value is empty.

Defaults to false.

allowed_values - Array of values#

A list of values that this argument must be in order to match this snippet definition. Allows you to match different usages of the same shortcode to separate snippet definitions based on the value of an argument.

Related Articles

Open in a new tab