Modern visual styling for Markdown buffers in Emacs (fork of mark-graf)
1
stars
38
commits
Emacs Lisp
primary language
Sep 3, 2026
updated
Modern visual styling for Markdown buffers in Emacs.

markdown-modern renders Markdown inline — headings, emphasis, code, tables, images — using text properties and overlays, in the spirit of org-modern. It reveals the raw markup of the element under the cursor for editing, rather than showing raw syntax or a split-pane preview.
markdown-modern is a fork of mark-graf by Marc Ansset.
markdown-modern's own rendering and editing:
jit-lock, so rendering cost is independent of file size (see Performance)Wired up from Emacs built-ins or external tools:
python-mode, rust-ts-mode, …); requires that mode to be installedtex2svg (MathJax-node)pandocKeybindings mirror markdown-mode's C-c C-s … conventions for familiarity — though markdown-modern is its own major mode (derived from text-mode), not built on markdown-mode.
Rendering is driven by jit-lock, so only the visible region is rendered — open and scroll cost is independent of file size, not proportional to it. On a 25,000-line (~600 KB) Markdown file, rendering a screenful takes about 3 ms with the tree-sitter parser, and reveal-at-point is sub-millisecond. Run make bench to measure on your machine.
pandoc - For PDF/DOCX exportmarkdown-modern is not on MELPA; install it directly from this repository.
package-vc-install (Emacs 29+, recommended)M-x package-vc-install RET https://github.com/rjprins/markdown-modern RET
Or in your init file:
(package-vc-install "https://github.com/rjprins/markdown-modern")
With use-package (Emacs 30+):
(use-package markdown-modern
:vc (:url "https://github.com/rjprins/markdown-modern"))
(add-to-list 'load-path "/path/to/markdown-modern/lisp")
(require 'markdown-modern)
Enable markdown-modern for markdown files by adding to your init file:
(add-to-list 'auto-mode-alist '("\\.md\\'" . markdown-modern-mode))
Or activate manually with M-x markdown-modern-mode in any markdown buffer.
| Key | Command | Description |
|---|---|---|
C-c C-s b | markdown-modern-insert-bold | Insert/toggle bold |
C-c C-s i | markdown-modern-insert-italic | Insert/toggle italic |
C-c C-s c | markdown-modern-insert-code | Insert/toggle code |
C-c C-t 2 | markdown-modern-insert-heading-2 | Insert ## heading |
C-c C-l | markdown-modern-insert-link | Insert link |
C-c C-i | markdown-modern-insert-image | Insert image |
C-c C-e h | markdown-modern-export-html | Export to HTML |
markdown-modern has a single view mode. Markdown is always rendered inline; when the cursor enters the scope of a markup element, that element's raw markup is revealed so you can edit it in place, and re-rendered once the cursor leaves:
# markers appear (at the heading's size).- , 1. , > ) appears.Task checkboxes are the exception: they are treated as interactive widgets, not
markup to reveal. Point on a checkbox keeps the rendered ☐/☑; SPC toggles
it, and Backspace/Delete on it removes the checkbox, leaving a plain list
item.
Plain prose is never disturbed, so moving the cursor through ordinary text does no work. There is no source/rendered toggle to manage.
You can also reveal the element at point on demand with
markdown-modern-toggle-element-at-point (C-c C-v e).
| Key | Command | Description |
|---|---|---|
C-c C-n | markdown-modern-next-heading | Next heading |
C-c C-p | markdown-modern-prev-heading | Previous heading |
C-c C-u | markdown-modern-up-heading | Parent heading |
TAB | Context-sensitive | Cycle visibility / table nav |
| Key | Command | Description |
|---|---|---|
M-RET | markdown-modern-insert-list-item | New list item |
C-c <up/down> | Move item | Reorder list items |
C-c <left/right> | Promote/demote | Change indentation |
C-c C-x C-b | markdown-modern-toggle-checkbox | Toggle task checkbox |
SPC (on a checkbox) | markdown-modern-space-or-toggle-checkbox | Toggle the checkbox under point |
Backspace/Delete (on a checkbox) | markdown-modern-checkbox-delete-{backward,forward} | Remove the checkbox, keep the list item |
| Key | Command | Description |
|---|---|---|
C-c | | markdown-modern-insert-table | Insert new table |
TAB | markdown-modern-table-next-cell | Next cell |
S-TAB | markdown-modern-table-prev-cell | Previous cell |
;; Built-in HTML export (no dependencies)
M-x markdown-modern-export-html
;; Preview in browser
M-x markdown-modern-preview-html
;; Export via Pandoc (requires pandoc)
M-x markdown-modern-export-pdf
M-x markdown-modern-export-docx
All options are under M-x customize-group RET markdown-modern RET. The faces
(markdown-modern-heading-1 … -6, -bold, -italic, -inline-code,
-code-block, -table, …) are under markdown-modern-faces.
| Variable | Default | Description |
|---|---|---|
markdown-modern-heading-scale | (1.8 1.5 1.3 1.1 1.05 1.0) | Height scale factors for heading levels 1–6 |
markdown-modern-heading-use-variable-pitch | t | Headings use a variable-pitch font |
markdown-modern-variable-pitch | t | Enable variable-pitch-mode (proportional prose; code/tables stay fixed-pitch) |
markdown-modern-visual-line | t | Enable visual-line-mode (soft word-wrap) |
markdown-modern-left-margin | 4 | Left margin width, in characters |
| Variable | Default | Description |
|---|---|---|
markdown-modern-manage-text-width | nil | Let markdown-modern constrain the reading width; when nil it leaves fill-column and wrapping to your config |
markdown-modern-text-width | 90 | Reading width in characters (used only when manage-text-width is non-nil) |
markdown-modern-table-max-width | nil | Cap rendered table width; nil = natural width (scroll over-wide tables with C-c C-v t) |
| Variable | Default | Description |
|---|---|---|
markdown-modern-code-block-syntax-highlight | t | Syntax-highlight code blocks via the language's major mode |
markdown-modern-code-block-full-width | t | Extend the code-block background to the window edge |
| Variable | Default | Description |
|---|---|---|
markdown-modern-display-images | t | Display images inline |
markdown-modern-image-max-width | 600 | Maximum inline-image width, in pixels |
markdown-modern-image-max-height | 400 | Maximum inline-image height, in pixels |
markdown-modern-cache-directory | ~/.cache/markdown-modern | Directory for cached rendered diagrams |
| Variable | Default | Description |
|---|---|---|
markdown-modern-math-renderer | text | How to render LaTeX math: text (Unicode) or svg (via tex2svg) |
markdown-modern-math-block-scale | 1.4 | Scale of display-math text relative to normal |
markdown-modern-tex2svg-executable | "tex2svg" | Path to tex2svg (MathJax-node), for SVG math |
| Variable | Default | Description |
|---|---|---|
markdown-modern-use-pandoc | nil | Use Pandoc for export when available |
markdown-modern-pandoc-executable | "pandoc" | Path to the Pandoc executable |
markdown-modern-export-embed-images | nil | Embed images as base64 in HTML export |
markdown-modern-export-html-template | (built-in) | HTML template used by the built-in HTML export |
markdown-modern-export-html-css | (built-in) | CSS used by the built-in HTML export |
(use-package markdown-modern
:vc (:url "https://github.com/rjprins/markdown-modern")
:mode ("\\.md\\'" "\\.markdown\\'")
:custom
;; Constrain the reading width (off by default):
(markdown-modern-manage-text-width t)
(markdown-modern-text-width 100)
:config
(set-face-attribute 'markdown-modern-heading-1 nil :foreground "#2aa198"))
| Key | Command |
|---|---|
C-c C-s b | Bold |
C-c C-s i | Italic |
C-c C-s c | Inline code |
C-c C-s s | Strikethrough |
C-c C-s q | Blockquote |
C-c C-s p | Code block |
C-c C-s k | <kbd> tag |
| Key | Command |
|---|---|
C-c C-t h | Insert heading (prompts for level) |
C-c C-t 1-6 | Insert heading level 1-6 |
C-c C-t ! | Promote heading |
C-c C-t @ | Demote heading |
| Key | Command |
|---|---|
C-c C-v e | Reveal raw markup for the element at point |
C-c C-v t | Toggle horizontal-scroll view (for tables wider than the window) |
| Key | Command |
|---|---|
C-c C-e h | Export to HTML |
C-c C-e p | Export to PDF (Pandoc) |
C-c C-e d | Export to DOCX (Pandoc) |
C-c C-c p | Preview in browser |
GPL-3.0-or-later
markdown-modern is a fork of mark-graf by Marc Ansset; thanks for the original work.
Inspired by:
33 commits
5 commits
Emacs Lisp
97.8%
Makefile
2.2%
Modern visual styling for Markdown buffers in Emacs (fork of mark-graf)
1
stars
38
commits
Emacs Lisp
primary language
Sep 3, 2026
updated
Modern visual styling for Markdown buffers in Emacs.

markdown-modern renders Markdown inline — headings, emphasis, code, tables, images — using text properties and overlays, in the spirit of org-modern. It reveals the raw markup of the element under the cursor for editing, rather than showing raw syntax or a split-pane preview.
markdown-modern is a fork of mark-graf by Marc Ansset.
markdown-modern's own rendering and editing:
jit-lock, so rendering cost is independent of file size (see Performance)Wired up from Emacs built-ins or external tools:
python-mode, rust-ts-mode, …); requires that mode to be installedtex2svg (MathJax-node)pandocKeybindings mirror markdown-mode's C-c C-s … conventions for familiarity — though markdown-modern is its own major mode (derived from text-mode), not built on markdown-mode.
Rendering is driven by jit-lock, so only the visible region is rendered — open and scroll cost is independent of file size, not proportional to it. On a 25,000-line (~600 KB) Markdown file, rendering a screenful takes about 3 ms with the tree-sitter parser, and reveal-at-point is sub-millisecond. Run make bench to measure on your machine.
pandoc - For PDF/DOCX exportmarkdown-modern is not on MELPA; install it directly from this repository.
package-vc-install (Emacs 29+, recommended)M-x package-vc-install RET https://github.com/rjprins/markdown-modern RET
Or in your init file:
(package-vc-install "https://github.com/rjprins/markdown-modern")
With use-package (Emacs 30+):
(use-package markdown-modern
:vc (:url "https://github.com/rjprins/markdown-modern"))
(add-to-list 'load-path "/path/to/markdown-modern/lisp")
(require 'markdown-modern)
Enable markdown-modern for markdown files by adding to your init file:
(add-to-list 'auto-mode-alist '("\\.md\\'" . markdown-modern-mode))
Or activate manually with M-x markdown-modern-mode in any markdown buffer.
| Key | Command | Description |
|---|---|---|
C-c C-s b | markdown-modern-insert-bold | Insert/toggle bold |
C-c C-s i | markdown-modern-insert-italic | Insert/toggle italic |
C-c C-s c | markdown-modern-insert-code | Insert/toggle code |
C-c C-t 2 | markdown-modern-insert-heading-2 | Insert ## heading |
C-c C-l | markdown-modern-insert-link | Insert link |
C-c C-i | markdown-modern-insert-image | Insert image |
C-c C-e h | markdown-modern-export-html | Export to HTML |
markdown-modern has a single view mode. Markdown is always rendered inline; when the cursor enters the scope of a markup element, that element's raw markup is revealed so you can edit it in place, and re-rendered once the cursor leaves:
# markers appear (at the heading's size).- , 1. , > ) appears.Task checkboxes are the exception: they are treated as interactive widgets, not
markup to reveal. Point on a checkbox keeps the rendered ☐/☑; SPC toggles
it, and Backspace/Delete on it removes the checkbox, leaving a plain list
item.
Plain prose is never disturbed, so moving the cursor through ordinary text does no work. There is no source/rendered toggle to manage.
You can also reveal the element at point on demand with
markdown-modern-toggle-element-at-point (C-c C-v e).
| Key | Command | Description |
|---|---|---|
C-c C-n | markdown-modern-next-heading | Next heading |
C-c C-p | markdown-modern-prev-heading | Previous heading |
C-c C-u | markdown-modern-up-heading | Parent heading |
TAB | Context-sensitive | Cycle visibility / table nav |
| Key | Command | Description |
|---|---|---|
M-RET | markdown-modern-insert-list-item | New list item |
C-c <up/down> | Move item | Reorder list items |
C-c <left/right> | Promote/demote | Change indentation |
C-c C-x C-b | markdown-modern-toggle-checkbox | Toggle task checkbox |
SPC (on a checkbox) | markdown-modern-space-or-toggle-checkbox | Toggle the checkbox under point |
Backspace/Delete (on a checkbox) | markdown-modern-checkbox-delete-{backward,forward} | Remove the checkbox, keep the list item |
| Key | Command | Description |
|---|---|---|
C-c | | markdown-modern-insert-table | Insert new table |
TAB | markdown-modern-table-next-cell | Next cell |
S-TAB | markdown-modern-table-prev-cell | Previous cell |
;; Built-in HTML export (no dependencies)
M-x markdown-modern-export-html
;; Preview in browser
M-x markdown-modern-preview-html
;; Export via Pandoc (requires pandoc)
M-x markdown-modern-export-pdf
M-x markdown-modern-export-docx
All options are under M-x customize-group RET markdown-modern RET. The faces
(markdown-modern-heading-1 … -6, -bold, -italic, -inline-code,
-code-block, -table, …) are under markdown-modern-faces.
| Variable | Default | Description |
|---|---|---|
markdown-modern-heading-scale | (1.8 1.5 1.3 1.1 1.05 1.0) | Height scale factors for heading levels 1–6 |
markdown-modern-heading-use-variable-pitch | t | Headings use a variable-pitch font |
markdown-modern-variable-pitch | t | Enable variable-pitch-mode (proportional prose; code/tables stay fixed-pitch) |
markdown-modern-visual-line | t | Enable visual-line-mode (soft word-wrap) |
markdown-modern-left-margin | 4 | Left margin width, in characters |
| Variable | Default | Description |
|---|---|---|
markdown-modern-manage-text-width | nil | Let markdown-modern constrain the reading width; when nil it leaves fill-column and wrapping to your config |
markdown-modern-text-width | 90 | Reading width in characters (used only when manage-text-width is non-nil) |
markdown-modern-table-max-width | nil | Cap rendered table width; nil = natural width (scroll over-wide tables with C-c C-v t) |
| Variable | Default | Description |
|---|---|---|
markdown-modern-code-block-syntax-highlight | t | Syntax-highlight code blocks via the language's major mode |
markdown-modern-code-block-full-width | t | Extend the code-block background to the window edge |
| Variable | Default | Description |
|---|---|---|
markdown-modern-display-images | t | Display images inline |
markdown-modern-image-max-width | 600 | Maximum inline-image width, in pixels |
markdown-modern-image-max-height | 400 | Maximum inline-image height, in pixels |
markdown-modern-cache-directory | ~/.cache/markdown-modern | Directory for cached rendered diagrams |
| Variable | Default | Description |
|---|---|---|
markdown-modern-math-renderer | text | How to render LaTeX math: text (Unicode) or svg (via tex2svg) |
markdown-modern-math-block-scale | 1.4 | Scale of display-math text relative to normal |
markdown-modern-tex2svg-executable | "tex2svg" | Path to tex2svg (MathJax-node), for SVG math |
| Variable | Default | Description |
|---|---|---|
markdown-modern-use-pandoc | nil | Use Pandoc for export when available |
markdown-modern-pandoc-executable | "pandoc" | Path to the Pandoc executable |
markdown-modern-export-embed-images | nil | Embed images as base64 in HTML export |
markdown-modern-export-html-template | (built-in) | HTML template used by the built-in HTML export |
markdown-modern-export-html-css | (built-in) | CSS used by the built-in HTML export |
(use-package markdown-modern
:vc (:url "https://github.com/rjprins/markdown-modern")
:mode ("\\.md\\'" "\\.markdown\\'")
:custom
;; Constrain the reading width (off by default):
(markdown-modern-manage-text-width t)
(markdown-modern-text-width 100)
:config
(set-face-attribute 'markdown-modern-heading-1 nil :foreground "#2aa198"))
| Key | Command |
|---|---|
C-c C-s b | Bold |
C-c C-s i | Italic |
C-c C-s c | Inline code |
C-c C-s s | Strikethrough |
C-c C-s q | Blockquote |
C-c C-s p | Code block |
C-c C-s k | <kbd> tag |
| Key | Command |
|---|---|
C-c C-t h | Insert heading (prompts for level) |
C-c C-t 1-6 | Insert heading level 1-6 |
C-c C-t ! | Promote heading |
C-c C-t @ | Demote heading |
| Key | Command |
|---|---|
C-c C-v e | Reveal raw markup for the element at point |
C-c C-v t | Toggle horizontal-scroll view (for tables wider than the window) |
| Key | Command |
|---|---|
C-c C-e h | Export to HTML |
C-c C-e p | Export to PDF (Pandoc) |
C-c C-e d | Export to DOCX (Pandoc) |
C-c C-c p | Preview in browser |
GPL-3.0-or-later
markdown-modern is a fork of mark-graf by Marc Ansset; thanks for the original work.
Inspired by:
33 commits
5 commits
Emacs Lisp
97.8%
Makefile
2.2%