Preview local markdown files in the browser with GitHub-flavored rendering, mermaid diagrams, math, syntax highlighting, and live reload. Zero runtime dependencies.
$ draft README.md
* Serving on http://localhost:6419/pip install markdraftOr with uv:
uv tool install markdraftDownload markdraft.pyz from
Releases
and run it directly — no installation needed:
python markdraft.pyz README.mdRequires Python 3.10+. On first run, markdraft downloads about 3.5 MB
of JavaScript libraries from jsDelivr and
caches them in ~/.markdraft/.
Preview a file:
$ draft README.md
* Serving on http://localhost:6419/Preview a directory (serves its README.md):
$ draft .Open the browser automatically:
$ draft -b README.mdSpecify host and port:
$ draft README.md 0.0.0.0:8080Export to a self-contained HTML file:
$ draft --export README.md
Exporting to README.htmlExport with CDN links instead of inlined assets:
$ draft --export --no-inline README.mdDark mode:
$ draft --theme=dark README.mdThe mdraft command is also available as an alias, for environments
where draft conflicts with Azure Draft.
- Live reload — file changes are detected and the browser refreshes automatically via Server-Sent Events
- Mermaid diagrams —
```mermaidfenced code blocks rendered by mermaid.js - Math/LaTeX —
$inline$and$$display$$math rendered by KaTeX - Syntax highlighting — code blocks highlighted by highlight.js
- GitHub Alerts —
> [!NOTE],> [!TIP],> [!IMPORTANT],> [!WARNING],> [!CAUTION]styled callout boxes - GeoJSON maps —
```geojsonand```topojsonrendered as interactive maps by Leaflet - STL 3D models —
```stlrendered as rotating 3D views by Three.js - Task lists —
- [x]and- [ ]checkboxes - Emoji shortcodes —
:rocket:→ 🚀, full gemoji set (1,800+ shortcodes) - GitHub styling — rendered with github-markdown-css
- Export — self-contained HTML files with all assets inlined or linked via CDN
- Zero dependencies — no pip runtime dependencies; rendering is done client-side by cached JavaScript libraries
- Auto/dark/light mode — follows OS preference by default
(
--theme=auto), or force with--theme=dark/--theme=light - Standalone executable — download a single
.pyzfile, no installation required
The examples below exercise every client-side rendering feature.
Running draft README.md and checking that each one renders correctly
is a quick smoke test for a new installation.
def fibonacci(n: int) -> int:
a, b = 0, 1
for _ in range(n):
a, b = b, a + b
return agraph LR
A[Markdown file] --> B[HTTP Server]
B --> C[Browser]
C --> D[marked.js]
D --> E[Rendered HTML]
Euler's identity:
The Gaussian integral:
Note
This is a note — useful for supplementary information.
Tip
This is a tip — helpful advice for the reader.
Important
This is important — key information the reader should know.
Warning
This is a warning — something that could cause problems.
Caution
This is a caution — potential for data loss or security risk.
{
"type": "FeatureCollection",
"features": [
{
"type": "Feature",
"geometry": {
"type": "Polygon",
"coordinates": [[
[-122.42, 37.78], [-122.42, 37.77], [-122.41, 37.77],
[-122.41, 37.78], [-122.42, 37.78]
]]
},
"properties": { "name": "San Francisco" }
}
]
}
🚀 ✨
- Syntax highlighting
- Mermaid diagrams
- Math / LaTeX
- GitHub Alerts
- GeoJSON maps
- Task lists
- Emoji shortcodes
| Library | Purpose | Size |
|---|---|---|
| marked.js | Markdown rendering | 40 KB |
| highlight.js | Syntax highlighting | 40 KB |
| KaTeX | Math rendering | 270 KB |
| mermaid.js | Diagrams | 2.9 MB |
usage: draft [-h] [-V] [--user-content] [--wide] [--clear] [--export]
[--no-inline] [-b] [--title TITLE] [--norefresh] [--quiet]
[--theme THEME]
[path] [address]
| Flag | Description |
|---|---|
path |
File or directory to render (- for stdin) |
address |
host:port to listen on, or output file for --export |
-b, --browser |
Open browser tab after server starts |
--export |
Export to HTML file instead of serving |
--no-inline |
Use CDN links instead of inlining assets in export |
--title TITLE |
Override the page title |
--theme THEME |
Color theme: auto (default), light, or dark |
--user-content |
Render as GitHub issue/comment style |
--wide |
Wide layout (with --user-content) |
--norefresh |
Disable auto-refresh on file change |
--quiet |
Suppress terminal output |
--clear |
Clear the cached assets and exit |
-V |
Show version and exit |
from markdraft import serve, export, clear_cache
# Start a preview server
serve("README.md", port=8080, browser=True)
# Export to HTML
export("README.md", out_filename="preview.html")
# Clear cached CDN assets
clear_cache()Create ~/.markdraft/settings.py to override defaults:
HOST = "0.0.0.0"
PORT = 8080
AUTOREFRESH = True
QUIET = FalseThe MARKDRAFT_HOME environment variable overrides the config directory
(default ~/.markdraft).
Markdraft is a thin HTTP server built on Python's http.server. It
serves raw markdown via a JSON API and lets the browser handle all
rendering:
sequenceDiagram
participant B as Browser
participant S as Server (http.server)
B->>S: GET /
S-->>B: HTML shell (template + script tags)
B->>S: GET /__/api/content
S-->>B: raw markdown as JSON
Note over B: marked.js renders markdown
Note over B: KaTeX renders math
Note over B: highlight.js highlights code
Note over B: mermaid.js renders diagrams
B->>S: GET /__/api/refresh (SSE)
S-->>B: file change notifications
Modules:
| Module | Purpose |
|---|---|
markdraft/server.py |
HTTP server with routing |
markdraft/readers.py |
File/directory/stdin reading |
markdraft/assets.py |
CDN asset downloading and caching |
markdraft/export.py |
Self-contained HTML export |
markdraft/watcher.py |
File change detection for auto-refresh |
markdraft/browser.py |
Browser tab opening |
markdraft/config.py |
Constants, CDN URLs, settings loader |
markdraft/command.py |
CLI argument parsing |
git clone https://github.com/imofftoseethewizard/markdraft
cd markdraft
uv sync
uv run pytest # 148 tests, ~8s parallel
uv run pyright markdraft/ # type checking
uv run black markdraft/ tests/ # formattingMarkdraft began as a fork of Grip by Joe Esposito. Grip is a well-crafted tool for previewing GitHub-flavored markdown locally, and its reader abstractions and CLI design informed markdraft's architecture.
- Zero runtime dependencies — Grip depends on Flask, Markdown, Pygments, requests, docopt, path-and-address, and Werkzeug. Markdraft has no pip dependencies; it uses only the Python standard library.
- Client-side rendering — Grip renders markdown server-side with Python. Markdraft serves raw markdown and renders it in the browser with marked.js, highlight.js, and mermaid.js.
-
Math/LaTeX support —
$inline$and$$display$$math via KaTeX. -
GitHub Alerts —
> [!NOTE],> [!WARNING], etc. rendered as styled callout boxes. -
Mermaid diagram support —
```mermaidfenced code blocks rendered as diagrams. - GeoJSON maps and STL 3D models — interactive maps via Leaflet, rotating 3D model views via Three.js.
-
stdlib HTTP server — replaces Flask with
http.server.ThreadingHTTPServer. - Modern Python — requires Python 3.10+, full type annotations, no Python 2 compatibility code.
-
uv project management —
pyproject.tomlwith hatchling build backend, managed by uv. -
Security hardening — path traversal protection via
Path.relative_to(), symlink escape prevention, case-insensitive</script>escaping in exports. -
Standalone executable — downloadable
.pyzfile, no installation required. - GitHub API removed — Grip's primary mode was to POST markdown to the GitHub API for rendering. Markdraft renders entirely offline.
MIT