Snippets Reference

Last modified: September 23rd, 2024

In some cases, you might want to configure a snippet that is more complicated than the snippet templates provided for your SSG allow. Alternatively, you might be working with a syntax that CloudCannon doesn't provide templates for and you want to create your own snippet templates. In these cases, CloudCannon's underlying snippet parser can be configured directly.

Configuring snippets directly is more complicated than using snippet templates, but unlocks significantly more potential.

Snippet syntax#

Custom snippets are configured under the _snippets key, and provide a snippet string rather than a template. If we were wanting to configure a custom snippet that matched bash variables like $HOME or $PATH, we might use the following config:

cloudcannon.config.yaml
copied
_snippets:
  variable:
    snippet: $[[var_name]]
    params:
      var_name:
        parser: argument
        options:
          model:
            editor_key: variable
          format:
            string_boundary:
              - ''

The snippet string contains the text to match for your snippet, with any dynamic sections represented using a placeholder in double square brackets. In this example, the $ will match a literal $ character, and [[var_name]] will use a specified parser.

For each placeholder, a matching key should be supplied inside the params object. Here, [[var_name]] maps to params.var_name.

Each param needs to specify a parser from the list of supported parsers. Here, the argument parser is used for matching a single value.

Each parser requires a different set of options. See the argument parser options for more details on this example.

cloudcannon.config.json
copied
{
  "_snippets": {
    "variable": {
      "snippet": "$[[var_name]]",
      "params": {
        "var_name": {
          "parser": "argument",
          "options": {
            "model": {
              "editor_key": "variable"
            },
            "format": {
              "string_boundary": [
                ""
              ]
            }
          }
        }
      }
    }
  }
}

The snippet string contains the text to match for your snippet, with any dynamic sections represented using a placeholder in double square brackets. In this example, the $ will match a literal $ character, and [[var_name]] will use a specified parser.

For each placeholder, a matching key should be supplied inside the params object. Here, [[var_name]] maps to params.var_name.

Each param needs to specify a parser from the list of supported parsers. Here, the argument parser is used for matching a single value.

Each parser requires a different set of options. See the argument parser options for more details on this example.

Parsers#

These are the parsers that a snippet can use:

argument#

Parses a single argument, optionally delimited by characters. Useful for matching a single positional argument.

{{<figure image.png>}}
          └───────┘

The argument parser is also used to parse a list of repeating arguments.

{{<images image1.png image2.png image3.png>}}
          └──────────────────────────────┘
argument_list#

Parses a list of distinct positional arguments based on their position.

{{<figure "image.png" "My image title">}}
          └──────────────────────────┘
key_values#

Parses repeating pairs of keys and values. Useful for most SSG snippets that take properties.

{% include "image.html" image: "tree.png" alt: "Image of a tree" %}
                        └──────────────────────────────────────┘

Can be configured to handle most syntax forms of key value pairs.

<Link href="/about/" new_tab={true}/>
      └───────────────────────────┘
content#

Parses rich multiline content, such as the content between paired tags. Can be configured to parse nested snippets within.

{% highlight "js" %} let a = b; {% endhighlight %}
                    └──────────┘
literal#

Parses an exact literal value. Mainly useful when configuring a snippet template.

repeating_literal#

Parses a repeating set of exact literal values.

optional#

Higher-order parser that wraps a snippet string and makes it optional.

repeating#

Higher-order parser that wraps a snippet string and allows it to repeat.

Argument parser#

Parses a single argument, optionally delimited by characters. Useful for matching a single positional argument.

Show exampleHide example

To illustrate the argument parser, we'll look at a custom video snippet that takes a single named argument:

page.md
copied
## My blog post

<<video "CZcNgDN81Sw">>

Morbi leo risus, porta ac consectetur ac, vestibulum at eros.
cloudcannon.config.yaml
copied
_snippets:
  video:
    snippet: <<video [[video_arg]]>>
    params:
      video_arg:
        parser: argument
        options:
          model:
            editor_key: video_id
          format:
            string_boundary:
              - '"'
cloudcannon.config.json
copied
{
  "_snippets": {
    "video": {
      "snippet": "<<video [[video_arg]]>>",
      "params": {
        "video_arg": {
          "parser": "argument",
          "options": {
            "model": {
              "editor_key": "video_id"
            },
            "format": {
              "string_boundary": [
                "\""
              ]
            }
          }
        }
      }
    }
  }
}

Options

model — object#

Defines the parsing configuration for this argument.

model.editor_key — string#

Specifies the key that a user in CloudCannon will see when editing this value. This key will also be used if you want to specify any input configuration using _inputs.

Show exampleHide example
cloudcannon.config.yaml
copied
parser: argument
options:
  model:
    editor_key: video_id
cloudcannon.config.json
copied
{
  "parser": "argument",
  "options": {
    "model": {
      "editor_key": "video_id"
    }
  }
}
model.allowed_values — array of strings#

A list of valid values for the parser. If specified, values not in the provided list will cause this snippet to be skipped, and a different snippet will be matched if possible. A user entering a different value while editing this snippet will cause it to error, so this option is best paired with a select input configured on the editor_key.

