Vim / Neovim (vimtex)

For writing LaTeX in Vim or Neovim, the de facto standard is the vimtex plugin by Karl Yngve Lervåg. It brings compilation, navigation between the table of contents and labels, motions and text objects that operate on environments and commands, completion, and two-way jumps with your PDF viewer — all without leaving the editor. This page walks through installing and configuring vimtex, completion, and inverse search via SyncTeX.

What vimtex is

Neither Vim nor Neovim typesets TeX itself; the actual compiling is done by a distribution you install — TeX Live (or MiKTeX / MacTeX). vimtex is the bridge: a filetype and syntax plugin for .tex. When it detects the tex filetype it automatically enables build commands, syntax highlighting, a completion function (omnifunc), a table-of-contents view, jump-to-definition for \ref/\cite, and text objects for environments, delimiters, and commands. Its core works with no external dependencies; completion and snippets are paired with plugins of your choosing.

There are only two prerequisites. First, the TeX commands must be reachable on your PATH — vimtex spawns latexmk and friends as child processes, so if latexmk --version runs in a terminal you are almost certainly fine. Second, Vim must have filetype detection and syntax enabled. Your vimrc/init.vim needs these two lines (most configurations already have them):

vimrc
filetype plugin indent on
syntax enable

Setup

You normally install vimtex with a plugin manager — vim-plug for Vim, lazy.nvim for Neovim, are the common choices. One critical caveat: do not lazy-load vimtex. As the project documents explicitly, loading it after the filetype is detected breaks the inverse search described below. The rule of thumb is lazy = false in lazy.nvim, an explicit load in packer, and no for-based lazy spec in vim-plug.

A minimal Vim (vim-plug) setup is below. Declare the plugin, enable filetype/syntax, and pick a PDF viewer with g:vimtex_view_method, and it starts working. Since maplocalleader (the local leader) is the entry point for vimtex’s commands, it is reassuring to set it explicitly (the default is backslash \).

vimrc
call plug#begin()
Plug 'lervag/vimtex'
call plug#end()

filetype plugin indent on
syntax enable

let maplocalleader = ' '          " use Space as <localleader>
let g:vimtex_view_method = 'zathura'   " 'skim' on macOS, 'sumatrapdf' on Windows
let g:vimtex_compiler_method = 'latexmk'

In Neovim (lazy.nvim) you write the same thing in Lua. Setting the variables inside init makes them take effect before the plugin body loads. Do not forget lazy = false:

lua
return {
  "lervag/vimtex",
  lazy = false,   -- do NOT lazy-load: it breaks inverse search
  init = function()
    vim.g.vimtex_view_method = "zathura"   -- "skim" / "sumatrapdf"
    vim.g.vimtex_compiler_method = "latexmk"
  end,
}

Open a .tex file and the commands keyed off <localleader>l become available. The four worth memorizing first are:

ActionDefault mappingWhat it does
compile\llStart/stop continuous compilation via latexmk; rebuilds automatically on every save
view\lvOpen the PDF and forward-search (source → PDF) to the cursor
clean\lcClean auxiliary files (.aux, .log, …)
stop\lkStop the running compilation (\lK stops all)

In practice: press \ll once and latexmk stays resident, refreshing the PDF on every save thereafter. Use \lv to show the current spot in the PDF, and \le to put errors into the quickfix list and jump to the offending line. As for typing them: if the local leader is still \, you press \ll; if you switched it to Space, you press <Space>ll, and so on.

The default compiler is latexmk, and vimtex passes options such as -verbose -file-line-error -synctex=1 -interaction=nonstopmode for you. In particular, **-synctex=1 is on by default**, so the synchronization data needed for the forward and inverse search below is emitted without any extra setup. To use Tectonic instead, set g:vimtex_compiler_method = "tectonic"; latexrun and arara are also available.

For Japanese, the cleanest approach is to **let .latexmkrc choose the engine.** vimtex’s latexmk backend reads $pdf_mode from .latexmkrc to decide the processing path (1 = pdfLaTeX directly, 3 = build a DVI then convert with dvipdfmx, 4 = LuaLaTeX). Note that engine flags like -pdf/-lualatex should not be added to vimtex’s options; specify them in .latexmkrc or via g:vimtex_compiler_latexmk_engines. Here is an upLaTeX + dvipdfmx setup — the classic combination for Japanese papers in the sciences:

.latexmkrc
$latex = 'uplatex -synctex=1 -interaction=nonstopmode -file-line-error %O %S';
$bibtex = 'upbibtex %O %B';
$biber = 'biber --bblencoding=utf8 -u -U --output_safechars %O %S';
$makeindex = 'upmendex %O -o %D %S';
$dvipdf = 'dvipdfmx %O -o %D %S';
$pdf_mode = 3;
$max_repeat = 5;

