A fast, native macOS app for reading Markdown files.
Drop a
.mdon the icon (or set Markdown Preview as your default handler) and get a clean, scrollable preview with a real document outline — no Electron, no browser tab.
Markdown Preview is available in the official Homebrew cask repository:
brew install --cask markdown-preview
Or grab the latest signed and notarized DMG from the Releases page.
Edit Markdown directly with a native formatting toolbar:
Quick Look preview — spacebar a .md in Finder:
Customize the toolbar — drag in Print, Copy, Zoom and the rest from View → Customize Toolbar…
App language: Choose Settings → General → Language to use a bundled translation independently of your system language. The app offers to restart right away so the choice applies to settings, menus, and document controls; choose Later to keep working, and it applies the next time you open the app. System Default removes the app override and follows macOS again. This preference persists across launches and affects only the app; Quick Look previews continue to use their system-selected language. Available languages are discovered from the app’s bundled translations.
Settings uses toolbar tabs. General holds the language, how files open, Always on Top, automatic saving, and the app the Open button uses. Reading contains text size, content width, text alignment, Markdown line breaks, and outline highlighting. Appearance contains the light/dark choice, themes, and their font, spacing, and color customization, with a live sample; resetting a theme leaves global Reading preferences unchanged. Advanced holds the command line tools installer and the app link approvals.
Text alignment: Choose Settings → Reading → Text alignment for Automatic, Left, Center, Right, or Justified prose. Automatic preserves the document’s existing alignment, including right-to-left text. This global preference persists across launches and theme changes, updates open reading views immediately, and applies to Quick Look when reopened. Code, tables, and explicit HTML alignment remain unchanged. Justification leaves the final paragraph line naturally aligned; enable Strict line breaks to join ordinary source wraps into flowing paragraphs.
Single source newlines remain visible by default. Enable Settings → Reading → Markdown → Strict line breaks to let ordinary source lines flow into paragraphs in reading view and Quick Look. Two trailing spaces or a backslash still create an explicit line break; blank lines still separate paragraphs. Reopen an existing Quick Look preview after changing this setting.
WKWebView pipeline backed by swift-markdown, with heading anchors and link handling. Bare http:// and https:// URLs are clickable in the app and Quick Look previews.md-preview: links open without this prompt, even after resetting approvals. Executable URLs and literal file:// links remain blocked; embedded resources cannot launch custom app links.mermaid code blocks render as diagrams in both the app and Quick Look previews, using a bundled renderer so previews work offline without a CDN request.$x_1 + x_2$), display ($$\int_0^1 x^2\,dx$$), and fenced math blocks render with bundled KaTeX in reading and edit modes. In edit mode, click a formula to edit its highlighted LaTeX source; move the caret away to render it again. Inline math source uses inline-code styling; spaces immediately inside either $ delimiter leave the expression literal. Standalone $$ source uses the code-block card with visible delimiters and no header controls; fenced math uses the usual code-block controls with hidden fences. Pressing Enter after a standalone $$ inserts a matching closing delimiter and places the caret between them; single-line $$ equation $$ stays on one source line. Unclosed math stays editable as source. Selecting a rendered formula and copying yields the original LaTeX source (via the official copy-tex extension)..md), Escape keeps the default name, and a new file then opens. They go into the selected folder, or else the open file's folder. If the app has no write access to that folder (for example, when you opened a single file from it), it asks once for access to the project folder and remembers it, so later launches do not ask again.node_modules, vendor, build, DerivedData, Pods, target, and similar), the contents of packages such as .app or .rtfd bundles, and folders more than 12 levels deep. In very large projects it indexes the first 20,000 files and says so.title, author, and lang frontmatter name the book. Typeface and page colours come from the reading app, not the preview’s theme..md previews from Finder spacebar, Spotlight, and Mail attachments without launching the app.mdp, md-preview, and markdown-preview from the app menu, then open files or folders from any shell with commands like mdp README.md or mdp ..md-preview://file/<absolute path> (e.g. md-preview://file/Users/me/project/README.md), the same shape as cursor://file/…. Percent-encode special characters in the path (a space becomes %20)..md opener on first launch..md, .markdown, .mdown, .mdx, .txt
UTI: net.daringfireball.markdown
git clone git@github.com:pluk-inc/markdown-preview.git
cd markdown-preview
open markdown-preview.xcodeproj
Build and run the markdown-preview scheme. Swift Package Manager will resolve Sparkle, Sentry, and swift-markdown on first build.
Release builds submit native crash reports to the pluk-inc/markdown-preview Sentry project. The integration does not collect performance traces, session data, breadcrumbs, network requests, user information, document contents, or file paths. Users can turn reporting off in Markdown Preview > Settings > Privacy; on later launches, the Sentry SDK will not initialize at all.
The committed DSN is a public client key. Release archives upload the app dSYM with sentry-cli; authenticate locally with sentry-cli login and keep that authentication token outside the repository.
Release builds can submit at most one anonymous app became active event per installation per UTC day when Markdown Preview becomes active. The event contains a random installation identifier, app version, macOS major version, processor architecture, locale country or region, and the flag that prevents PostHog from creating a person profile. It is used to count daily and monthly active installations and understand basic platform compatibility. It does not contain document contents, file names or paths, actions, screens, precise location, personal information, or advertising identifiers. Users can disable it from Settings > Privacy.
The PostHog project token is injected from the gitignored Secrets.xcconfig. Copy Secrets.xcconfig.example to Secrets.xcconfig and set POSTHOG_PROJECT_TOKEN before making a release build. If the token is absent, or for a Debug build, analytics remains disabled. Every event disables GeoIP enrichment, and the PostHog project must also be configured to discard IP data in Project Settings > General.
Publish to Web encrypts the document on your Mac (AES-256-GCM) before it is uploaded to md-preview.app, which runs on Cloudflare. The key is only in the part of the link after #, which browsers never send to a server, so the server stores only encrypted bytes and cannot read the document, its title, or its images. Anyone who has the full link can read the document until it expires after 7 days or you stop sharing it. The server does see who connects (IP address), how large each share is, and when it was made and when it expires, and it keeps no account. The web viewer is a page that md-preview.app serves, so you trust that page as you trust any web-based encrypted paste service: a changed viewer page could read the documents it opens.
md-preview/ Main app target (AppKit, WKWebView)
quick-look/ Quick Look extension (.appex)
scripts/ Release & rollback automation
Version.xcconfig Marketing & build version (single source of truth)
appcast.xml Sparkle update feed
Releases are driven by Amore — it handles building, code signing, notarization, DMG creation, S3 upload, and Sparkle appcast publishing in one shot.
To prepare a release PR, start from latest main, update both MARKETING_VERSION and CURRENT_PROJECT_VERSION in Version.xcconfig, and add the matching CHANGELOG.md entry with contributor credits. Submit these together in a ready PR; see the release-process skill for naming and validation.
When ready to publish the prepared release, run the following from a clean working tree. This builds, notarizes, uploads, tags, and publishes the release:
./scripts/release.sh
Use ./scripts/rollback-release.sh to revert the appcast pointer if a release misbehaves.
Pull requests are welcome. For larger changes, please open an issue first to discuss what you'd like to change.
main.Markdown Preview is free and MIT-licensed. If it saved you a browser tab, you can buy us a coffee.
mermaid fenced code blocks$…$, display $$…$$, and ```math blocks| 日期 | 榜单 | 排名 | 周期新增 | 总 STAR |
|---|