Display.dev renders Markdown to themed HTML on publish. The viewer experience aligns with the Markdown Experience Guidelines (MDXG).
Markdown syntax
Display-flavored Markdown uses CommonMark as its base. Standard syntax works as you'd expect – headings, paragraphs, lists, blockquotes, links, images, inline and fenced code, and horizontal rules. Published pages also support these GFM features:
- Tables – pipe-delimited, GFM-style.
- Strikethrough –
~~text~~. - Task lists –
- [ ] itemshows an unchecked box;- [x] itemor- [X] itemshows a checked box. Published checkboxes are read-only. Change the Markdown and publish an updated version to change their state. - Auto-linking – bare URLs and email addresses become clickable links. Wrap a URL in
[text](url)only when you want different link text.
Display.dev adds the author notes, highlights, image dimensions, footnotes, math, Mermaid diagrams, and callouts documented below. Published raw HTML follows Display.dev's sanitization rules rather than GFM's raw HTML tag filter. Auto-linking uses Display.dev's supported top-level domain list rather than GitHub's exact recognition rules.
Author notes
Wrap an author note in %% to omit it from the rendered page:
Visible text %%check this wording%% continues here.
For notes containing blank lines, put each delimiter on its own line:
%%
Check the figures before the next update.
Confirm the conclusion too.
%%Unclosed comments and markers inside code stay visible. Escape a marker as
\%\% to display it literally. Author notes remain in Markdown source and
Copy as Markdown. They are not private and are separate from discussion comments.
Existing versions keep their rendered content; publish an updated version to
apply this syntax.
Highlights
Wrap text in == to highlight it: ==important text==. You can include
emphasis inside a highlight, such as ==**important** text==. Leave no spaces
immediately inside the delimiters. Markers inside code remain literal.
Image dimensions
Add |300 to an image description for a width of 300 pixels, or |300x200
for a width and height. Images shrink to fit narrow screens. The size suffix
does not appear in the accessible description.

