A block shortcode is one you’ve described in site.yaml: its name, the parameters it takes and, optionally, what it should look like in the preview. In the visual editor it shows up as a card with a form, so whoever is writing doesn’t need to remember the parameter names or the quoting rules. In the file it’s the same {{< name >}} text Hugo has always read.
Configuration#
Define them under shortcodes:
shortcodes:
youtube:
label: YouTube video
icon: "🎬"
category: media
fields:
- name: id
type: string
label: Video ID
required: true
preview: |
<div class="video">YouTube video {{id}}</div>
hugo: '{{< youtube id="{{id}}" >}}'The key (youtube) is the shortcode’s name in Hugo, i.e. the filename in layouts/shortcodes/ without the extension.
| Property | What it does |
|---|---|
label |
The name shown in the insert menu and in the preview pane |
icon |
An emoji or character shown next to the label in those two places |
category |
Groups shortcodes into a submenu of the insert menu |
fields |
The parameters, using the same field types as frontmatter |
preview |
An HTML template for the preview pane; see Preview templates |
hugo |
An example of the shortcode’s syntax. Emet reads it to decide whether the shortcode is paired and whether it takes positional or named parameters, and shows it to the AI as the canonical form. See below |
children |
Names of shortcodes allowed inside this one, which makes it a container |
parent |
Marks a shortcode as child-only, so it’s left out of the main insert menu |
min_children |
A container shows a warning while it has fewer children than this |
max_children |
A container’s Add button is disabled once it has this many |
inline |
Insert the shortcode as a chip inside the current paragraph rather than as a block card. See Shortcodes inside a sentence |
allow_text_content |
Accepted for documentation’s sake. Text before a container’s first child is preserved whether or not you set it |
Note that label and icon don’t appear on the editing card itself, which shows the raw shortcode name. I’d like to change that; for now it’s how it is.
Fields#
Each field has a name, a type and the usual optional label, required, default and hint. The name is the parameter name Hugo will see, so it has to match what your template reads with .Get. Defaults are filled in when you insert the shortcode from the menu.
One field name is special. A field called content is the text between the opening and closing tags rather than a parameter, and it’s edited in a textarea (rows sets its height). Declaring it also tells Emet the shortcode is paired:
shortcodes:
callout:
label: Callout
fields:
- name: type
type: select
options: [info, warning, tip]
default: info
- name: content
type: text
rows: 4
preview: '<div class="callout callout-{{type}}">{{content}}</div>'Paired and positional shortcodes#
Hugo is strict about two things, and the editor follows the same rules when it writes a file.
Closing tags. A shortcode whose template reads .Inner must be closed: {{< callout >}}...{{< /callout >}}. A shortcode whose template doesn’t read .Inner (the built-in youtube and figure, for instance) must not have a closing tag or a trailing /, or Hugo fails the build. Emet writes a closing tag only when it considers the shortcode paired, which is when any of these is true: it was read from the file with a closing tag; its config declares a content field; or its hugo example shows a closing tag or a {{content}} placeholder. Type something into a content field and the shortcode becomes paired on the spot.
Parameter style. A call uses either positional parameters ({{< gist "user" "id" >}}) or named ones ({{< figure src="a.png" >}}), never both. Bare words are positional: {{< highlight go >}} passes go as .Get 0, and Emet writes it back as {{< highlight "go" >}}, which Hugo reads identically. When you edit a positional shortcode in a card, the fields map onto positions in the order they’re declared under fields, and the style is preserved.
The hugo property is how you tell Emet which form a shortcode takes before anyone has used it. Write it with {{fieldName}} placeholders matching your field names:
hugo: '{{< figure src="{{src}}" caption="{{caption}}" >}}' # named, not paired
hugo: '{{< gist "{{user}}" "{{id}}" >}}' # positional, not paired
hugo: '{{< callout type="{{type}}" >}}{{content}}{{< /callout >}}' # named, pairedIt’s an example, not an output template: Emet doesn’t substitute into it when writing the file, it just reads the shape. The setup wizard fills it in from your shortcode templates.
In the editor#
Insert a shortcode from the braces menu in the toolbar, which lists everything that isn’t child-only, grouped by category. It appears as a card where your cursor was (or as a chip in the paragraph, for inline: true shortcodes). The menu only appears in sections with body.shortcodes: true; the cards appear regardless. See Shortcodes.
The card’s header has a drag handle, a collapse chevron, the shortcode name and, on hover, a delete button. Expanded, the card shows a field for each parameter and the content textarea if there is one. Collapsed, it shows only the header. A freshly inserted shortcode with empty fields opens expanded; one loaded from the file with values in it starts collapsed. Click the header, or double-click anywhere on the card, to toggle.
Drag the handle to move a card up or down the document. The delete button removes it straight away, except for a container with children, which asks first.
Shortcodes inside a sentence#
A shortcode that shares a line with other text (Hello {{< param "site.title" >}} world., or a {{< ref >}} in a list item) is shown as a small blue chip inside the paragraph rather than as a block card, and the paragraph stays in one piece when you save. The chip shows the preview text if the shortcode has a preview, and the raw shortcode otherwise. Click it to open a little form with the fields; click away to close it. Shortcodes configured with inline: true are inserted this way from the toolbar.
Markdown shortcodes#
Both delimiters are supported. A {{% name %}} shortcode gets the same card and is written back with its % delimiter, so Hugo still renders its output as Markdown.
What the editor never touches#
Escaped shortcodes (the slash-star form Hugo documents for showing shortcode syntax literally, which this page is full of), shortcodes inside fenced code blocks and shortcodes inside inline code spans are left exactly as written. You can document shortcodes with Emet without Emet trying to render them.
Containers#
A shortcode with children can hold other shortcodes:
shortcodes:
tabs:
label: Tabs
fields: []
children: [tab]
min_children: 1
preview: '<div class="tabs">{{children}}</div>'
tab:
label: Tab
parent: tabs
fields:
- name: title
type: string
required: true
- name: content
type: text
preview: '<section><h4>{{title}}</h4>{{content}}</section>'A container card has an Add Tab button at the bottom of its children area (or Add item with a menu, if more than one child type is allowed), shows the child count in its header and, when collapsed, says how many items are hidden. Each level of nesting gets a different coloured left border so you can see what’s inside what, and nesting works to any depth. With min_children set, the card says “Needs at least N items” while it’s short; with max_children, the add button greys out once you’re there.
A child shortcode’s content textarea only appears on leaf shortcodes. A child that’s itself a container edits its own children instead. Any text between the container’s opening tag and its first child is kept and written back first.
What gets written#
Every block shortcode is serialised the same way: the delimiter it came in with, positional values first, then key="value" pairs with quotes escaped, then the content and a closing tag if it’s paired:
{{< youtube id="abc123" >}}
{{< callout type="warning" >}}Mind the gap.{{< /callout >}}Containers are written with each child on its own line, indented. Unknown shortcodes keep whichever form they were read in. Multi-line parameter lists are collapsed onto one line. The Markdown around your shortcodes gets the usual visual-editor treatment once you edit the entry; see What happens to your Markdown.
In Markdown mode#
In the Markdown editor a block shortcode is just highlighted text, and you edit it like any other text.