📝 mdo — Markdown to HTML5 Converter (with optional live watch)
mdo is a small, fast command-line tool written in Rust that converts
Markdown files into HTML.
By default it produces a complete, HTML5-compliant document styled with simple.css (vendored at build time — no network access at runtime). An optional watch mode keeps re-rendering the output whenever the Markdown source is edited.
Project site: https://maphew.github.io/mdo/
🚀 Features
- ✅ Converts
.mdfiles to standalone HTML5 documents - 🎨 Pretty default styling via embedded simple.css
- 🌓 Automatic light/dark mode (follows OS) plus a manual toggle button
- 📄
--bareflag emits a raw HTML fragment (no<html>/<head>/<body>/CSS) - 👀
--watchflag enables auto-rerender on file change (with debouncing) - 🌐
--openflag renders to a temp dir and launches the system default browser - ⚡ Fast and self-contained — single binary, no runtime assets
- 🧩 Built on
pulldown-cmark,clap, andnotify
📦 Installation
From crates.io
Build from source
📦 Usage
Usage: mdo [OPTIONS]
Arguments:
Input Markdown file
Options:
-o, --output Output HTML file (defaults to .html alongside the input,
or to a temp directory when --open is used). Existing files are overwritten
-w, --watch Watch the input file and re-render on every change
-b, --bare Emit only the raw HTML fragment (no , , , no CSS)
--open Render to a temp directory and launch the system default browser.
The source folder is left untouched unless --output is given
-h, --help Print help
-V, --version Print version
If --output is omitted, the output is written next to the input with the
extension changed to .html (e.g. foo.md → foo.html). Existing files are
overwritten without prompting.
When --open is used without --output, the rendered HTML goes to a stable
location under your OS temp directory (e.g.
%TEMP%\mdo\<hash>\<name>.html on Windows) so the source folder stays
clean. Re-opening the same file overwrites the same temp output. A
<base href="file:///…"> tag pointing at the source folder is automatically
injected whenever the output lives elsewhere, so relative images and links in
the Markdown still resolve correctly.
Examples
Convert once and exit (default — produces a styled, standalone HTML5 page next to the input). The README.html in this repo is generated like this:
Emit a bare HTML fragment (useful for embedding in another template):
Watch for changes and re-render on every save:
Render to a temp file and open it in your default browser (does not write next to the source):
This is the recommended setup for a Windows Open as HTML file
association — use the bundled mdo-open.exe wrapper and double-clicking
a .md file in Explorer will render to the platform temp directory and
open it without leaving any artifacts in the source folder.
Linux file manager integration
The repo also ships Linux helpers under scripts/ for per-user
file-manager integration:
# Add "Open as HTML" as an "Open With" handler for Markdown files
# Same, but also make it the default Markdown handler
# Undo everything the install script did
The installer writes ~/.local/share/applications/mdo.desktop, whose
command is mdo --open %f, plus a small Ⓜ SVG icon under
~/.local/share/icons/hicolor/scalable/apps/mdo.svg. The desktop entry is
named Open as HTML, so GNOME Files/Nautilus and other XDG file managers
show an action-oriented entry instead of a tool-name-only entry. Rerunning the
installer also removes older Nautilus Scripts entries named Preview with
mdo or Render with mdo.
Pass --exe /path/to/mdo if the binary is not on PATH. The script looks
for mdo on PATH first, then falls back to target/release/mdo
next to this repo after cargo build --release.
Result examples after install:
- Most XDG file managers: right-click a
.mdfile → Open With → Open as HTML. - With
--set-default: double-clicking a Markdown file launchesmdo --open. - The rendered page opens in your default browser from a temp path such as
/tmp/mdo/<hash>/<name>.html, and no.htmlfile is left beside the source.
🪟 Windows Explorer integration
The repo ships two PowerShell helpers under scripts/ that wire
mdo into Explorer for the current user only (no admin, no HKLM changes):
# Add: an "Open as HTML" right-click verb and Open With app entry
powershell -ExecutionPolicy Bypass -File .\scripts\install-explorer.ps1
# Undo everything the install script did
powershell -ExecutionPolicy Bypass -File .\scripts\uninstall-explorer.ps1
The install script registers mdo-open.exe, a tiny windows-subsystem
wrapper built alongside mdo.exe. The wrapper exists for one reason:
when Explorer launches a normal console binary it briefly flashes a black
console window. mdo-open.exe runs as a GUI subsystem app and spawns
mdo.exe --open with CREATE_NO_WINDOW, so double-clicking a .md
file renders from the platform temp directory and opens straight in the
browser with no flash. The regular CLI is
unchanged — mdo.exe from a terminal still prints to stdout normally.
The install script auto-locates mdo-open.exe via PATH, falling
back to target\release\mdo-open.exe next to the repo. Pass
-ExePath C:\path\to\mdo-open.exe to override. mdo.exe must
sit next to mdo-open.exe; both are produced by cargo build --release and cargo install mdo-cli.
It also generates a small .ico at %LOCALAPPDATA%\mdo\md.ico by
rendering a single Unicode character — by default Ⓜ (circled M) in a
mid-tone blue chosen so it stays legible in both light and dark Explorer
themes. Override either via parameters:
.\scripts\install-explorer.ps1 -IconChar "📄" -IconColor "#E64A19"
uninstall-explorer.ps1 removes the .ico (and its folder if empty)
along with all the registry keys. It also removes the old Preview with mdo
and Render with mdo verbs from earlier installs.
To make Open as HTML the default .md handler after running the install
script, right-click a .md file → Open with → Choose another app →
pick Open as HTML → tick Always use this app. Windows requires that
last step to be done interactively.
Result examples after install:
- Right-click any
.mdfile → Open as HTML. On Windows 11, this may appear under Show more options. - Open with → Open as HTML appears as an available app for Markdown files.
- If you make Open as HTML the default handler, double-clicking a
.mdfile opens the rendered page in your browser with no console-window flash. - The rendered page opens from
%TEMP%\mdo\<hash>\<name>.html, and the source folder stays unchanged.
macOS Finder quick action
mdo does not ship a macOS installer script yet, but Finder can run mdo --open
through a per-user Automator Quick Action. Apple documents Quick Action
workflows and shell-script actions in the Automator User Guide:
- https://support.apple.com/en-by/guide/automator/use-quick-action-workflows-aut73234890a/2.10/mac/15.0
- https://support.apple.com/guide/automator/use-scripts-aut4bb6b2b4f/mac
Create a Quick Action in Automator, set it to receive files in Finder, add
Run Shell Script, set Pass input to as arguments, and use the
absolute path to mdo:
for; do
done
Save the workflow as Open as HTML. The result is:
- Finder shows Quick Actions → Open as HTML for selected Markdown files.
- Running the Quick Action opens the rendered page in your browser.
- The rendered page opens from a temp path under
$TMPDIR/mdo/<hash>/<name>.html, and no.htmlfile is left beside the source.
🎨 Default output
The default (non---bare) output is a complete HTML5 document:
<!DOCTYPE html>+<html lang="en">- UTF-8 charset and responsive viewport meta
<title>derived from the first# Headingin the source (falls back to the input filename)- An inlined copy of simple.css inside
<style>, giving you sensible typography and automatic light/dark mode out of the box - A small floating ☀/☾ button (top-right) for manually overriding the theme;
the choice is remembered in
localStorage - Body content wrapped in
<main>
Markdown extensions enabled: tables, footnotes, task lists, strikethrough.
🙏 Credits
This project is a grateful fork of Hafiz Ali Raza's original Markdown-to-HTML CLI. Hafiz remains credited as an author, and this fork keeps that lineage explicit so future improvements can be offered back upstream.
This fork adds:
- Convert-and-exit as the default; watch mode is now opt-in (
--watch) - Optional
--output(defaults to<input>.htmlnext to the source) - Standalone HTML5 output with embedded simple.css
- A
--bareflag that preserves the original fragment-only behavior - An
--openflag that renders to a temp directory and launches the system default browser (with auto-injected<base href>so relative refs resolve) - Light/dark theme toggle button overlaid on the rendered page
- Title auto-derived from the first heading
- Debounced file-change events (no more duplicate renders per save)
- Surfaced watcher errors instead of swallowing them
- Markdown extensions: tables, footnotes, task lists (in addition to strikethrough)
The bundled simple.css is © 2020
Kev Quirk and distributed under the MIT License — see
assets/simple.css.LICENSE.
📄 License
This project is dual-licensed under either of:
- MIT License (LICENSE-MIT)
- Apache License, Version 2.0 (LICENSE-APACHE)
at your option, matching the licensing of the upstream project.