Show exampleHide example
cloudcannon.config.yaml
copied
parser: argument
options:
  model:
    editor_key: video_id
    allowed_values:
      - riXoAr6gO-E
      - CZcNgDN81Sw
      - 0iwNjcFIHNM
cloudcannon.config.json
copied
{
  "parser": "argument",
  "options": {
    "model": {
      "editor_key": "video_id",
      "allowed_values": [
        "riXoAr6gO-E",
        "CZcNgDN81Sw",
        "0iwNjcFIHNM"
      ]
    }
  }
}

Example usage:

page.md
copied
## My blog post

<<video "CZcNgDN81Sw">> 

<<video "riXoAr6gO-E">> 

<<video "RuvMHkfOpvg">> 

Morbi leo risus, porta ac consectetur ac, vestibulum at eros.

These IDs match a value in allowed_values, thus will be shown as a video snippet

This ID is not present in allowed_values and will not parse as a video snippet. It may still parse as another snippet, or will remain as plain text.

model.default — string#

The default value for this argument, which is used when adding a new snippet to a page in CloudCannon.

Show exampleHide example
cloudcannon.config.yaml
copied
parser: argument
options:
  model:
    editor_key: video_id
    default: riXoAr6gO-E
cloudcannon.config.json
copied
{
  "parser": "argument",
  "options": {
    "model": {
      "editor_key": "video_id",
      "default": "riXoAr6gO-E"
    }
  }
}

With this config, adding a new video snippet to a page in CloudCannon will instantiate it as <<video "riXoAr6gO-E">>.

model.implied_boolean — boolean#

If implied_boolean is set to true, then any value in this argument will alias to true, and an empty value will alias to false.

Show exampleHide example
cloudcannon.config.yaml
copied
parser: argument
options:
  model:
    editor_key: has_video_id
    implied_boolean: true
cloudcannon.config.json
copied
{
  "parser": "argument",
  "options": {
    "model": {
      "editor_key": "has_video_id",
      "implied_boolean": true
    }
  }
}

With this config the video ID itself will not be used. The editor will instead see a checkbox named has_video_id representing the presence of the argument. Unchecking and rechecking this checkbox will result in <<video true>>, so this option is not very useful for our video snippet example.

model.optional — boolean#

Whether a value must exist for this parser. Useful for a snippet that takes zero or one positional arguments.

Show exampleHide example
cloudcannon.config.yaml
copied
parser: argument
options:
  model:
    editor_key: video_id
    optional: true
cloudcannon.config.json
copied
{
  "parser": "argument",
  "options": {
    "model": {
      "editor_key": "video_id",
      "optional": true
    }
  }
}

With this config, both of the following snippet examples will parse:

page.md
copied
## My blog post

<<video "CZcNgDN81Sw">>

<<video>>

Morbi leo risus, porta ac consectetur ac, vestibulum at eros.
model.remove_empty — boolean#

If true, the output of this parser will be omitted entirely if the value is empty, rather than saving an empty value such as "". In most cases, you will want to make sure to set optional: true so that the output is able to be parsed again.

Show exampleHide example
cloudcannon.config.yaml
copied
parser: argument
options:
  model:
    editor_key: video_id
    remove_empty: true
cloudcannon.config.json
copied
{
  "parser": "argument",
  "options": {
    "model": {
      "editor_key": "video_id",
      "remove_empty": true
    }
  }
}

In cases where you have a string boundary set, with this config saving a snippet with an empty video_id value will save it as <<video>> rather than <<video "">> or <<video ''>>.

format — object#

This should be an object containing keys from the available Format keys.

Show exampleHide example

A common option to configure for the argument parser is the string_boundary:

cloudcannon.config.yaml
copied
_snippets:
  video:
    snippet: <<video [[video_arg]]>>
    params:
      video_arg:
        parser: argument
        options:
          model:
            editor_key: video_id
          format:
            string_boundary:
              - '"'
              - '`'
cloudcannon.config.json
copied
{
  "_snippets": {
    "video": {
      "snippet": "<<video [[video_arg]]>>",
      "params": {
        "video_arg": {
          "parser": "argument",
          "options": {
            "model": {
              "editor_key": "video_id"
            },
            "format": {
              "string_boundary": [
                "\"",
                "`"
              ]
            }
          }
        }
      }
    }
  }
}

This would make our snippet match <<video "CZcNgDN81Sw">> or <<video `CZcNgDN81Sw`>> but not <<video 'CZcNgDN81Sw'>> or <<video CZcNgDN81Sw>>.

Another common requirement is specifying forbidden_tokens, especially when defining an argument with no string boundary.

cloudcannon.config.yaml
copied
_snippets:
  video:
    snippet: <<video [[video_arg]]>>
    params:
      video_arg:
        parser: argument
        options:
          model:
            editor_key: video_id
          format:
            forbidden_tokens:
              - '>'
            string_boundary:
              - ''
cloudcannon.config.json
copied
{
  "_snippets": {
    "video": {
      "snippet": "<<video [[video_arg]]>>",
      "params": {
        "video_arg": {
          "parser": "argument",
          "options": {
            "model": {
              "editor_key": "video_id"
            },
            "format": {
              "forbidden_tokens": [
                ">"
              ],
              "string_boundary": [
                ""
              ]
            }
          }
        }
      }
    }
  }
}

