Neovim Markdown: Preview, Rendering, and Plugins
September 11, 2026 · 9 min read
Neovim Markdown: Preview, Rendering, and Plugins
A good Neovim Markdown setup has three layers. Fix the built-in tree-sitter highlighting and options first, then add an in-buffer renderer such as render-markdown.nvim and a browser preview such as markdown-preview.nvim. This guide gives you a copy-paste lazy.nvim config for each, plus a section for classic Vim users.
What Neovim Already Does for Markdown
Before installing anything, check what your Neovim version ships with. Neovim bundles a tree-sitter parser for Markdown (the block grammar and the markdown_inline grammar from tree-sitter-markdown), alongside C, Lua, Vimscript, Vimdoc, Query, and Diff. You don't need nvim-treesitter to get Markdown highlighting, though most people install it anyway for other languages.
The behaviour depends on the release you're on:
- Neovim 0.11 added a Markdown ftplugin with
gOfor a heading outline and]]/[[to jump between sections. - Neovim 0.12 (0.12.5 is the current release as of September 2026) calls
vim.treesitter.start()in that ftplugin, so tree-sitter highlighting is on without any config. - Neovim 0.10 and earlier use the old regex syntax file unless you start tree-sitter yourself.
To get the same result on an older version, or to add sensible writing options everywhere, create after/ftplugin/markdown.lua:
vim.treesitter.start() -- no-op on 0.12, enables highlighting on 0.10/0.11
vim.opt_local.wrap = true
vim.opt_local.linebreak = true -- wrap at word boundaries, not mid-word
vim.opt_local.spell = true
vim.opt_local.conceallevel = 2
vim.opt_local.textwidth = 0 -- don't hard-wrap on gq unless you want to
conceallevel matters more than it looks. Per the Neovim options docs, 0 shows text as written, 1 replaces concealed spans with one character, 2 hides them unless a replacement character is defined, and 3 hides them entirely. Rendering plugins rely on concealment to hide the ** around bold text, and they expect 2 or 3. Concealed text reappears on the cursor line, which is the behaviour that makes editing possible.
How Do You Preview Markdown in Neovim?
The phrase "nvim markdown preview" covers three different tools, and picking the wrong one is the most common setup mistake.
| Approach | Plugin | What you see | Best for |
|---|---|---|---|
| In-buffer rendering | render-markdown.nvim, markview.nvim | Headings, bullets, tables, and code blocks styled inside the buffer | Notes, READMEs, staying in the terminal |
| Browser preview | markdown-preview.nvim, peek.nvim | A browser tab that scrolls with your cursor | Checking how GitHub or a site renders it |
| Terminal viewer | glow | A read-only render in a separate terminal pane | Reading, not editing |
In-buffer rendering is the one most people want day to day. Browser preview is the one you want before pushing a README. We prefer running both and toggling the browser only when it's needed. The terminal viewer route is a separate topic; glow gets one line here because it doesn't edit anything.
Render Markdown in the Buffer With render-markdown.nvim
render-markdown.nvim is the plugin behind most "render-markdown nvim" screenshots. It draws headings with icons and background colors, replaces list markers with bullets, aligns table borders, and shades code blocks, all without leaving Neovim. Its README lists the requirements: Neovim 0.9.0 minimum (0.10.0 recommended), a Nerd Font, and the markdown and markdown_inline tree-sitter parsers. The latex, html, and yaml parsers are optional extras.
The lazy.nvim spec from the render-markdown.nvim README is short:
{
"MeanderingProgrammer/render-markdown.nvim",
dependencies = { "nvim-treesitter/nvim-treesitter", "nvim-tree/nvim-web-devicons" },
ft = { "markdown" },
opts = {},
}
Swap nvim-web-devicons for nvim-mini/mini.icons if you use the mini suite. If you manage parsers through nvim-treesitter, add markdown and markdown_inline to ensure_installed.
The commands are :RenderMarkdown enable, :RenderMarkdown disable, and :RenderMarkdown toggle, plus buf_ variants that apply to the current buffer only. Two design choices make it comfortable to edit inside: the current line shows raw text (anti-conceal), and it switches to raw view in insert mode by default. For large files it renders only the visible range, and you can disable it entirely above a size you choose.
One honest limitation: it's a stylised view, not a faithful render. Tables are aligned with box characters, but a Nerd Font glyph that your terminal lacks shows as a blank or a square. If you see boxes where icons should be, the font is the problem, not the plugin.
Preview Neovim Markdown in the Browser
For a real neovim markdown preview in HTML, markdown-preview.nvim opens a browser tab. It updates as you type and scrolls in sync with your cursor. According to its README, it runs on Vim 8.1 or later and on Neovim, and it renders KaTeX math, PlantUML, and Mermaid diagrams out of the box. That last point is why it's still the standard markdown preview nvim users reach for even with in-buffer rendering installed.
Searches for nvim markdown-preview, neovim markdown-preview, and vim-markdown-preview all land on this same plugin, iamcco/markdown-preview.nvim. It ships a prebuilt server, so the install step is a function call rather than a Node build:
{
"iamcco/markdown-preview.nvim",
cmd = { "MarkdownPreviewToggle", "MarkdownPreview", "MarkdownPreviewStop" },
ft = { "markdown" },
build = function() vim.fn["mkdp#util#install"]() end,
keys = {
{ "<leader>mp", "<cmd>MarkdownPreviewToggle<cr>", ft = "markdown", desc = "Markdown preview" },
},
}
If you have Node and yarn installed, build = "cd app && yarn install" works instead. Run :MarkdownPreview to open the tab, :MarkdownPreviewStop to close it, or :MarkdownPreviewToggle to flip between them. The README also exposes <Plug>MarkdownPreview if you'd rather map it in Vimscript.
The preview uses its own stylesheet, which is close to GitHub's but not identical. When the exact GitHub output matters, paste the file into the editor for a side-by-side view that matches GFM. You can also run it through the Markdown to HTML converter to inspect the markup. If you're weighing editors, VS Code's built-in preview is the usual comparison point.
Try the sample document in a live preview
Here's the kind of file that shows every rendering difference at once: headings, a task list, a table, and a code block. Paste it into Neovim with render-markdown.nvim enabled and compare with the render below.
What the LazyVim Markdown Extra Enables
If you run LazyVim, the lang.markdown extra wires most of this up. Per the LazyVim docs, it installs markdownlint-cli2 and markdown-toc through mason and enables the marksman language server. It configures markdown-preview.nvim with <leader>cp mapped to :MarkdownPreviewToggle. It also adds render-markdown.nvim with a quieter default: no heading icons, no signs, and checkboxes left as plain text.
It also sets prettier, markdownlint-cli2, and markdown-toc as formatters, which means a save can reflow your file, fix lint findings, and regenerate a table of contents in one pass. If a formatter surprises you, that's the place to look.
To rebind the preview key, override the plugin spec in your own config:
{
"iamcco/markdown-preview.nvim",
keys = {
{ "<leader>cp", false },
{ "<leader>mp", "<cmd>MarkdownPreviewToggle<cr>", ft = "markdown", desc = "Markdown preview" },
},
}
Marksman deserves a mention on its own. It's a self-contained LSP binary that gives you completion for links and headings, go-to-definition on [text](file.md#heading) links, rename across files, and diagnostics for broken references. It also understands wiki-style [[links]] if your notes use them.
Does vim-markdown Work in Vim and Neovim?
If you searched "vim markdown" and you're on Vim rather than Neovim, two plugins still cover you. preservim/vim-markdown is a Vimscript plugin, so it runs in Vim and Neovim alike. vim-markdown previews nothing on its own; it improves editing. Its README documents folding by heading (on by default), concealment of bold, italic, code, and link syntax, and a :Toc command that opens a table of contents in a split. Turn folding off with let g:vim_markdown_folding_disabled = 1 if you'd rather see the whole file.
For a vim markdown preview, markdown-preview.nvim is the answer again because its Vim 8.1 support is explicit. It's the only browser markdown preview Vim 8 users get without a separate build step, and the classic Markdown Vim workflow pairs it with vim-markdown for folding. Install it with vim-plug and let the plugin download its prebuilt server:
Plug 'iamcco/markdown-preview.nvim', { 'do': { -> mkdp#util#install() }, 'for': ['markdown', 'vim-plug'] }
nmap <leader>mp <Plug>MarkdownPreviewToggle
What you lose in classic Vim is tree-sitter. Highlighting comes from the regex syntax file, and there's no in-buffer rendering equivalent to render-markdown.nvim. If that's a deal-breaker, the best Markdown editors roundup lists the alternatives.
Common Neovim Markdown Setup Mistakes
Installing a renderer without the inline parser. render-markdown.nvim needs both markdown and markdown_inline. With only the block parser, headings render but bold, links, and inline code stay raw. Run :checkhealth vim.treesitter or :TSInstall markdown_inline.
Leaving conceallevel at 0. The plugins conceal syntax characters, so with the default of 0 you see both the icon and the # it was supposed to replace. Set it to 2 in the Markdown ftplugin, not globally, so other filetypes are unaffected.
Fighting table alignment by hand. Tree-sitter highlights tables, but neither renderer edits them. A formatter such as prettier (which LazyVim enables) or a dedicated table-mode plugin realigns columns. The Markdown table guide explains the alignment syntax those tools produce, and the cheat sheet is a handy reference while you learn the keymaps.
Neovim Markdown FAQ
A Neovim Markdown workflow doesn't need a giant dotfile. Fix conceallevel and wrapping in one ftplugin, add render-markdown.nvim for the buffer and markdown-preview.nvim for the browser, and let marksman handle links. When you need to confirm exactly how GitHub will render a README, drop the file into the editor and compare side by side.