The key points: assign uplatex to $latex and dvipdfmx to $dvipdf, then set $pdf_mode = 3 to choose the “build a DVI, then convert to PDF with dvipdfmx” path. Passing -synctex=1 to $latex as well carries the SyncTeX data through to the PDF even via DVI. If you typeset Japanese with LuaLaTeX you do not need a .latexmkrc at all — just have g:vimtex_compiler_latexmk_engines select lualatex, or write $pdf_mode = 4 (assuming luatexja/ltjsclasses).

Completion

vimtex offers context-aware candidates through Vim’s standard completion function (omnifunc). In a tex buffer it **sets omnifunc=vimtex#complete#omnifunc automatically**, so as long as g:vimtex_complete_enabled (default 1) is on, it is ready to use. The main things it completes:

  • \cite{...}** — reads .bib files and \bibitems to complete citation keys.
  • \ref{...} / \eqref{...}** — gathers the \labels in the document to complete references.
  • Commands and environments\commands and \begin{...} environment names according to the packages you load.
  • File names — paths for figures, \input/\include, \includepdf, and the like.
  • Glossary and package namesglossaries entries, and package names based on the available .sty/.cls files.

In plain Vim, press **Ctrl-X Ctrl-O** (the omni-completion trigger) in insert mode and the candidates appear. Type \cite{ and then Ctrl-X Ctrl-O, for instance, and your citation keys are listed. Pressing that every time is tedious, though, so in practice people wire the omnifunc into a completion engine that pops up suggestions automatically as you type.

For a modern Neovim setup, nvim-cmp is the usual choice. Add its omni source and the candidates from vimtex’s omnifunc flow straight in:

lua
local cmp = require("cmp")
cmp.setup({
  sources = cmp.config.sources({
    { name = "omni" },   -- pulls vimtex completion via omnifunc
  }),
})

If you prefer coc.nvim (which works in both Vim and Neovim), install the coc-omni extension and include the tex filetype among its omni sources. On older deoplete setups, pair it with deoplete-vimtex. In every case the division of labor is the same: vimtex produces the candidates, and the completion engine decides when and how to present them.

SyncTeX records the correspondence between source lines and positions in the PDF. It enables forward search — jumping from your editing position to the PDF — and inverse search — going from a place in the PDF back to the source line. vimtex handles forward search for you, so you just press \lv. Inverse search is the tricky half: it needs the PDF viewer to call back into Vim. The key piece is the :VimtexInverseSearch command that vimtex provides.

Here is the mechanism. Using SyncTeX, the viewer works out which source line and file a click corresponds to, then runs a shell command to invoke the editor with that line number and file as arguments. Inside that command you call VimtexInverseSearch <line> <file>, and vimtex#view#inverse_search locates your running Vim/Neovim and opens the spot. vimtex handles multiple instances and server names for you, so you generally need not pass a --servername yourself. The placeholder names differ per viewer: line and file are %{line}/%{input} in zathura, %line/%file in Skim, and %l/%f in SumatraPDF.

Linux — zathura. The most battle-tested pairing with vimtex. With g:vimtex_view_method = "zathura" you get forward search via \lv and, in most cases, inverse search with essentially no configuration: vimtex launches zathura with -x and hands it the editor command that calls VimtexInverseSearch for you (on systems without xdotool, use zathura_simple). In zathura, **Ctrl-click** moves to the matching source line.

If you want to configure it yourself, or to sync the column too, put it directly in zathurarc. zathura’s placeholders are %{line} and %{input} (note: not %l/%f); with a column-aware build you can also use %{column}:

zathurarc
set synctex true
set synctex-editor-command "nvim --headless -c 'VimtexInverseSearch %{line} %{input}'"

macOS — Skim. Set g:vimtex_view_method = "skim". On the Skim side, open Preferences → Sync, set the preset to “Custom,” and register a command and arguments. %line and %file are replaced by Skim with the line number and file:

terminal
Command:   nvim
Arguments: --headless -c "VimtexInverseSearch %line '%file'"

Now **Cmd-Shift-click** in Skim performs inverse search: Skim launches Neovim in the background, and VimtexInverseSearch relays the spot to your existing instance.

Windows — SumatraPDF. Set g:vimtex_view_method = "sumatrapdf". In SumatraPDF, under Settings → Options → “Set inverse search command-line,” register a command like the following (%l and %f are the line number and file):

terminal
cmd /c start /min "" nvim --headless -c "VimtexInverseSearch %l '%f'"

In SumatraPDF, a double-click triggers inverse search. For plain gVim, replace the nvim --headless part with vim -v --not-a-term -T dumb (gVim and Vim on Windows start a server automatically).

One last caveat. Vim running in a terminal (Linux/macOS) does not start a server by default, so Vim users should add the following to vimrc to be reachable by the inverse-search callback (Neovim needs none of this):

vimrc
if empty(v:servername) && exists('*remote_startserver')
  call remote_startserver('VIM')
endif

On every OS and viewer, the prerequisite is that the document was **compiled with -synctex=1** — but as noted, that is already part of vimtex’s default latexmk options, so you rarely have to think about it.