This example sets the string boundary to nothing, with the intention of matching the source <<video CZcNgDN81Sw>>.

Without specifying a forbidden token of >, the above configuration would not match the Snippet, as the argument parser is not able to determine the end of the argument.

Argument List parser#

This parser matches a list of positional arguments and parses each using a different model.

Instead of taking a single model object, this parser takes an array of model objects. Each model object is an argument parser, but they all share one format object.

Show exampleHide example

Continuing with our video snippet shown above, we might add the ability to add a custom title as an optional second positional argument:

page.md
copied
## My blog post

<<video "riXoAr6gO-E">>

<<video "CZcNgDN81Sw" "Whiteboard the Web and Astro with Ben Holmes">>

Morbi leo risus, porta ac consectetur ac, vestibulum at eros.
cloudcannon.config.yaml
copied
_snippets:
  video:
    snippet: <<video [[arguments]]>>
    params:
      arguments:
        parser: argument_list
        options:
          models:
            - editor_key: video_id
            - editor_key: video_title
              optional: true
          format:
            string_boundary:
              - '"'
cloudcannon.config.json
copied
{
  "_snippets": {
    "video": {
      "snippet": "<<video [[arguments]]>>",
      "params": {
        "arguments": {
          "parser": "argument_list",
          "options": {
            "models": [
              {
                "editor_key": "video_id"
              },
              {
                "editor_key": "video_title",
                "optional": true
              }
            ],
            "format": {
              "string_boundary": [
                "\""
              ]
            }
          }
        }
      }
    }
  }
}

Since the title might contain whitespace, we want to make sure we set a string boundary of " to capture each argument as intended.

Options

models — array of objects#

Defines the parsing configurations for each argument. Matches the model options from the argument parser.

format — object#

This should be an object containing keys from the available Format keys. These options will apply to all argument models.

Content parser#

The content parser is used to extract a block of content, optionally spread across multiple lines.

Usually this parser will be used to parse the content between paired start and end tags in a templating language.

Show exampleHide example

To illustrate the content parser, we'll look at a custom highlight snippet that wraps a block of content:

page.md
copied
## My blog post

<<highlight>> My highlighted content! <</highlight>>

Morbi leo risus, porta ac consectetur ac, vestibulum at eros.
cloudcannon.config.yaml
copied
_snippets:
  highlight:
    snippet: <<highlight>>[[inner]]<</highlight>>
    params:
      inner:
        parser: content
        options:
          editor_key: highlighted_text
cloudcannon.config.json
copied
{
  "_snippets": {
    "highlight": {
      "snippet": "<<highlight>>[[inner]]<</highlight>>",
      "params": {
        "inner": {
          "parser": "content",
          "options": {
            "editor_key": "highlighted_text"
          }
        }
      }
    }
  }
}

Options

editor_key — string#

Specifies the key that a user in CloudCannon will see when editing this value. This key will also be used if you want to specify any input configuration using _inputs.

Show exampleHide example
cloudcannon.config.yaml
copied
_snippets:
  highlight: 
  snippet: <<highlight>>[[inner]]<</highlight>>
  params:
    inner:
      parser: content
      options:
        editor_key: highlighted_text
  _inputs:
    highlighted_text:
      type: markdown
      comment: Enter the text that should be highlighted
cloudcannon.config.json
copied
{
  "_snippets": {
    "highlight": null,
    "snippet": "<<highlight>>[[inner]]<</highlight>>",
    "params": {
      "inner": {
        "parser": "content",
        "options": {
          "editor_key": "highlighted_text"
        }
      }
    },
    "_inputs": {
      "highlighted_text": {
        "type": "markdown",
        "comment": "Enter the text that should be highlighted"
      }
    }
  }
}

With this config, the inner value of our snippet will be editable in CloudCannon using an input named highlighted_text, which we have configured as markdown and specified a comment for.

indented_by — string#

Configures the content parser to require whitespace indentation when parsing. Used when a snippet continues for as long as content is indented.

This option should only be used if required, since it prevents your Snippet from parsing if the whitespace does not match. If you want parsing to be flexible, but the output to be formatted, use the Style options instead.

Show exampleHide example
cloudcannon.config.yaml
copied
_snippets:
  highlight:
    snippet: |-
      <<highlight>>
      [[inner]]
      <</highlight>>
    params:
      inner:
        parser: content
        options:
          editor_key: highlighted_text
          indented_by: '  '
cloudcannon.config.json
copied
{
  "_snippets": {
    "highlight": {
      "snippet": "<<highlight>>\n[[inner]]\n<</highlight>>",
      "params": {
        "inner": {
          "parser": "content",
          "options": {
            "editor_key": "highlighted_text",
            "indented_by": "  "
          }
        }
      }
    }
  }
}

Example usage:

page.md
copied
## My blog post

<<highlight>>
  My highlighted content!   
<</highlight>>

<<highlight>>
    My highlighted content! 
<</highlight>>

<<highlight>>
My highlighted content!     
<</highlight>>

Morbi leo risus, porta ac consectetur ac, vestibulum at eros.

This will parse correctly as My highlighted content!

