Markdown directives
Every block a page can hold has a Markdown spelling, and the spelling round-trips: export a page with GET …/pages/{pageId}/markdown?form=body, send it back as body, and you get the same page. Plain Markdown covers prose, lists, code, tables and pictures (see Markdown in Forumulate); everything else — callouts, tabs, cards, steppers, columns, buttons and the rest — is a directive.
The same list is served as JSON, generated from the importer itself:
curl https://forumulate.com/api/docs/markdown-dialectThe four forms
Form | Written | Used for |
|---|---|---|
container |
| a block that holds other blocks |
item |
| a child of a container: a tab, a step, a column |
one line |
| a block with no body: a button, an embed, a video |
one-line item |
| a self-closing child: a card |
Closers count, not match. Containers nest freely — a callout inside a tab inside a tab group — and each bare
:::closes the innermost open container.::only ever closes an item.A one-line directive always has braces.
:::feed{}:::is a block;:::feed:::is the text:::feed:::.Attributes are
key="value",key=value(no spaces) or a bareflag. Inside quotes,\"is a literal quote.Up to three spaces of indent are allowed before a directive. Four or more is an indented code block, and a directive inside a
```fence stays code.An attribute a directive does not read is dropped and reported — the write answers
X-Field-Warningsnaming it — never kept silently and never a 422.An unknown directive is transparent: its body is kept as ordinary blocks, and the same warning names it.
Attributes every directive takes
anchor, class, hook, stackOn, alternate, hideOn, shadow, radius, divider, gap, rowGap, spaceBefore, spaceAfter, spaceAround — the block's hooks and look keys. See Sections, video and block hooks in the authoring reference.
Directives
The attribute lists in this section are the importer's own table: anything else on these directives is dropped with a warning.
hint → hint
Written:
:::hint{…}…:::Attributes:
style,kind,title,icon,tone,titleGap,edgeSame block as:
:::attention,:::bug,:::caution,:::check,:::danger,:::deprecated,:::done,:::error,:::gloss,:::important,:::info,:::note,:::success,:::tip,:::warn,:::warning
:::hint{style="warning" title="Heads up"}
Mind the gap.
:::expandable → expandable
Written:
:::expandable{…}…:::Attributes:
title,label,style,icon,open
:::expandable{title="More detail"}
Hidden until opened.
:::expandablegroup → expandableGroup
Written:
:::expandablegroup{…}…:::Holds:
expandableAttributes:
exclusive,style
:::expandablegroup{exclusive}
:::expandable{title="One"}
First.
:::
:::expandable{title="Two"}
Second.
:::
:::tabs → tabs
Written:
:::tabs{…}…:::Holds:
tabAttributes:
syncKey,overflow,orientation,asCodeGroup,variant,tabStyle,activeColor,activeColorDark,activeUnderline,style
:::tabs
::tab{title="npm"}
npm install
::
::tab{title="pnpm"}
pnpm add
::
:::tab → tab
Written:
::tab{…}…::Attributes:
title,label
:::tabs
::tab{title="npm"}
npm install
::
:::cards → cards
Written:
:::cards{…}…:::Holds:
cardAttributes:
columns,variant,border,iconAlign,iconSize,tileSize,titleWeight,titleSize,descriptionSize,background,backgroundDark,ctaPlacement,minWidth,gap,rowGap,divider,iconBand
:::cards{columns="2"}
::card{title="Quickstart" href="/quickstart"}
::card{title="API" href="/api"}
:::card → card
Written:
::card{…}(one line, no close)Also read:
:::card{…}:::on one lineAttributes:
title,href,cover,coverUrl,icon,description,iconUrl,iconStyle,color,cta,kicker,badge
:::cards
::card{title="Quickstart" href="/quickstart" icon="book"}
:::stepper → stepper
Written:
:::stepper{…}…:::Holds:
stepAttributes:
headingLevel,titleSize,collapsible
:::stepper
::step{title="Install"}
Run the installer.
::
::step{title="Sign in"}
Use your account.
::
:::step → step
Written:
::step{…}…::Attributes:
title,label,icon,badges,aside
:::stepper
::step{title="Install"}
Run the installer.
::
:::columns → columns
Written:
:::columns{…}…:::Holds:
columnAttributes:
breakout,caption
:::columns
::column
Left.
::
::column
Right.
::
:::column → column
Written:
::column{…}…::Attributes:
width,role,sticky
:::columns
::column{width="60"}
Left.
::
::column
Right.
::
:::grid → grid
Written:
:::grid{…}…:::Holds:
gridItemAttributes:
cols
:::grid{cols="2"}
::gridItem
One.
::
::gridItem
Two.
::
:::gridItem → gridItem
Written:
::gridItem{…}…::Attributes:
colSpan,colStart,rowStart,rowSpan,mdSpan,smSpan,bg,pad,align,border,gradient,textAlign,icon,iconStyle,accent
:::grid{cols="2"}
::gridItem{colSpan="2"}
Wide.
::
:::buttons → buttons
Written:
:::buttons{…}…:::Holds:
buttonAttributes:
align
:::buttons{align="center"}
:::button{label="Start" href="/start"}:::
:::button{label="Docs" href="/docs" style="secondary"}:::
:::button → button
Written:
:::button{…}:::(one line)Also read:
:::button{…}, the label on the next line, then:::Attributes:
label,href,style,icon,action,query,targetSlug,width
:::button{label="Deploy" href="/deploy" style="primary"}:::updates → updates
Written:
:::updates{…}…:::Holds:
updateAttributes:
format
:::updates
::update{date="2026-10-01" tags="api"}
Shipped the thing.
::
:::update → update
Written:
::update{…}…::Attributes:
date,tags
:::updates
::update{date="2026-10-01"}
Shipped.
::
:::audience → audience
Written:
::audience{…}…::Also read:
:::audience{…}…:::Attributes:
claim,equals,condition
::audience{claim="plan" equals="pro"}
Only Pro readers see this.
::embed → embed
Written:
:::embed{…}:::(one line)Also read:
::embed{url="…"}on one lineAttributes:
url
:::embed{url="https://www.youtube.com/watch?v=dQw4w9WgXcQ"}:::reusable → reusableRef
Written:
:::reusable{…}:::(one line)Attributes:
id,name
:::reusable{id="0b6c…" name="support-footer"}:::customcode → customCode
Written:
:::customcode{…}…:::Also read:
:::customcode{html="…" css="…"}:::on one lineAttributes:
data,html,css
:::customcode{css=".hero { color: tomato }"}
<div class="hero">Hello</div>
:::prompt → prompt
Written:
:::prompt{…}:::(one line)Also read:
:::prompt{…}, the prompt verbatim, then:::; a```promptfenceAttributes:
text,label,variant,wrap,targets,showText,actions,align,textStyle,collapse,layout,primary,icon,header,copyStyle,phoneTargets,labelCase,background,backgroundDark,collapseStyle,openInDesktop
:::prompt{label="Ask" text="Summarise this page"}:::field → field
Written:
:::field{…}…:::Attributes:
name,type,required,default,deprecated,location,badges
:::field{name="limit" type="integer" location="query" default="10"}
How many rows to return.
:::example → example
Written:
:::example{…}…:::Attributes:
kind,title
:::example{kind="request" title="cURL"}
```sh
curl https://api.example.com/v1/items
```
:::filetree → fileTree
Written:
:::filetree{…}…:::Attributes: —
:::filetree
- src/
- index.ts
- package.json
:::swatch → swatch
Written:
:::swatch{…}:::(one line)Attributes:
colors,layout,data
:::swatch{colors="Brand #4f46e5, Ink #111111"}:::Directives with their own vocabulary
These blocks are read by dedicated readers whose attributes are documented with the block in the authoring reference. data blocks carry their whole configuration as one encoded attribute: build the block once (in the editor or as JSON), export the page, and copy the line.
codeGroup → codeGroup
Written:
:::codeGroup{…}…:::Attributes: see
codeGroupin the authoring referenceSame block as:
:::codegroup
:::codeGroup{syncKey="pm"}
```sh title="npm"
npm install x
```
```sh title="pnpm"
pnpm add x
```
:::video → video
Written:
:::video{…}:::(one line)Attributes: see
videoin the authoring reference
:::video{src="https://cdn.example.com/demo.mp4"}:::spacer → spacer
Written:
:::spacer{…}:::(one line)Attributes: see
spacerin the authoring reference
:::spacer{size="lg"}:::rateLimit → rateLimit
Written:
:::rateLimit{…}:::(one line)Attributes: see
rateLimitin the authoring reference
:::rateLimit{limit="100" window="minute" scope="per API key"}:::errorCodes → errorCodes
Written:
:::errorCodes{…}:::(one line)Attributes: see
errorCodesin the authoring reference
:::errorCodes{rows="rate_limited|429|Too many requests."}:::systemStatus → systemStatus
Written:
:::systemStatus{…}:::(one line)Attributes: see
systemStatusin the authoring reference
:::systemStatus{org="00000000-0000-4000-8000-000000000001" space="00000000-0000-4000-8000-000000000002" style="status"}:::form → form
Written:
:::form{…}:::(one line)Attributes:
data(the block as encoded JSON — export one to get it)
quiz → quiz
Written:
:::quiz{…}:::(one line)Attributes:
data(the block as encoded JSON — export one to get it)
event → event
Written:
:::event{…}:::(one line)Attributes: see
eventin the authoring reference
:::event{spaceId="00000000-0000-4000-8000-000000000000" event="launch-party"}:::rsvp → eventRsvp
Written:
:::rsvp{…}:::(one line)Attributes: see
eventRsvpin the authoring reference
:::rsvp{spaceId="00000000-0000-4000-8000-000000000000" event="launch-party" label="Save my seat"}:::contactform → contactForm
Written:
:::contactform{…}:::(one line)Attributes:
data(the block as encoded JSON — export one to get it)
product → product
Written:
:::product{…}:::(one line)Attributes:
data(the block as encoded JSON — export one to get it)
lottie → lottie
Written:
:::lottie{…}:::(one line)Attributes:
data(the block as encoded JSON — export one to get it)
productCard → productCard
Written:
:::productCard{…}:::(one line)Attributes: see
productCardin the authoring reference
:::productCard{url="https://shop.example.com/p/air-max-1" endpoint="https://api.example.com/cards/air-max-1" layout="horizontal"}:::sources → sources
Written:
:::sources{…}:::(one line)Attributes:
data(the block as encoded JSON — export one to get it)
corrections → corrections
Written:
:::corrections{…}:::(one line)Attributes:
data(the block as encoded JSON — export one to get it)
compare → imageCompare
Written:
:::compare{…}:::(one line)Attributes: see
imageComparein the authoring reference
:::compare{before="https://cdn.example.com/a.png" after="https://cdn.example.com/b.png"}:::pricing → pricing
Written:
:::pricing{…}:::(one line)Attributes:
data(the block as encoded JSON — export one to get it)
testimonials → testimonials
Written:
:::testimonials{…}:::(one line)Attributes:
data(the block as encoded JSON — export one to get it)
logos → logoWall
Written:
:::logos{…}:::(one line)Attributes:
data(the block as encoded JSON — export one to get it)
stats → stats
Written:
:::stats{…}:::(one line)Attributes:
data(the block as encoded JSON — export one to get it)
hero → hero
Written:
:::hero{…}:::(one line)Attributes:
data(the block as encoded JSON — export one to get it)
person → person
Written:
:::person{…}:::(one line)Attributes:
data(the block as encoded JSON — export one to get it)
offices → officeMap
Written:
:::offices{…}:::(one line)Attributes:
data(the block as encoded JSON — export one to get it)
carousel → carousel
Written:
:::carousel{…}…:::Holds:
slideAttributes: see
carouselin the authoring reference
:::carousel{loop}
::slide
First.
::
::slide
Second.
::
:::slide → carouselSlide
Written:
::slide{…}…::Attributes: see
carouselSlidein the authoring reference
:::carousel
::slide{label="One"}
First.
::
:::gallery → gallery
Written:
:::gallery{…}…:::Attributes: see
galleryin the authoring reference
:::gallery{columns="2"}


