Why format Markdown at all?
Markdown renders the same whether a table’s pipes line up or not, so why bother? Because people read the source too: in pull request diffs, in terminals, in editors without preview. A README edited by ten contributors ends up with three list markers, two heading styles, tables whose columns zig-zag, and paragraphs wrapped at random widths. Consistent source also produces smaller, more meaningful diffs.
This formatter targets GitHub-flavoured Markdown (GFM): tables, task lists (- [x]), strikethrough, autolinks and fenced code blocks. Layout comes from Prettier’s Markdown printer, the same one used by prettier --write "**/*.md" in countless repositories, followed by a pass that applies the marker choices Prettier does not expose.
Working with the editor
Paste a document or drop a .md file; headings, list markers and links are enough for auto-detection to pick Markdown. Ctrl/Cmd+Enter formats and Ctrl/Cmd+Shift+C copies the result. Open the Preview tab for a rendered view: it handles GFM tables, task-list checkboxes and mermaid code fences rendered as diagrams, and it is sanitised before display so raw HTML in the document cannot run scripts. Ctrl/Cmd+K opens the command palette, which also offers Markdown to HTML and Markdown to PDF conversions.
Draft release notes and internal docs stay on your machine, because both the formatting and the preview happen in the page.
Options
- Wrap prose: As written keeps your line breaks inside paragraphs. At line width reflows every paragraph to the line width, the classic 80-column style. Never (one line per paragraph) joins each paragraph into a single line, which suits editors with soft wrap and gives cleaner diffs when a sentence changes.
- List marker:
-,*or+for bullet lists. When two separate lists follow each other, the second uses a different marker on purpose; otherwise Markdown would merge them into one list. - Emphasis:
_underscore_or*asterisk*for italics. Emphasis inside a word (un*believ*able) always uses asterisks, because underscores do not work mid-word in CommonMark. - Bold:
**asterisks**or__underscores__, with the same mid-word exception. - Tables: Aligned columns pads every cell so the pipes form straight columns. Compact writes
| a | b |with single spaces, which keeps diffs small when one long cell would otherwise re-pad the whole table. Column alignment markers (:--,:-:,--:) are kept in both styles.
Indent and line width apply as on other pages; line width only has an effect when Wrap prose is set to At line width.
What is normalised and what is left alone
Setext headings (a line underlined with === or ---) become ATX headings (# and ##). Ordered lists keep their numbering style: 1. 2. 3. stays sequential, while a list that starts 1. 1. keeps all ones, a convention that avoids renumbering diffs. Escape characters are added only where needed to keep the same meaning.
Code is never reformatted: fenced and indented code blocks, inline code and HTML blocks are copied exactly. Front matter at the top of the file (YAML between --- lines, as used by Jekyll, Hugo and Astro) is preserved too.
Examples
README with mixed heading and list styles
The underlined headings become # and ##, the three bullet markers become dashes, and the table columns line up.
Order Service
=============
A small service that accepts orders and emits events.
Setup
-----
* Install Node 22
+ Run `npm ci`
* Copy `.env.example` to `.env`
|Variable|Required|Default|
|:--|:-:|--:|
|DATABASE_URL|yes||
|PORT|no|8080|
|LOG_LEVEL|no|info|
Order Service
=============
A small service that accepts orders and emits events.
Setup
-----
- Install Node 22
* Run `npm ci`
- Copy `.env.example` to `.env`
| Variable | Required | Default |
| :----------- | :------: | ------: |
| DATABASE_URL | yes | |
| PORT | no | 8080 |
| LOG_LEVEL | no | info |
Changelog reflowed to the line width
Long bullet points are wrapped at 80 columns with correct continuation indentation, and bold is rewritten as bold.
## 2.4.0 - 2026-09-14
### Added
- Refunds can now be issued for part of an order, with the refunded amount recorded on each line item so that finance exports match the payment provider's settlement reports exactly.
- New `/orders/{id}/events` endpoint.
### Fixed
- Webhook retries no longer send duplicate __order.paid__ events when the first delivery times out after the receiver has already processed it.
## 2.4.0 - 2026-09-14
### Added
- Refunds can now be issued for part of an order, with the refunded amount
recorded on each line item so that finance exports match the payment
provider's settlement reports exactly.
- New `/orders/{id}/events` endpoint.
### Fixed
- Webhook retries no longer send duplicate **order.paid** events when the first
delivery times out after the receiver has already processed it.
Docs page with compact tables and asterisk lists
The table is written with single spaces between pipes, task-list items switch to asterisks, and per API key becomes per API key.
# Rate limits
Requests are limited _per API key_.
| Plan | Requests per minute | Burst |
| --- | --- | --- |
| Starter | 60 | 10 |
| Team | 600 | 100 |
| Enterprise | Custom | Custom |
- [x] Limits apply to all endpoints
- [ ] Per-endpoint limits (planned)
# Rate limits
Requests are limited *per API key*.
| Plan | Requests per minute | Burst |
| --- | --- | --- |
| Starter | 60 | 10 |
| Team | 600 | 100 |
| Enterprise | Custom | Custom |
* [x] Limits apply to all endpoints
* [ ] Per-endpoint limits (planned)
Common errors and how to fix them
| Error | Cause | Fix |
|---|---|---|
Everything after a code block renders as code | A fenced code block was opened with ``` but never closed, so the rest of the document is inside it. | Add the closing ``` on its own line; the formatted output makes the problem visible because nothing after the fence is reformatted. |
The table shows up as plain text with pipes | GFM tables need a delimiter row such as | — | — | directly under the header row, and a blank line before the table. | Add the delimiter row with one — per column, and separate the table from the preceding paragraph with an empty line. |
Line breaks disappear in the preview | In Markdown, a single newline inside a paragraph is a space. Lines only break at a blank line, a trailing backslash or two trailing spaces. | Use a blank line for a new paragraph or end the line with a backslash for a hard break; trailing spaces are invisible and easily lost. |
A numbered list restarts at 1 halfway through | Content between items (a code block or paragraph) is not indented under the list item, so it ends the list. | Indent the content to line up with the item text, usually three spaces for "1. ", so it stays inside the item. |
Frequently asked questions
Does the formatter follow GitHub’s Markdown rules?
Yes. Parsing follows CommonMark with the GitHub extensions (tables, task lists, strikethrough, autolinks), and the preview renders the same features GitHub does, plus Mermaid diagrams in mermaid code fences.
How do I align a Markdown table quickly?
Paste the table and press Ctrl/Cmd+Enter with Tables set to Aligned columns. Wide characters such as CJK text and emoji are measured by display width, so columns line up in a monospace editor.
Will it change my code blocks?
No. The contents of fenced and indented code blocks and inline code are copied unchanged. Use the formatter for the code’s own language if you want it reformatted.
Which wrap setting is best for Git?
Never (one line per paragraph) or semantic line breaks give the cleanest diffs, because editing one sentence does not re-wrap the rest of the paragraph. At line width suits files read mostly in terminals.
Is my document uploaded for the preview?
No. The Markdown is rendered to HTML in your browser and sanitised there; there is no server-side rendering step.