This will strip only the indentation that matches, and return My highlighted content!

This will not match this parser, as indentation is required. Use the style.block.indent option instead.

allow_leading — boolean#

If an indented_by value is set, allow_leading: true signifies that the first line of the content does not need to be indented, but subsequent lines do.

Show exampleHide example
cloudcannon.config.yaml
copied
parser: content
options:
  editor_key: highlighted_text
  indented_by: '  '
  allow_leading: true
cloudcannon.config.json
copied
{
  "parser": "content",
  "options": {
    "editor_key": "highlighted_text",
    "indented_by": "  ",
    "allow_leading": true
  }
}

Example usage:

page.md
copied
## My blog post

<<highlight>>
  My highlighted content!   
<</highlight>>

<<highlight>>Some initial content
    More highlighted content!   
<</highlight>>

Morbi leo risus, porta ac consectetur ac, vestibulum at eros.

This will parse correctly as My highlighted content!

This will also parse correctly, as Some initial content\nMore highlighted content!, despite the first line not being indented.

trim_text — boolean#

If true, all whitespace and newlines will be trimmed from the start and end of the content before it is presented to an editor in CloudCannon.

Show exampleHide example
cloudcannon.config.yaml
copied
parser: content
options:
  editor_key: highlighted_text
  trim_text: true
cloudcannon.config.json
copied
{
  "parser": "content",
  "options": {
    "editor_key": "highlighted_text",
    "trim_text": true
  }
}

Example usage:

page.md
copied
## My blog post

<<highlight>>
  My highlighted content!   
<</highlight>>

<<highlight>>  My highlighted content!  <</highlight>> 

Morbi leo risus, porta ac consectetur ac, vestibulum at eros.

Both of these will parse as My highlighted content!, despite the newlines and whitespace surrounding them.

To add whitespace back in when outputting the snippet, use the Style options.

allow_nested — boolean#

If set, nested snippets will be parsed and returned.

Show exampleHide example
cloudcannon.config.yaml
copied
parser: content
options:
  editor_key: highlighted_text
  allow_nested: true
cloudcannon.config.json
copied
{
  "parser": "content",
  "options": {
    "editor_key": "highlighted_text",
    "allow_nested": true
  }
}

Example usage:

page.md
copied
## My blog post

<<highlight>>
  My highlighted <<highlight>>content!<</highlight>>   
<</highlight>>

Morbi leo risus, porta ac consectetur ac, vestibulum at eros.

With allow_nested turned on, the content parser will detect and parse the nested <<highlight>> tags as another snippet, rather than plaintext.

This doesn't have to be the same snippet, any configured snippet will be parsed inside the content parser.

If allow_nested is set to false, inner snippets will still be parsed, but will not be returned. This means that in the above example, My highlighted <<highlight>>content!<</highlight>> would be the raw text with allow_nested: false. If inner snippets were not parsed, the content parser would end parsing at the first <</highlight>>, giving you the text: My highlighted <<highlight>>content!. This is the behavior of the raw option.

raw — boolean#

A stricter version of allow_nested: false that performs no parsing of the inner text. Useful to improve the performance of the content parser, but can misbehave.

Show exampleHide example
cloudcannon.config.yaml
copied
parser: content
options:
  editor_key: highlighted_text
  raw: true
cloudcannon.config.json
copied
{
  "parser": "content",
  "options": {
    "editor_key": "highlighted_text",
    "raw": true
  }
}

Example usage:

page.md
copied
## My blog post

<<highlight>>
  My highlighted <<highlight>>content!<</highlight>>   
<</highlight>>

Morbi leo risus, porta ac consectetur ac, vestibulum at eros.

With raw turned on, the content parser will perform no parsing on the text it is capturing. This means that the nested <<highlight>> will not be detected as a snippet and the first <</highlight>> tag found will end the content parser, giving you the parsed content My highlighted <<highlight>>content!.

forbidden_tokens — array of strings#

Stops parsing content as soon as one of these tokens is encountered. Useful when the content parser is being greedy and consuming more input than intended.

Show exampleHide example
cloudcannon.config.yaml
copied
parser: content
options:
  editor_key: highlighted_text
  forbidden_tokens:
    - <
cloudcannon.config.json
copied
{
  "parser": "content",
  "options": {
    "editor_key": "highlighted_text",
    "forbidden_tokens": [
      "<"
    ]
  }
}

Example usage:

page.md
copied
## My blog post

<<highlight>> My content <</highlight>> 

<<highlight>> My < content <</highlight>> 

Morbi leo risus, porta ac consectetur ac, vestibulum at eros.

This snippet does not contain a forbidden token, so will parse and return correctly.

This snippet does contain a forbidden token, and will not match.

This isn't particularly useful in this example, but is generally used to prevent the content parser from consuming the end token of itself.

optional — boolean#

Whether content is required for this snippet to match.

style — Style options#

A style object to control how content is parsed and stringified, to produce cleaner source code.

Show exampleHide example
cloudcannon.config.yaml
copied
parser: content
options:
  editor_key: highlighted_text
  style:
    inline:
      leading: ' '
      trailing: ' '
    block:
      leading: |+

      trailing: |+

      indent: '  '
