← Vim研究所

NeovimでMarkdownをプレビューする(render-markdown.nvim / markdown-preview.nvim)

2026-10-19 公開 ・

NeovimでMarkdownを書いていると、見出しや表を整った形で見たくなります。検索するといちばん名前が出てくるのはmarkdown-preview.nvimですが、GitHubのスターが8,015と多い一方で、cloneしたmasterの最新コミットは2023-10-17でした。いまのNeovim 0.12.4で本当に動くのか、動かないならどこで止まるのかを確かめるところから始めました。

結論から書くと、markdown-preview.nvimは入れただけではCannot find module 'tslib'で止まり、npm installを流すと動きます。後継として名前が挙がるrender-markdown.nvimとmarkview.nvimは、ブラウザを開かずバッファの文字を描き替える道具なので、新しさで選ぶと用途を外します。

検証環境:NVIM v0.12.4(macOS)、Node v18.20.2(npm 10.9.0)。プラグインはvim.pack.add()で一時ディレクトリに入れ、XDG_CONFIG_HOMEとXDG_DATA_HOMEもそこへ向けてから、tmuxの中のNeovimで同じsample.mdを開いて画面を取りました。入れた版はrender-markdown.nvim v8.14.0(コミット8e87953)、markview.nvim v29.0.0(ecc5a88)、markdown-preview.nvim(a923f5f)です。スター数と最終pushは2026-10-19にGitHub APIで取得しました。ブラウザ側の確認はChromeで行っています。

目次(9項目)

表示を変える道具は2系統ある

Markdownの見え方を変えるプラグインは、描く場所で2つに分かれます。render-markdown.nvimとmarkview.nvimはNeovimのバッファの中で、記号を隠して枠線やアイコンを重ねます。markdown-preview.nvimはローカルにHTTPサーバーを立て、ブラウザでHTMLとして表示します。

名前描く場所スター最終push必要なもの
render-markdown.nvimバッファの中5,1742026-10-10Nerd Font(アイコン用)
markview.nvimバッファの中3,6652026-10-18Nerd Font推奨
markdown-preview.nvimブラウザ8,0152024-07-23Node.jsとnpm install

render-markdown.nvimのdoc/markdown-ecosystem.mdも同じ分け方で、markview.nvimを衝突する仲間に、markdown-preview.nvimを併用できる側に置いています。

本体だけでどこまで見えるか(conceallevel と treesitter)

プラグインを入れる前に、Neovim本体の力を確かめておきます。0.12.4にはmarkdownとmarkdown_inlineのパーサが同梱されていて、ftplugin/markdown.luaの1行目がvim.treesitter.start()です。nvim-treesitterを入れていなくても、Markdownを開いた時点でtreesitterのハイライトが動きます。nvim --clean sample.mdで開き、vim.treesitter.highlighter.activeを見るとtrueでした。

既定のconceallevelは0なので、記号はそのまま見えています。:set conceallevel=2にして、カーソルを別の行へ動かすと次のようになりました。