This also works with self-contained Base64 image URLs. Dimensions must be positive whole numbers; invalid suffixes remain part of the description.
Themes
Pick a theme on publish with --theme. Available:
github(default)modestpandocairsolarized
Every theme adapts to the viewer's dark-mode preference. The --theme flag has no effect when publishing HTML.
dsp publish report.md --theme pandocHeading anchors
Every Markdown heading gets an id derived from its text. A # permalink appears on hover – click it to copy a link to that section:
https://<org>.dsp.so/<shortId>-<slug>#section-title
The id strips punctuation and preserves Unicode letters. Repeated heading text gets a numeric suffix (setup, setup-1). Multi-page documents extend the URL with a page slug.
Raw HTML headings render without these generated ids or permalink controls.
Multi-page documents
An artifact with two or more # H1 headings automatically renders as a multi-page document. A navigation panel on the left titled "On this doc" lists every page, the active page expands to show its outline, and a prev/next footer at the bottom of the page connects them. One # H1 (or none) renders as a single scrolling page.
Display.dev splits on # H1 only, not ## H2. An inline # H1 typically signals a major section, while H2s subdivide a page. Content before the first # H1 becomes an Introduction page.
URLs
In single-page documents the URL fragment is the heading id (#section-title). In multi-page documents it identifies the page:
| Fragment | Behavior |
|---|---|
(none) | First page |
#<page-slug> | Open that page |
#<page-slug>--<heading-id> | Open that page and scroll to the heading |
Old #heading-id bookmarks continue to work – they resolve to the page containing the heading.
Keyboard
- Arrow Up / Arrow Down move focus through the page list.
- Enter opens the focused page.
- Page Up / Page Down move to the previous / next page from anywhere outside form fields.
- Esc returns focus to the article body.
The top-bar Copy as Markdown button becomes per-page in multi-page documents – see Source export.
Table of contents
Single-page documents with two or more H2 sections get a collapsible table of contents on the left that highlights the current section as you scroll. Don't write your own – Display.dev builds it from H2 and H3 headings.
Multi-page documents replace the TOC with a per-page outline inside the side nav: when a page is active, its H2 and H3 headings expand inline under its row.
Search
Cmd+F (or Ctrl+F) opens an in-document find input that cycles through matches with a counter. In multi-page documents it walks hidden pages too and shows the page name next to each match, so matches outside the active page aren't skipped.
Cmd+K (or Ctrl+K) opens a palette of ranked pages with snippets. The palette is only available in multi-page documents.
Footnotes
[^label] references become superscript links to a footnote section at the bottom of the document. Footnotes can be referenced multiple times; each reference gets a back-link in the definition.
The migration[^why] took a week.
[^why]: Originally scheduled for one day. Estimates remain hard.A reference with no matching definition renders as literal [^label] text. A definition with no reference is dropped silently.
In multi-page documents, footnote definitions are redistributed per page – each page that references a footnote gets its own copy at the bottom, with back-links pointing to the reference on that page.
Smart punctuation
These character sequences get transformed at publish time:
---→ em dash--→ en dash...→ ellipsis(c),(C)→ ©(r),(R)→ ®(tm),(TM)→ ™+-→ ±- Straight quotes around words → curly quotes
Fenced code blocks bypass the transform – code samples render exactly as typed.
Code blocks
Fenced blocks (```language) render in a monospace block with the theme's background color.
Math (KaTeX)
Wrap math in $…$ for inline, $$…$$ for a centered display block. The LaTeX is rendered to HTML when you publish.
The loss is $\mathcal{L} = -\sum_i y_i \log \hat{y}_i$.
$$
\frac{\partial \mathcal{L}}{\partial z_j} = \hat{y}_j - y_j
$$Supported: the KaTeX function set. MathML, asciimath, and other math syntaxes are not.
Invalid LaTeX shows as an inline error instead of breaking the page. Prose like $5 fee or It costs $100. is left alone – math only parses when the opening $ has whitespace or punctuation before it and the closing $ has whitespace or punctuation after it. Links inside math (\href) are disabled.
Diagrams (Mermaid)
Fence a block with mermaid and the diagram renders in the viewer's browser. Flowcharts, sequence diagrams, state diagrams, ER diagrams, Gantt charts, and the rest of the Mermaid syntax are supported.
```mermaid
flowchart LR
A[Markdown] --> B[Display.dev] --> C[Themed HTML]
```The diagram follows the page theme. Toggle dark mode and the colors update without a reload.
Invalid syntax shows an error in place of the diagram instead of breaking the page. Clickable links inside diagrams are disabled.
Callouts
Start a blockquote with a type marker and an optional plain-text title:
> [!tip] Try this
> The body supports **Markdown**.
>
> > [!note]- More detail
> > This nested callout starts collapsed.Common types include note, info, tip, warning, danger, question,
example, and quote. Types are case-insensitive. Unknown types use note
styling. Without a title, the type supplies one. Add + after the marker for
an initially expanded callout or - for an initially collapsed one; omit both
for a callout that stays expanded. Readers can open and close foldable callouts
without changing the Markdown. Existing versions keep their rendered content;
publish an updated version to apply this syntax.
Raw HTML
HTML is limited to 128 nested elements. An HTML fragment exceeding that depth is omitted from the rendered page; its original source remains available.
Markdown can include sanitized HTML for paragraphs, headings, emphasis, lists, tables, links, code and embedded raster images. Markdown syntax inside a complete HTML element stays literal:
<div style="color:teal;text-align:center">
<strong>HTML formatting works.</strong>
**These Markdown delimiters stay visible.**
</div>Inline styles support text and background colors, font weight/style, text decoration, and alignment. Colors use three/six-digit hex or a limited set of named colors. Layout rules, CSS functions and URLs are removed.
Scripts, event handlers, forms, frames, SVG/MathML, IDs, classes, and unsupported
attributes are removed from the rendered page. Raw HTML images must use embedded
Base64 PNG, JPEG, GIF, WebP or AVIF data; external raw HTML image URLs are removed.
Markdown image syntax () keeps its existing behavior. Safe HTML links
work in the published page. The original source remains available for export.
To publish interactive content, create a new HTML artifact. An existing Markdown artifact keeps its format.
What's not supported
A few syntaxes and features from other Markdown viewers aren't supported here:
- Definition lists – the
term/: definitiontwo-line pattern renders as plain text. - Embedded media –
<video>,<audio>, and<iframe>are not supported in Markdown. Create a new HTML artifact to embed media. - Code-block syntax highlighting – fenced blocks render as plain monospace. Pre-highlight as HTML and publish that file directly if you need it.
- Per-block code copy buttons – the in-viewer Copy button copies the complete document or the active page, not individual code blocks.
Source export
Display.dev keeps the original Markdown alongside the rendered HTML. Two ways to get it back:
dsp export <shortId>– returns the original Markdown.- The Copy button in the viewer's top bar – copies the source to the clipboard. On single-page documents it is labeled Copy as Markdown. On multi-page documents it becomes Copy this page (or Copy intro on the Introduction page), with a secondary Copy whole document link for the full source.
If the artifact was published as HTML, dsp export <shortId> --format markdown converts the HTML back to Markdown.