cloudcannon.config.json
copied
{
  "parser": "content",
  "options": {
    "editor_key": "highlighted_text",
    "style": {
      "inline": {
        "leading": " ",
        "trailing": " "
      },
      "block": {
        "leading": "\n",
        "trailing": "\n",
        "indent": "  "
      }
    }
  }
}

For example, we'll look at the following input file:

page.md
copied
## My blog post

<<highlight>>My content<</highlight>> 

<<highlight>>My
multiline               
content<</highlight>>

Morbi leo risus, porta ac consectetur ac, vestibulum at eros.

As this snippet contains a single line of text, it will be formatted as inline.

This snippet contains multiple lines, so will be auto-formatted as block.

When the above file is opened in CloudCannon and saved, the source will be formatted to:

page.md
copied
## My blog post

<<highlight>> My content <</highlight>> 

<<highlight>>
  My
  multiline               
  content
<</highlight>>

Morbi leo risus, porta ac consectetur ac, vestibulum at eros.

The inline snippet was given the leading and trailing padding of the inline style options.

The block snippet was given the leading and trailing padding of the block style options, and was also indented by the provided tokens.

The style options are also stripped when the content editor loads a snippet, so the above snippets would be edited as My content and My\nmultiline\ncontent respectively. As a result, when using the style options you shouldn't need to configure the indented_by option for the content parser.

Key values parser#

Parses repeating pairs of keys and values.

Show exampleHide example

To illustrate the Key values parser, we'll look at a custom anchor snippet that parses the standard HTML anchor tag:

page.md
copied
## My blog post

<a href="#link"> inner </a>

Morbi leo risus, porta ac consectetur ac, vestibulum at eros.
cloudcannon.config.yaml
copied
_snippets:
  anchor:
    snippet: <a [[args]]>[[inner]]</a>
    params:
      args:
        parser: key_values
        options:
          models:
            - editor_key: href
          format:
            string_boundary:
              - '"'
            root_value_delimiter: '='
      inner:
        parser: content
        options:
          editor_key: text
cloudcannon.config.json
copied
{
  "_snippets": {
    "anchor": {
      "snippet": "<a [[args]]>[[inner]]</a>",
      "params": {
        "args": {
          "parser": "key_values",
          "options": {
            "models": [
              {
                "editor_key": "href"
              }
            ],
            "format": {
              "string_boundary": [
                "\""
              ],
              "root_value_delimiter": "="
            }
          }
        },
        "inner": {
          "parser": "content",
          "options": {
            "editor_key": "text"
          }
        }
      }
    }
  }
}

Options

models — array of objects#

Defines the parsing configurations for each argument.

models[*].editor_key — string#

Specifies the key that a user in CloudCannon will see when editing this value. This key will also be used if you want to specify any input configuration using _inputs.

If no source_key is set, this key will also be assumed to be the source key.

Show exampleHide example
cloudcannon.config.yaml
copied
parser: key_values
options:
  models:
    - editor_key: href
    - editor_key: class
    - editor_key: target

Key/value models can be specified in any order, so do not have to match the order of they keys in the source code.

cloudcannon.config.json
copied
{
  "parser": "key_values",
  "options": {
    "models": [
      {
        "editor_key": "href"
      },
      {
        "editor_key": "class"
      },
      {
        "editor_key": "target"
      }
    ]
  }
}

Key/value models can be specified in any order, so do not have to match the order of they keys in the source code.

Example usage:

page.md
copied
## My blog post

<a href="#link" target="_blank" class="my_link"> ... </a>
Morbi leo risus, porta ac consectetur ac, vestibulum at eros.

When edited in CloudCannon, this will edit as the data model:

href: "#link"
target: "_blank"
class: "my_link"
models[*].source_key — string#

Specifies the key that the parser should look for in the source text.

This only needs to be set if it differs from the editor_key.

Show exampleHide example
cloudcannon.config.yaml
copied
parser: key_values
options:
  models:
    - source_key: href
      editor_key: link
    - source_key: target
      editor_key: link_target
    - source_key: class
      editor_key: classnames

Key/value models can be specified in any order, so do not have to match the order of they keys in the source code.

cloudcannon.config.json
copied
{
  "parser": "key_values",
  "options": {
    "models": [
      {
        "source_key": "href",
        "editor_key": "link"
      },
      {
        "source_key": "target",
        "editor_key": "link_target"
      },
      {
        "source_key": "class",
        "editor_key": "classnames"
      }
    ]
  }
}

Key/value models can be specified in any order, so do not have to match the order of they keys in the source code.

Example usage:

page.md
copied
## My blog post

<a href="#link" target="_blank" class="my_link"> ... </a>
Morbi leo risus, porta ac consectetur ac, vestibulum at eros.

When edited in CloudCannon, this will edit as the data model:

link: "#link"
link_target: "_blank"
classnames: "my_link"
models[*].default — string#

The default value for this argument, which is used when adding a new snippet to a page in CloudCannon.

Show exampleHide example
cloudcannon.config.yaml
copied
parser: key_values
options:
  models:
    - editor_key: href
      default: /
    - editor_key: target
      default: _blank
