Markdown essentials
Every Markdown construct sweetdocs supports — what works, what doesn't, and the few extensions we add on top. Verified against the Tiptap + tiptap-markdown configuration.
sweetdocs stores docs as Markdown source, not HTML. Every keystroke serializes to the same Markdown you’d write by hand — paste a doc into another Markdown editor and it works.
This page is the syntax cheat sheet plus the precise list of what we do and don’t support.
Headings
Six levels, plain Markdown:
# H1
## H2
### H3
#### H4
##### H5
###### H6
Or use the slash menu (H1, H2, H3 are the menu items) / the bubble toolbar (H1, H2, H3 buttons appear on selection). H4–H6 are accessible only via raw Markdown — they’re not in either menu.
Inline formatting
| Style | Syntax | Shortcut |
|---|---|---|
| Bold | **bold** or __bold__ | ⌘B |
| Italic | *italic* or _italic_ | ⌘I |
~~strike~~ | ⌘⇧X | |
Inline code | `code` | — (toolbar or backticks) |
Links
[link text](https://example.com)
Reference-style ([text][1] plus a separate [1]: url line) does parse on paste, but it gets converted to the inline form when the doc round-trips through our serializer. Don’t rely on the reference syntax surviving.
Plain URLs typed into prose autolink (https://example.com becomes clickable as you type). Links carry rel="noopener noreferrer nofollow" and open in a new tab.
Lists
Bullet lists — -, *, or + all work and round-trip as -:
- First
- Second
- Nested
Ordered lists:
1. First
2. Second
3. Third
Task lists (- [ ] todo) are not supported — we don’t load the Tiptap task-list extension. The square brackets render as literal text.
Blockquotes
> A blockquote.
> Continues across lines.
Code blocks
Fenced with three backticks, optional language hint after the opening fence:
```javascript
const greet = name => `Hello, ${name}!`
```
The language hint is preserved in the source but there is no syntax highlighting in v1 — code renders as plain monospace on a subtle background. See Code blocks for the details.
Inline code uses single backticks: `code`.
Tables
GFM pipe tables. The header row + separator row are required:
| Column A | Column B | Column C |
|---|---|---|
| Cell | Cell | Cell |
Column alignment (GFM :--- / ---: / :---:):
| Left | Center | Right |
|:---|:---:|---:|
| a | b | c |
Tables in sweetdocs are not resizable by drag (we set resizable: false). Column widths follow content. Insert from the slash menu or paste from a spreadsheet — pasting from Excel / Google Sheets converts the TSV to a real Markdown table.
Horizontal rule
Three or more of any of: -, *, _ on their own line:
---
Images
Standard Markdown:

For our width + alignment extension, sweetdocs serializes inline HTML so the attributes survive export. See Image layout for the toolbar; the under-the-hood shape is:
<img src="…" alt="…" width="320" class="img-align-center" data-align="center" />
This is standard HTML — other Markdown tools that render embedded HTML show it correctly. The image’s src is a stable URL into our R2-backed storage, so the image keeps loading after export.
See Adding images for paste / drag / upload behaviour.
Smart typography (automatic)
The Typography extension is on. As you type, common ASCII gets replaced by their typographic equivalents:
| You type | Becomes |
|---|---|
-- | – (en-dash) |
--- | — (em-dash) |
... | … (ellipsis) |
"hello" | ”hello” (curly quotes) |
'world' | ’world’ (curly quotes) |
(c) | © |
(r) | ® |
(tm) | ™ |
1/2, 1/4, 3/4 | ½ ¼ ¾ |
You can always undo (⌘Z) the substitution if you want the literal characters.
Inline HTML
Inline HTML passes through in both Edit and Preview modes. We need this for the image width/align attributes; we sanitize the Preview render with DOMPurify (so scripts, event handlers, and unsafe attributes are stripped) but keep image attributes, classes, and data-* intact.
Most users never need to write HTML by hand — the toolbars + slash menu cover everything that has a Markdown shape.
sweetdocs extensions on top of Markdown
A small list of things we add that aren’t in plain Markdown:
Hex color swatches
Type any hex literal (#ed4d92, #FFFFFF, #FFFFFFAA) anywhere in body text and a clickable color chip renders next to it in both Edit and Preview. The Markdown source is just the literal — no special syntax.
Comment anchors
When you select text and add a comment, the anchor is stored as an inline <span data-comment-id="…" class="comment-anchor">…</span>. The comment thread itself lives in a separate table and is rendered by the Comments panel.
Image width + alignment
See above — inline HTML attributes, accessible via the image toolbar.
Slash menu
Press / in the editor for block insertion. Not a Markdown extension — a UI shortcut. See The slash menu.
What’s not supported
Things people often look for that aren’t in v1:
| Construct | Why |
|---|---|
Task lists (- [ ] / - [x]) | TaskList Tiptap extension not loaded |
Footnotes ([^1]) | No Footnote extension |
Definition lists (Term : definition) | No DefinitionList extension |
Math / LaTeX ($x^2$, $$ … $$) | No Math extension |
Callouts / admonitions (:::note … :::) | No callout syntax — use a blockquote |
Highlight (==text==) | No Highlight extension |
Subscript / superscript (H~2~O, x^2) | No sub / sup extensions |
Shortcode emoji (:smile:) | Not parsed — paste the actual Unicode character |
| Reference-style links | Parsed on paste, but converted to inline form on save |
Any of these will simply render as literal characters in Preview mode.
Render pipeline
The two modes don’t share an engine — they’re kept in sync by sharing the same Markdown source string:
- Edit mode: Tiptap with the extensions listed in the editor source — your edits are CRDT-tracked via Yjs.
- Preview mode: the Markdown is parsed by
marked(with GFM enabled by default), sanitized by DOMPurify, then color-swatches injected. - Both modes write back to the same
contentfield on save.
In practice the two produce identical output for every construct on this page. The handful of edge cases where they could diverge (raw <script> tags, exotic CSS) are blocked by DOMPurify.