:::marquee → marquee
Written:
:::marquee{…}…:::Attributes: see
marqueein the authoring reference
:::marquee{speed="slow"}
Acme · Globex · Initech
:::modal → modal
Written:
:::modal{…}…:::Attributes: see
modalin the authoring reference
:::modal{label="Watch the demo" title="Demo"}
Anything at all.
:::layer → layer
Written:
:::layer{…}:::(one line)Attributes: see
layerin the authoring reference
:::layer{kind="image" src="https://cdn.example.com/badge.png"}:::story → story
Written:
:::story{…}…:::Holds:
sceneAttributes: see
storyin the authoring reference
:::story
::scene{media="https://cdn.example.com/1.png"}
Step one.
::
:::scene → storyStep
Written:
::scene{…}…::Attributes: see
storyStepin the authoring reference
:::story
::scene
Step one.
::
:::playground → playground
Written:
:::playground{…}…:::Attributes: see
playgroundin the authoring reference
:::playground
```html
<button>Hi</button>
```
:::walkthrough → walkthrough
Written:
:::walkthrough{…}…:::Holds:
stepAttributes: see
walkthroughin the authoring reference
:::walkthrough{language="ts"}
```ts
const a = 1;
const b = 2;
```
::step{lines="1"}
The first line.
::
:::step → walkthroughStep
Written:
::step{…}…::Attributes: see
walkthroughStepin the authoring reference
:::walkthrough
```ts
const a = 1;
```
::step{lines="1"}
One line.
::
:::feed → spaceFeed
Written:
:::feed{…}:::(one line)Attributes: see
spaceFeedin the authoring reference
:::feed{}:::categories → spaceCategories
Written:
:::categories{…}:::(one line)Attributes: see
spaceCategoriesin the authoring reference
:::categories{}:::band → spaceBand
Written:
:::band{…}:::(one line)Attributes: see
spaceBandin the authoring reference
:::band{name="search"}:::leaderboard → spaceLeaderboard
Written:
:::leaderboard{…}:::(one line)Attributes: see
spaceLeaderboardin the authoring reference
:::leaderboard{}:::Blocks written in plain Markdown
Block | Spelling |
|---|---|
| a paragraph |
|
|
|
|
|
|
|
|
| an item of either list |
|
|
| one |
| a |
| a |
|
|
|
|
|
|
|
|
| a ` |
| a table row |
| a header cell |
| a cell |
Blocks with no hand-written form
Block | Why |
|---|---|
| a shape's named slot — write slots with |
| drawn in the editor; exported as a |
| an installed extension's block; exported as a marker comment |
| generated by the OpenAPI importer ( |
This page is generated from the importer's own tables (packages/docs-schema/src/markdown-dialect.ts); a test fails when the two disagree.