cloudcannon.config.json
copied
{
  "parser": "key_values",
  "options": {
    "models": [
      {
        "editor_key": "href",
        "default": "/"
      },
      {
        "editor_key": "target",
        "default": "_blank"
      }
    ]
  }
}

With this config, adding a new anchor snippet to a page in CloudCannon will instantiate it as <a href="/" target="_blank"> ... </a>

models[*].optional — boolean#

Whether a value must exist for this parser. Useful for long sets of key-value pairs that are not all required.

Show exampleHide example
cloudcannon.config.yaml
copied
parser: key_values
options:
  models:
    - editor_key: href
    - editor_key: target
      optional: true
    - editor_key: class
      optional: true
cloudcannon.config.json
copied
{
  "parser": "key_values",
  "options": {
    "models": [
      {
        "editor_key": "href"
      },
      {
        "editor_key": "target",
        "optional": true
      },
      {
        "editor_key": "class",
        "optional": true
      }
    ]
  }
}

Example usage:

page.md
copied
## My blog post

<a href="#link" target="_blank" class="my_link"> ... </a> 
<a href="#link" target="_blank"> ... </a> 
<a href="#link"> ... </a> 

<a target="_blank"> ... </a> 

Morbi leo risus, porta ac consectetur ac, vestibulum at eros.

All of these anchor tags will parse correctly, as they are missing either target or class keys from the arguments, and both are marked as optional.

This anchor tag will not be parsed, as the href key is absent and is not marked as optional.

models[*].remove_empty — boolean#

Whether this key-value pair should be omitted altogether if the value is empty. Requires that this key is also set as optional, as otherwise the snippet would not re-parse.

Show exampleHide example
cloudcannon.config.yaml
copied
parser: key_values
options:
  models:
    - editor_key: href
    - editor_key: target
    - editor_key: class
      optional: true
      remove_empty: true
cloudcannon.config.json
copied
{
  "parser": "key_values",
  "options": {
    "models": [
      {
        "editor_key": "href"
      },
      {
        "editor_key": "target"
      },
      {
        "editor_key": "class",
        "optional": true,
        "remove_empty": true
      }
    ]
  }
}

Example usage:

page.md
copied
## My blog post

<a href="#link" target="_blank" class=""> ... </a> 
<a href="#link" target="_blank"> ... </a>  

Morbi leo risus, porta ac consectetur ac, vestibulum at eros.

With remove_empty: true both of these anchor tags will be output as <a href="#link" target="_blank"> ... </a> as the class key contains no value.

If remove_empty: false was set, this snippet would be output as <a href="#link" target="_blank" class=""> ... </a>. The empty class key would be written even if it did not originally appear in the parsed source.

format — object#

This should be an object containing keys from the available Format keys.

Literal parser#

Used to parse an exact literal value.

This parser is less useful when writing custom Snippets directly, but is very useful when writing Snippet templates.

Options

literal — string#

The exact literal string that should be matched.

Repeating literal parser#

Used to parse some number of repeating literal values.

Show exampleHide example

For this example, we'll pretend that we're making a Snippet for a markdown heading. This isn't something you need to do on CloudCannon since we understand Markdown natively, but it's a good illustration of the repeating literal parser.

page.md
copied
# My blog post

Aenean lacinia bibendum nulla sed consectetur.

## My heading

Morbi leo risus, porta ac consectetur ac, vestibulum at eros.
cloudcannon.config.yaml
copied
_snippets:
  custom_title:
    snippet: '[[hashes]] [[title]]'
    params:
      hashes:
        parser: repeating_literal
        options:
          literal: '#'
          minimum: 1
      title:
        parser: content
        options:
          editor_key: title
          forbidden_tokens:
            - |+

Useful tip: Here we need to specify to the content parser that the value cannot contain newline characters, otherwise the content parser would consume the rest of the page.

cloudcannon.config.json
copied
{
  "_snippets": {
    "custom_title": {
      "snippet": "[[hashes]] [[title]]",
      "params": {
        "hashes": {
          "parser": "repeating_literal",
          "options": {
            "literal": "#",
            "minimum": 1
          }
        },
        "title": {
          "parser": "content",
          "options": {
            "editor_key": "title",
            "forbidden_tokens": [
              "\n"
            ]
          }
        }
      }
    }
  }
}

Useful tip: Here we need to specify to the content parser that the value cannot contain newline characters, otherwise the content parser would consume the rest of the page.

Options

literal — string#

The exact literal string that should be matched.

minimum — number#

The minimum number of times that the literal string must occur to match this parser. A good example is a markdown code block, which can open with any number of backticks greater than or equal to three.

editor_key — string#

Specifies a key that a user in CloudCannon will see to edit this value. The value returned by this parser is a number input that can be edited to change how many literals are output by the parser.

Show exampleHide example
cloudcannon.config.yaml
copied
_snippets:
  custom_title:
    snippet: '[[hashes]] [[title]]'
    params:
      hashes:
        parser: repeating_literal
        options:
          literal: '#'
          minimum: 1
          editor_key: heading_level
      title:
        parser: content
        options:
          editor_key: title
          forbidden_tokens:
            - |+