本文に **太字** と *斜体* と `code` と [リンク](https://vim.blue/) があります。
本文に 太字 と 斜体 と code と リンク があります。

強調の**と*、インラインコードのバッククォート、リンクの[]と(URL)がまとめて消えます。コードブロックを囲む```luaと```の行は、行ごと空白に見えました。一方で、見出しの#、チェックボックスの[ ]、表の|、引用の>は残ります。本体の仕事は「記号を隠す」までで、表に罫線を引いたり見出しに色帯を付けたりはしません。

:lua vim.treesitter.stop()でtreesitterを止めて従来の正規表現のシンタックスに戻すと、太字と斜体の記号は隠れたままでしたが、バッククォートとリンクのURL、フェンスの行は表示に戻りました。conceallevel=1にした場合は、消えた記号の位置に空白が1つずつ入り、文字の位置がずれにくくなります。

カーソルのある行は、concealcursorが既定で空なので生のテキストに戻ります。見出しの一覧はgOでロケーションリストに出せました。これも同じftplugin/markdown.luaが定義しています。nvim-treesitterのmain移行で書いたとおり、0.12は本体だけで動く範囲が広く、Markdownも例外ではありません。拡張子が.mdでないファイルなら、ファイルタイプを手動で指定すれば同じ表示になります。

render-markdown.nvim をバッファ内で描画する

vim.packで入れる場合、設定は2行です。nvim-treesitterは入れていませんが、本体同梱のパーサで動きました。

vim.pack.add({ 'https://github.com/MeanderingProgrammer/render-markdown.nvim' })
require('render-markdown').setup({})

同じsample.mdを開くと、見出しの#はアイコンに、チェックボックスは四角とチェックのアイコンに、引用の>は縦の棒に置き換わりました。表には罫線が引かれます。

  ┌────────────┬────────────────┐
  │ 名前       │ 説明           │
  ├────────────┼────────────────┤
  │ vim        │ エディタ       │
  │ 日本語の列 │ 全角を含むセル │
  └────────────┴────────────────┘

コードブロックは、フェンスの行が言語名luaだけの帯になり、背景に色が付きます。アイコンはNerd Fontの私用領域の文字なので、Nerd Fontを入れていない端末では豆腐になります。

カーソル行が生のテキストに戻る動き(READMEではanti-concealと呼んでいます)は、行単位で効きます。表の3行目にカーソルを置くと、その行だけが| vim | エディタ |に戻り、上下の罫線と他の行は描かれたままでした。見出しの行に置くと、#が見えてアイコンが消えます。

描画するモードは、ソースの既定値がrender_modes = { 'n', 'c', 't' }です。ノーマルモードでは描き、iで挿入モードに入ると、バッファ全体が生のMarkdownに戻りました。描画中はconceallevelが3に上がり、:RenderMarkdown toggleを打つとバッファ全体が生のテキストに戻って、もう一度打つと描画が戻りました。

markview.nvim との違いと、同時に入れたとき

markview.nvimはvim.pack.add()の1行だけで有効になります。READMEにも遅延読み込みをしないよう書かれていて、setup()を呼ばなくても.mdを開けば描画されました。見た目は近いものの、既定の動きが2点違います。

1つ目はカーソル行です。markviewの既定はhybrid_modes = {}で、カーソルを置いた行も描かれたままでした。生のテキストを見るには挿入モードに入るか、:Markview toggleで切ります。setup({ preview = { hybrid_modes = { 'n' } } })にすると、ノーマルモードでもカーソルの下が生に戻ります。ただし戻る単位は行ではなく要素で、表の1行にカーソルを置くと表全体が生の|区切りに戻りました。行単位に切り替えるlinewise_hybrid_modeという設定もありますが、これは試していません。

2つ目は表の罫線です。既定の'wrap'がオンのままだと、上下の角の罫線は引かれるのに、行の区切りは|のままでした。

  ╭────────────┬────────────────╮
  | 名前       | 説明           |
  | ---------- | -------------- |
  | vim        | エディタ       |
  | 日本語の列 | 全角を含むセル |
  ╰────────────┴────────────────╯

ソースを読むと、表の描画関数がウィンドウのwrapを見ていて、BUG, wrap breaks table rendering.というコメントとともに縦線を|へ切り替えています。--cmd 'set nowrap'を付けて開き直すと、│と├─┼─┤の罫線になりました。

では2つを同時に入れるとどうなるか。render-markdownのsetup({})を呼び、markviewも入れたまま開くと、どちらかが勝つのではなく両方が重ねて描きました。見出しにはアイコンが2つ並び、## 見出し2は先頭の文字が欠けてし2と表示され、チェックボックスもアイコンが2つになって本文の先頭が欠けました。表は罫線が二重です。

  ╭────────────┬────────────────╮┌────────────┬────────────────┐
  │ 名前             │ 説明                     │
  ├────────────┼────────────────┤
  │ vim               │ エディタ             │
  │ 日本語の列 │ 全角を含むセル │
  ╰────────────┴────────────────╯└────────────┴────────────────┘

render-markdown側のドキュメントも、markviewと一緒に使うと機能がぶつかると書いています。試しにmarkviewへsetup({ preview = { enable = false } })を渡すと、render-markdownの描画だけが残りました。とはいえ、使わないほうは入れないのがいちばん確実です。

markdown-preview.nvim はブラウザに出す(2023年10月からコミットなし)

表の最終pushは2024-07-23ですが、cloneしたリポジトリのどのブランチにも2023-10-17より新しいコミットはなく、タグは2022年5月のv0.0.10が最後でした。描く場所はブラウザで、READMEによると数式(KaTeX)やMermaid、PlantUMLの図、ローカルの画像にも対応しています。今回確かめたのは見出しと本文の表示、スクロールの同期、更新の反映です。vim.pack.add({ 'https://github.com/iamcco/markdown-preview.nvim' })で入れ、そのまま:MarkdownPreviewを打つと、ブラウザは開かず、:messagesに次のエラーが残りました(パスは一部伏せています)。

Error: Cannot find module 'tslib'
Require stack:
- <TMP>/mp/data/nvim/site/pack/core/opt/markdown-preview.nvim/app/lib/app/index.js
- <TMP>/mp/data/nvim/site/pack/core/opt/markdown-preview.nvim/app/index.js
...
  code: 'MODULE_NOT_FOUND',
...
Node.js v18.20.2

プラグインのappディレクトリにある依存パッケージが入っていないためです。lazy.nvimやvim-plugではbuildやdoにインストールの手順を書きますが、vim.packにはその欄がありません。代わりにPackChangedイベントでnpm installを流します。

vim.api.nvim_create_autocmd('PackChanged', {
  callback = function(ev)
    local name, kind = ev.data.spec.name, ev.data.kind
    if name == 'markdown-preview.nvim' and (kind == 'install' or kind == 'update') then
      vim.system({ 'npm', 'install' }, { cwd = ev.data.path .. '/app' }):wait()
    end
  end,
})
vim.pack.add({ 'https://github.com/iamcco/markdown-preview.nvim' })

autocmdはvim.pack.add()より前に書きます。初回のインストールでkindがinstallのイベントが届き、Node v18.20.2でnpm installが終了コード0で終わって、app/node_modules/tslibができました。手で入れる場合も、appでnpm installを流すと3秒ほどで60パッケージが入りました。READMEにはビルド済みの実行ファイルを取ってくるmkdp#util#install()も載っていますが、今回はnpmの経路だけを試しています。

依存を入れたあと:MarkdownPreviewを打つと、http://localhost:8728/page/1のようなURLでサーバーが立ち、ページは200を返しました。60節ある文書でNeovimのカーソルを120行目へ動かすと、ブラウザのscrollYは0から3019.5へ変わり、スクロールは同期しています。挿入モードで見出しに文字を足すと、保存しなくてもブラウザの見出しが書き換わりました。0.12.4とNode 18の組み合わせでは、依存さえ入れれば動きます。

気を付けたいのは、npm installのあとプラグインの作業ツリーが汚れることです。git statusを見るとapp/yarn.lockが変更扱いになり、app/package-lock.jsonが増えていました。

日本語の表と見出しで桁がずれないか

全角を含む表は、render-markdownとmarkviewのどちらでも列がそろいました。上の2つの表のとおり、日本語の列(全角5文字で10桁)とvim(3桁)の行で右端の縦線が同じ位置に来ています。どちらも表示幅で計算していて、markviewはwrapのせいで|表示になったときも、空白を足して幅をそろえていました。和文と英字が混ざる見出しも崩れませんでした。

ずれたのは2つを同時に入れたときだけで、これは日本語のせいではなく、2つのプラグインが同じ行に別々の空白を足した結果です。

和文に特化した描画が欲しい場合は、delphinusさんのmd-render.nvim(スター231、2026-10-08に最終push)があります。READMEによると、禁則処理とBudouXによる文節単位の折り返しに対応し、バッファの中ではなくフローティングウィンドウやタブ、ページャーとして表示する作りです。Neovim 0.12以上が必要とされています。今回は実機に入れておらず、READMEで確かめた範囲です。

選び方

やりたいこと選ぶもの
記号を隠すだけでいい本体のconceallevel=2
カーソル行だけ生に戻して書きながら読むrender-markdown.nvim
カーソルを動かしても描画を保ちたいmarkview.nvim
画像や図も含めてブラウザで確認したいmarkdown-preview.nvim

バッファ内の2つはどちらか1つにします。ブラウザ系はこれと競合しないので、普段はバッファ内で描き、人に渡す前だけブラウザで見る組み合わせもできます。

使ってみて

いちばん時間を使ったのはmarkviewの表でした。罫線が|のまま出たのを見て、最初は全角の幅の計算で崩れているのだと思い込み、英字だけの表を作って開き直しました。それでも同じだったので、ようやくソースを読んでwrapの分岐にたどり着いています。markdown-preview.nvimも、:MarkdownPreviewを打った直後の画面には何も出ず、:messagesを開いて初めてNodeのエラーに気づきました。どちらも、見えている結果から原因を推測すると外す壊れ方でした。

まとめ

0.12.4なら本体だけでconceallevel=2が効き、記号は隠せます。表や見出しまで整えるならrender-markdown.nvimかmarkview.nvimを1つだけ入れます。markdown-preview.nvimはappでnpm installを流せば今も動き、vim.packならPackChangedに書いておきます。0.12で変わった本体の機能全体はNeovim 0.12の新機能にまとめています。

関連記事

← Vim研究所 トップへ戻る