flowedit.
FLOWEDIT DOCUMENTATION

Shortcode reference

From the plugin documentation · View source ↗

FlowEdit stores rich media and structured content as shortcodes inside the page’s Markdown. Everything below is rendered server-side by FlowEdit’s own shortcode handlers (via Shortcode Core) — the editor is just one way to produce this markup. You can also write these shortcodes by hand.

General rules

  • Every handler uses a closed allow-list of parameters. An unknown parameter key makes the whole shortcode render nothing (silent fail-safe).
  • width is an integer percentage clamped to 25–100; default 100. Width 100 omits the inline width style entirely.
  • align is left | center | right; default center (class flow-media--align-{align} on the <figure>).
  • All string values are entity-decoded then HTML-escaped on output.

[flow-image] — sized / captioned image

[flow-image src="hero.jpg" alt="Sunset" caption="Lake view" width="75" align="left"]
Parameter Required Notes
src yes https://…, site-root /…, or a page media filename (resolved through the page’s media). .. and protocol-relative //… URLs are rejected.
alt no Plain text.
caption no Rendered as <figcaption>.
width, align no See general rules.

Output: a <figure> with flow-media and alignment classes, an <img> with loading="lazy", and an optional <figcaption>. Non-default widths add an inline percentage width to the figure.

Images with default width, center alignment, and no caption stay plain Markdown (![alt](file.jpg)) instead — FlowEdit only emits [flow-image] when one of those options is used.

[flow-youtube] — YouTube embed

[flow-youtube id="dQw4w9WgXcQ" title="Demo" width="100" align="center" caption="Intro"]
Parameter Required Notes
id yes 11-character video id ([A-Za-z0-9_-]{11}).
title yes Non-empty; used as the iframe title (accessibility).
caption no Rendered as <figcaption>.
width, align no See general rules.

Output: a privacy-enhanced https://www.youtube-nocookie.com/embed/{id} iframe inside a <figure> (class video-embed flow-media …). Only the server-side renderer produces this markup — the in-editor preview is editor-only.

[flow-embed] — third-party embed

[flow-embed url="https://x.com/getgrav/status/1234567890123456789"]
Parameter Required Notes
url yes https-only URL owned by an allow-listed provider.

Allow-listed providers (validated again server-side — unknown URLs render nothing):

  • X / Twitter posts (twitter.com/x.com …/status/{id}) → platform.twitter.com/embed/Tweet.html iframe (JS-free, dnt=1).
  • GitHub Gists (gist.github.com/{user}/{id}) → .pibb iframe.
  • Spotify tracks/albums/playlists/episodes/shows → converted to the matching /embed/… URL.
  • Google Maps (/maps/embed path or ?output=embed) → passthrough iframe.

Output: <figure class="flow-embed flow-embed--{provider}"> with a lazy iframe. Front-end sizing lives in css/flow-embed.css; Gists/Maps use the flow-embed--tall height variant instead of a 16:9 aspect ratio. In the editor, pasting a supported URL onto an empty selection inserts the embed directly, and /embed opens the picker.

[flow-gallery width="80" align="left"]
[flow-slide src="a.jpg" alt="A" caption="First"]
[flow-slide src="b.jpg"]
[/flow-gallery]
  • Slides are [flow-slide src="…" alt="…" caption="…"] tags (self-closing or paired); src accepts the same values as [flow-image].
  • Maximum 32 slides; slides with unresolvable src are skipped.
  • Gallery-level parameters: width, align only.

Output: <figure class="flow-gallery …"><div class="flow-gallery__grid"> with one <figure class="flow-gallery__slide"> per slide.

[flow-video] — uploaded video or Vimeo

Exactly one of src or vimeo must be present:

[flow-video src="clip.mp4" title="Walkthrough" caption="Part 1"]
[flow-video vimeo="76979871" title="Promo"]
Parameter Required Notes
src one of Page media filename, https://…, or site-root path ending in .mp4, .webm, .ogv, or .ogg (query strings allowed). Rendered as <video controls> with the matching MIME type.
vimeo one of Numeric Vimeo video id (5–12 digits). Rendered as a player.vimeo.com iframe.
title yes Non-empty; used as the <video title> / iframe title.
caption no Rendered as <figcaption>.
width, align no See general rules.

[flow-columns] / [flow-column] — column layout

Two to four side-by-side rich-text columns. Each column body is ordinary Markdown (processed by Grav before the shortcode renders):

[flow-columns count="2"]
[flow-column]
Left **column** content.
[/flow-column]
[flow-column]
Right column content.
[/flow-column]
[/flow-columns]
  • count accepts 2–4 and is clamped; the rendered grid follows the actual number of column bodies, so hand-edited markup stays truthful.
  • Column author HTML passes through a rich-text allow-list. Validated FlowEdit media and custom-block renderer output is preserved; raw scripts/iframes and unsafe links remain blocked. Empty columns keep their position.
  • Blocks with fewer than 2 or more than 4 columns render nothing (the raw text is consumed).
  • Output: <div class="flow-columns flow-columns--N"> with one <div class="flow-columns__col"> per column, laid out by css/flow-columns.css (loaded on demand; collapses to a single column below 768 px). Themes can restyle via .flow-columns* or override the template templates/flow-blocks/columns.html.twig.
  • Columns support [flow-image], [flow-gallery], [flow-video], [flow-youtube], [flow-embed], and [flow-block] as direct children.

[flow-block] / [flow-item] — structured custom blocks

Custom blocks store their fields as shortcode attributes and their body between the tags:

[flow-block type="callout" tone="warning" title="Heads up"]
Save your work first.
[/flow-block]

List-type blocks (accordion, tabs) store items as nested shortcodes:

[flow-block type="accordion" multiple="0" open_first="1"]
[flow-item title="What is FlowEdit?"]
A modern slash editor for Grav.
[/flow-item]
[/flow-block]
  • type selects the block definition; attributes are allow-listed per type (type + the configured field names). Unknown keys render nothing.
  • Item bodies pass through a server-side rich-text allow-list (prose elements, safe links) before Twig renders them. Maximum 32 items.
  • Rendering goes through templates/flow-blocks/{name}.html.twig, overridable by the theme — see Creating custom blocks.
  • If the block is disabled in the plugin config, its shortcode renders empty on the front end (content stays in the page file).

Legacy HTML markers (deprecated)

Older FlowEdit versions used freeform HTML markers:

<!--flow-block:Button-->
<a class="btn btn-primary" href="/contact">Contact us</a>
<!--/flow-block-->

These display a read-only visual preview with a warning and retain their original HTML source. Front-end processing follows Grav’s HTML/security settings. New content uses structured shortcodes. See the stable storage contract and legacy replacement policy.