cloudcannon.config.json
copied
{
  "_snippets": {
    "custom_title": {
      "snippet": "[[hashes]] [[title]]",
      "params": {
        "hashes": {
          "parser": "repeating_literal",
          "options": {
            "literal": "#",
            "minimum": 1,
            "editor_key": "heading_level"
          }
        },
        "title": {
          "parser": "content",
          "options": {
            "editor_key": "title",
            "forbidden_tokens": [
              "\n"
            ]
          }
        }
      }
    }
  }
}

Example usage:

page.md
copied
# My blog post 

Aenean lacinia bibendum nulla sed consectetur.

## My heading 

Morbi leo risus, porta ac consectetur ac, vestibulum at eros.

If editing these as snippets in CloudCannon, the editor will see heading_level as a number input. Changing this value will add or remove # characters to the source code to match the number.

default — number#

The default number of literals, which is used when adding a new snippet to a page in CloudCannon.

Optional parser#

Wraps a Snippet string, marking it as an optional segment of the Snippet.

Show exampleHide example

For this example, we'll look at configuring a Snippet for a theoretical custom internal link syntax. This syntax can handle linking to another page, and optionally lets you configure a custom label.

page.md
copied
# My blog post

[[Other Page]]

[[Other Page|custom display text]]

We can support this with the Snippet configuration:

cloudcannon.config.yaml
copied
_snippets:
  internal_link:
    snippet: \[\[[[target]][[optional_text]]\]\]
    params:
      target:
        parser: content
        options:
          editor_key: link_target
          forbidden_tokens:
            - ']'
            - '|'
      optional_text:
        parser: optional
        options:
          snippet: '|[[text]]'
          remove_empty: true
      text:
        parser: content
        options:
          editor_key: link_text
          forbidden_tokens:
            - ']'

Since this Snippet needs to match double square brackets in the source code, our template string here must escape them.

It's generally good practice to ensure non-delimited parsers have forbidden_tokens configured to ensure they don't consume more input than intended.

This parser references a [[text]] param, which is defined in the outer params object.

This text param is only referenced from inside the optional parser's Snippet template string.

cloudcannon.config.json
copied
{
  "_snippets": {
    "internal_link": {
      "snippet": "\\[\\[[[target]][[optional_text]]\\]\\]",
      "params": {
        "target": {
          "parser": "content",
          "options": {
            "editor_key": "link_target",
            "forbidden_tokens": [
              "]",
              "|"
            ]
          }
        },
        "optional_text": {
          "parser": "optional",
          "options": {
            "snippet": "|[[text]]",
            "remove_empty": true
          }
        },
        "text": {
          "parser": "content",
          "options": {
            "editor_key": "link_text",
            "forbidden_tokens": [
              "]"
            ]
          }
        }
      }
    }
  }
}

Since this Snippet needs to match double square brackets in the source code, our template string here must escape them.

It's generally good practice to ensure non-delimited parsers have forbidden_tokens configured to ensure they don't consume more input than intended.

This parser references a [[text]] param, which is defined in the outer params object.

This text param is only referenced from inside the optional parser's Snippet template string.

Options

snippet — string#

A snippet string to wrap and make optional.

This shares the same params as the outermost snippet, so may reference itself or other params.

remove_empty — boolean#

Whether to output an empty string, or no value at all. This will bubble up to any outer parsers, so may control whether they return output.

Repeating parser#

Wraps a Snippet string, marking it as a repeating segment of the Snippet.

Useful when repeating a complex Snippet with multiple inputs. Simple arrays of primitive types should instead use the argument parser configured as an array.

Show exampleHide example

For this example, we'll look at configuring a Snippet for a theoretical compound component that renders a block of links.

page.md
copied
# My blog post

<Links>
  <Link href="about">About Page</Link>
  <Link href="index">Index Page</Link>
</Links>

One option would be to configure a component for <Links> that wraps a content editor, within which you can place <Link> components. This would work, but may expose too much functionality, for example we may not want editors to be able to type plain text between each <Link> component.

Instead, we can define the following Snippet to handle the entire block as one component:

cloudcannon.config.yaml
copied
_snippets:
  link_block:
    snippet: <Links>[[links]]</Links>
    params:
      links:
        parser: repeating
        options:
          snippet: <Link [[link_args]]>[[link_content]]</Link>
          editor_key: links
          style:
            output: block
            between: |+

            block:
              leading: |+

              trailing: |+

              indent: '  '
      link_args:
        parser: key_values
        options:
          models:
            - editor_key: href
          format:
            string_boundary:
              - '"'
            root_value_delimiter: '='
      link_content:
        parser: content
        options:
          editor_key: label
          forbidden_tokens:
            - <

This parser references the [[link_args]] and [[link_content]] params, which are defined in the outer params object.

This style object ensures that any Snippets are formatted to match the example Markdown above.

cloudcannon.config.json
copied
{
  "_snippets": {
    "link_block": {
      "snippet": "<Links>[[links]]</Links>",
      "params": {
        "links": {
          "parser": "repeating",
          "options": {
            "snippet": "<Link [[link_args]]>[[link_content]]</Link>",
            "editor_key": "links",
            "style": {
              "output": "block",
              "between": "\n",
              "block": {
                "leading": "\n",
                "trailing": "\n",
                "indent": "  "
              }
            }
          }
        },
        "link_args": {
          "parser": "key_values",
          "options": {
            "models": [
              {
                "editor_key": "href"
              }
            ],
            "format": {
              "string_boundary": [
                "\""
              ],
              "root_value_delimiter": "="
            }
          }
        },
        "link_content": {
          "parser": "content",
          "options": {
            "editor_key": "label",
            "forbidden_tokens": [
              "<"
            ]
          }
        }
      }
    }
  }
}

