MD Viewer is a free, open-source Markdown viewer and .md file reader for
Windows and Linux. Double-click any .md or .markdown file and it opens
straight into a rendered preview — no editor, no browser extension, no
Electron. Built with Tauri (Rust + a native WebView), so the install is a few
megabytes instead of several hundred.
Grab the latest release for your platform →
| Platform | File | Version |
|---|---|---|
| Windows 10 / 11 | MD-Viewer_0.4.0_x64-setup.exe |
0.4.0 |
| Debian / Ubuntu (amd64) | MD-Viewer_0.2.6_amd64.deb |
0.2.6 |
Why two version numbers? Windows and Linux are packaged and versioned independently right now — the
.debwas built from an earlier snapshot and hasn't caught up to the Windows 0.4.0 fixes yet (see CHANGELOG.md). Each platform's badge/version reflects what's actually in that download, not a shared release number. Each platform also has its own release notes on the Releases page.
Windows: run the .exe. It installs for your user account only, so there
is no admin prompt, and it registers .md/.markdown file associations
automatically.
Debian / Ubuntu:
sudo apt install ./MD-Viewer_0.2.6_amd64.debThis pulls in libwebkit2gtk-4.1-0 and libgtk-3-0 if they're missing,
installs the binary to /usr/bin/md-viewer, and registers the app + a
.md/.markdown MIME association in your desktop menu. Launch it from your
app launcher, or run md-viewer from a terminal. To make it the default
handler for .md files, right-click one in your file manager → Open
With → Other Application → pick MD Viewer.
If you find this useful, a ⭐ on the repo genuinely helps other people find it.
Two things a reader notices. No visual changes, no new keyboard shortcuts, no new dependencies, no change to install size or startup time.
Reopen where you left off. Close the app halfway through a document — or shut the machine down — and reopening it puts you back at that paragraph rather than at the top. It holds at any window size, any zoom level and after a reboot, because what gets saved is which paragraph you were on rather than how many pixels down you had scrolled. Every tab restores its own position, background tabs included. Scroll a file back to the top and it opens at the top next time. If the file was edited while the app was closed, it lands on the nearest heading instead.
Find searches what you can see. Ctrl+F was matching the
page's internal structure rather than the rendered text, which showed up as
several unrelated-looking glitches: phrases containing bold, links or code
found nothing, phrases inside syntax-highlighted code blocks found nothing,
phrases split across a wrapped source line found nothing, and copy and text
matched every code block because the Copy button and the language label were
being searched as document content. All of it was one bug, and it's fixed.
Full detail, including why the earlier pixel-offset approach was removed rather than kept, is in CHANGELOG.md; the user-facing version is in RELEASE_NOTES.md.
- Tabbed — open many documents in one window
- Click-to-preview — double-click a
.mdin Explorer, get a rendered view instantly; no raw-text step - Single instance — a second file opens as a new tab in the existing window, whatever folder it came from
- Dark and light themes — high-contrast, editor-style rendering
- Syntax-highlighted code blocks — 68 grammars and over 200 fence labels via highlight.js, each fetched on demand the first time a document uses it (Apex and SOQL included)
- Live outline — jump between headings
- Picks up where you left off — reopen the app and every tab is back at the paragraph you were reading, at any window size or zoom, even after a reboot
- In-document search — Ctrl+F, matching the rendered
text, so phrases spanning bold, links or
codeare found - Live reload — edits made in VS Code or any other editor appear automatically
- GitHub Flavored Markdown — tables, task lists, callouts, fenced code
- Tiny — a few MB installed, low idle CPU (event-driven file watching, not polling)
- Offline and private — no telemetry, no network calls, no account
- Not an editor. Preview only, by design.
- No Mermaid diagram rendering (~2 MB of JavaScript against a project whose whole point is a small install).
- Tabs can't be dragged to reorder —
dragDropEnabled(needed for dropping files onto the window) conflicts with HTML5 drag events on Windows. Ctrl+Tab and Ctrl+1–9 cover most of the need. - The Linux
.deb(0.2.6) predates everything described in CHANGELOG.md for 0.3.0 and 0.4.0 — the layout and performance work, and the find and reading-position work. None of it has been rebuilt for Linux yet. - Not byte-identical to VS Code's preview on obscure GFM edge cases, though visually very close — same parser family, same contrast approach.
- Double-click any
.md/.markdownfile in Explorer. - Drag-and-drop onto the window, or press Ctrl+O.
- To make MD Viewer the default for
.mdfiles: right-click one → Open with → Choose another app → pick MD Viewer → tick "Always use this app".
See USER_GUIDE.md for day-to-day use and the full keyboard shortcut list, tech-behind-md-viewer.md for how it works internally, and CHANGELOG.md for what changed in each release.
Most people should just download a release. Read on only if you want to build it yourself.
sudo apt update
sudo apt install -y libwebkit2gtk-4.1-dev libgtk-3-dev libayatana-appindicator3-dev \
librsvg2-dev build-essential curl wget file libssl-devThen install Rust (rustup.rs) and Node.js (a current LTS
from nodejs.org; Vite 8 requires Node
^20.19.0 || >=22.12.0).
npm install
npm run tauri:buildThe .deb (and an AppImage, if that target is enabled) lands in
src-tauri/target/release/bundle/. Package name and dependency list come from
src-tauri/tauri.conf.json.
For other distros, swap the package names above: Fedora needs
webkit2gtk4.1-devel gtk3-devel, Arch needs webkit2gtk-4.1 gtk3 — the
Tauri Linux prerequisites guide
covers the full matrix.
Three prerequisites, in this order. The first one is the one people skip, and skipping it produces a confusing wall of linker errors rather than a clear "not installed" message — so do it first.
Rust's Windows toolchain does not ship a linker. It calls link.exe from
Microsoft's C++ tools, and without them every crate fails to link.
rustup offers to install these during setup, but it is easy to decline or
miss, and rustup will happily finish without them.
From an administrator terminal:
winget install -e --id Microsoft.VisualStudio.BuildTools --override "--passive --wait --add Microsoft.VisualStudio.Workload.VCTools --includeRecommended"The --override portion is what actually installs the compiler, linker and
Windows SDK. A bare winget install gives you an empty installer shell and
leaves you with the same failure.
Prefer a GUI? Download Build Tools for Visual Studio from visualstudio.microsoft.com/downloads (under "Tools for Visual Studio"), run it, and tick Desktop development with C++, making sure the Windows SDK is checked in the right-hand pane.
Budget roughly 5–7 GB and 15–30 minutes. Reboot afterwards — an installer exit code of 3010 means exactly that.
Install via rustup.rs — download rustup-init.exe and
accept the defaults.
Install a current LTS from nodejs.org. Vite 8 requires
Node ^20.19.0 || >=22.12.0.
WebView2 is already present on Windows 11 and on updated Windows 10. If it is missing, Windows Update will fetch it, or you can install the Evergreen Bootstrapper from Microsoft.
doctor.cmd
This checks everything at once and saves a copy to doctor-report.txt. Two
lines decide whether a build can succeed:
=== Visual Studio installs ===must list a path. Blank means step 1 did not happen, or installed without the C++ workload.=== link.exe on PATH ===must not lead with a path underGit,msysorcygwin. Those ship a GNU coreutils tool that is also namedlink.exebut is not a linker; if it wins on PATH, rustc calls it and every crate fails.dev.cmdandbuild.cmdguard against this by putting MSVC first, so a Git hit here is survivable — an empty Visual Studio section is not.
Both are explained in TROUBLESHOOTING.md.
Double-click build.cmd, or from a Command Prompt in this folder:
build.cmd
That sets up the MSVC environment before building, which avoids a common
link.exe collision — see TROUBLESHOOTING.md. If your
PATH is already clean you can use npm directly:
npm install
npm run tauri:buildFirst build takes a few minutes (Rust compiles the whole dependency tree). Subsequent builds are much faster.
When it finishes, your installer is at:
src-tauri\target\release\bundle\nsis\MD Viewer_0.4.0_x64-setup.exe
Windows:
dev.cmd
or, equivalently, npm install && npm run tauri:dev from a shell where the
MSVC linker is on PATH.
Linux: npm install && npm run tauri:dev (no wrapper script needed — there's
no linker-shadowing issue on Linux, that's a Windows/Git-Bash-specific trap,
see TROUBLESHOOTING.md).
Note: single-instance file-forwarding only works with the installed build, since it relies on the OS launching a second process of the same registered executable.
npm install
npm run devThen open http://localhost:1420. This runs the UI with no desktop backend: rendering, outline, find, theme and zoom all work, and you can drop a file onto the page or use Ctrl+O. Anything that needs a real filesystem path — reload on external edit, relative links between documents, Show in Explorer — is unavailable and says so. Useful for iterating on the frontend without waiting on a Rust build.
npm test :: rendering pipeline, no build needed
npm run test:all :: the above, plus a boot test against the built bundle75 checks covering the rendering pipeline: HTML sanitizing, heading slugs,
link classification, code fences, callouts, path resolution, find, and the
reading-position anchors. Runs in Node against jsdom, so it needs no Windows
toolchain — useful as a fast check before the much slower tauri:build.
npm run test:boot (run after npm run build) loads the bundled frontend with
no Tauri globals present and asserts it still boots, styles itself and opens a
dropped file. It guards a regression where one unguarded Tauri call at module
scope left the window blank and inert.
On Windows, see TROUBLESHOOTING.md, or run doctor.cmd
and read the first line of output. On Linux, the most common failure is a
missing system dependency (webkit2gtk/gtk3) reported by the linker at build
time — install the packages listed above and re-run npm run tauri:build.
md-viewer/
├── src/ # Frontend — the actual UI
│ ├── index.html # App shell: tab bar, outline, find bar, status bar
│ ├── style.css # Design tokens + both themes + markdown styling
│ ├── main.js # Tabs, shortcuts, link routing, live reload
│ └── lib/
│ ├── markdown.js # markdown-it setup, callouts, slugs, DOM pass
│ ├── sanitize.js # Strips executable HTML from rendered documents
│ ├── highlight.js # highlight.js with a curated language set
│ ├── find.js # Ctrl+F
│ ├── anchor.js # Reading-position search and validation
│ └── paths.js # Windows path helpers
├── src-tauri/ # Rust backend
│ ├── src/main.rs # Single-instance, file reading, launch handshake
│ ├── tauri.conf.json # Window, CSP, asset protocol, file associations
│ ├── capabilities/ # What the frontend is allowed to call
│ └── icons/ # App icons
├── tests/
│ ├── render.test.mjs # The 75-check suite
│ ├── boot.test.mjs # Boots the built bundle with no Tauri globals
│ └── bench.mjs # Render and find timings
├── package.json
└── vite.config.js
- Colors: two token blocks at the top of
src/style.css,html[data-theme="dark"]andhtml[data-theme="light"]. Nothing outside those blocks hardcodes a color, so retheming means editing one place. - Reading width: the
--measuretoken near the top ofsrc/style.css, set per mode underhtml[data-width]—full(the default),wide(118ch) andreading(78ch), cycled with Ctrl+Shift+W. The caps are inchrather than pixels so the line length stays constant as you zoom. - Syntax highlighting languages:
src/lib/highlight.jsholds aLOADERSmap of 68 grammars and anALIASESmap that routes another 134 fence labels onto them. Add a grammar as a literalimport()inLOADERS— Vite can only split a dynamic import it can see statically, so a computed path bundles everything or nothing. - Icons: replace
src-tauri/icons/, or runnpx tauri icon path/to/logo.pngto regenerate the set from one image. - File-type icon didn't update after install: Windows caches these
aggressively; a reboot or
ie4uinit.exe -showfixes it.
Issues and pull requests are welcome. On Windows, if you hit a bug, please run
doctor.cmd and paste doctor-report.txt into the issue — it answers most of
the questions I'd otherwise have to ask. On Linux, please include your distro,
md-viewer --version if available, and the output of running md-viewer from
a terminal (it'll surface WebKitGTK errors that don't show up otherwise).
MD Viewer is free and open source. If it saved you some time:
- ⭐ Star this repository — it's the single most useful thing you can do, and it's what makes the project findable for the next person
- 🐛 Open an issue for bugs or feature ideas
- 🔗 Share it with someone who keeps opening
.mdfiles in Notepad
MIT — free for personal and commercial use.
Keywords: markdown viewer, md viewer, markdown reader, .md file opener, open md files on Windows, open md files on Linux, markdown preview app, offline markdown viewer, lightweight markdown viewer, Tauri markdown app, Windows markdown viewer, Linux markdown viewer, Debian markdown viewer, free markdown viewer.