This parser references the [[link_args]] and [[link_content]] params, which are defined in the outer params object.

This style object ensures that any Snippets are formatted to match the example Markdown above.

When edited in CloudCannon, the data will be extracted as:

links:
  - href: "about"
    label: "About Page"
  - href: "index"
    label: "Index Page"

Notice how each value is available under the editor_key for its model, and each repetition of the inenr snippet is then grouped inside the editor_key set on the repeating parser.

Options

snippet — string#

A snippet string to wrap and make repeatable.

This shares the same params as the outermost snippet, so may reference itself or other params.

editor_key — string#

The key to use when editing the data for this snippet in CloudCannon.

This key will become an array of objects, each containing the data for an iteration of the repeated snippet.

default_length — number#

Used when initializing a new Snippet, and determines how many repetitions should be created in the initial data.

optional — boolean#

If false this parser requires at least one matching value. If true, matching zero times is a valid state.

Setting this to true is preferred to wrapping the repeating parser inside an optional parser.

style — Style options#

A style object to control how content is parsed and stringified, to produce cleaner source code.

Can be customized to indent each repetition, add leading and trailing tokens to the entire block, and add tokens between each repetition.

Common style options#

Options to use when configuring how to save your Snippet back to your source file.

These options are flexible, and aren't required when parsing a Snippet. For example, this allows a Snippet to parse when it is found entirely on one line, but output across multiple lines with tidy indentation.

output — string#

Either inline or block. For parsers such as the content and repeating parsers, determines how to format the output.

When set to block, values will always be output with leading and trailing newlines.

When set to inline, values will always be output on a single line, with newlines inserted as \n.

If unspecified, inline will be chosen when the value is a single line, otherwise block formatting will be used.

between — string#

Used only in the repeating parser. Specifies the text to insert between each repetition. Useful for adding newlines between each repetition of the parser.

inline.leading — string#

For inline content, what should lead the value. Useful for adding whitespace around strings.

inline.trailing — string#

For inline content, what should trail the value. Useful for adding whitespace around strings.

block.leading — string#

For block content, what should lead the value. Useful for adding newlines around content.

block.trailing — string#

For block content, what should trail the value. Useful for adding newlines around content.

block.indent — string#

For block content, how should the value be indented. Nested snippets only need to specify the indentation relative to their parent.

Common format options#

Options to use when configuring how to parse your Snippet from your source file.

root_boundary — { start: string, end: string }#

For key-value segments, what boundary is required (if any).

remove_empty_root_boundary — boolean#

If a key-value segment is empty, but it has a root boundary, should that boundary be removed from the source code?

root_value_delimiter — string#

For key-value pairs, what delimits the key from the value.

  • For pairs such as key="value", this would be =.
  • For pairs such as key: "value", this would be : .
root_pair_delimiter — array of strings#

For key-value pairs, what delimits the each key-value pair from the next.

  • For segments such as href="about" title="Hello World", this would be (a space).
  • For segments such as href: about, title: "Hello World", this would be ,.
root_value_boundary — { start: string, end: string }#

For arguments and values, what boundary is required (if any).

For example, MDX specifies { start: "{", end: "}" }, which allows syntax such as: <Component flag={true} />.

root_value_boundary_optional — { [type]: boolean }#

Where a root_value_boundary applies, what types do not require a boundary?

For example, MDX specifies { string: true }, which allows strings to exist as <Component arg="true" /> rather than <Component arg={"true"} />.

string_boundary — array of strings#

The valid tokens that can bound a string. The parser will ensure the matched end token is the same as the start token.

To handle common strings with either single or double quotes, this would usually be ["'", "\""]

string_escape_character — string#

What character can be placed before a string boundary to escape it?

If required, this will usually be \.

object_boundary — { start: string, end: string }#

For snippets which can parse object literals, what characters start and end an object?

If required, this will usually be { start: "{", end: "}" }.

object_value_delimiter — string#

Within an object, what delimits a key from a value?

If required, this will usually be :.

object_pair_delimiter — string#

Within an object, what delimits each key-value pair from the next.

If required, this will usually be ,.

array_boundary — { start: string, end: string }#

For snippets which can parse array literals, what characters start and end an array?

If required, this will usually be { start: "[", end: "]" }.

array_delimiter — string#

Within an array, what delimits each value from the next"?

If required, this will usually be ,.

forbidden_tokens — string#

For the given parser, what tokens are strictly not allowed in values. Most often used to limit what the content parser will consume.

allow_booleans — boolean#

Whether unquoted booleans like true and false can be used as values.

allow_numbers — boolean#

Whether unquoted numbers like 5 and -0.4 can be used as values.

allow_implied_values — boolean#

When parsing key-value pairs, can a key be specified without a value to imply a value of true?

Related Articles

Open in a new tab