C.W.K.
Stream
Lesson 01 of 03 · published

Seamless Navigation with vim-tmux-navigator

~12 min · vim, tmux, integration, plugins

Level 0Trapped
0 XP0/35 lessons0/12 achievements
0/100 XP to next level100 XP to go0% complete

The friction nobody warns you about

You've configured Vim to navigate splits with Ctrl-h/j/k/l (Track 4). You've configured tmux to navigate panes with prefix + h/j/k/l (Track 6). The moment you have a Vim split next to a shell pane in tmux, your fingers don't know which prefix applies. Two systems, two muscle memories, friction every time you cross the boundary.

The fix: vim-tmux-navigator

christoomey/vim-tmux-navigator is a tiny plugin that makes Ctrl-h/j/k/l work seamlessly in both layers. Press Ctrl-l in Vim — if there's a split to the right, you go to it; if not, tmux moves to the pane on the right. The boundary disappears.

How it works

Two halves: a Vim plugin and a tmux config snippet. The tmux side checks whether the focused pane is running a Vim process; if yes, it forwards Ctrl-h/j/k/l to Vim, which the Vim plugin handles. If no, tmux runs select-pane directly. It's clever, robust, and the snippet is short enough to read in one minute.

Install both halves

Add the Vim plugin via lazy.nvim. Add the tmux snippet to ~/.tmux.conf and reload. That's the whole setup; once it's wired, navigation feels like magic.

Integration removes layers, it doesn't add them. Vim+tmux without vim-tmux-navigator is two tools you're constantly translating between. With it, they collapse into one workspace. The best integration plugins reduce mental load instead of giving you more buttons to push.

Caveats

The detection regex assumes your Vim process name contains vim, nvim, view, or similar. Some sandboxed terminals or unusual processes (fzf, lazygit) can confuse it. The plugin's README covers the edge-case overrides. For 99% of users, the snippet below Just Works.

Code

Add to your lazy.nvim plugin spec·lua
{
  "christoomey/vim-tmux-navigator",
  cmd = {
    "TmuxNavigateLeft", "TmuxNavigateDown",
    "TmuxNavigateUp",   "TmuxNavigateRight",
    "TmuxNavigatePrevious",
  },
  keys = {
    { "<C-h>", "<cmd>TmuxNavigateLeft<CR>",     desc = "Go to left split/pane" },
    { "<C-j>", "<cmd>TmuxNavigateDown<CR>",     desc = "Go to lower split/pane" },
    { "<C-k>", "<cmd>TmuxNavigateUp<CR>",       desc = "Go to upper split/pane" },
    { "<C-l>", "<cmd>TmuxNavigateRight<CR>",    desc = "Go to right split/pane" },
    { "<C-\\>", "<cmd>TmuxNavigatePrevious<CR>", desc = "Go to previous split/pane" },
  },
}
Add to ~/.tmux.conf — the partner snippet·tmux
# vim-tmux-navigator — make Ctrl-h/j/k/l cross between Vim and tmux
is_vim="ps -o state= -o comm= -t '#{pane_tty}' \
    | grep -iqE '^[^TXZ ]+ +(\\S+\\/)?g?(view|l?n?vim?x?|fzf)(diff)?$'"
bind-key -n 'C-h' if-shell "$is_vim" 'send-keys C-h' 'select-pane -L'
bind-key -n 'C-j' if-shell "$is_vim" 'send-keys C-j' 'select-pane -D'
bind-key -n 'C-k' if-shell "$is_vim" 'send-keys C-k' 'select-pane -U'
bind-key -n 'C-l' if-shell "$is_vim" 'send-keys C-l' 'select-pane -R'
bind-key -n 'C-\\' if-shell "$is_vim" 'send-keys C-\\' 'select-pane -l'

# In Vim's copy-mode-vi, also forward the keys
bind-key -T copy-mode-vi 'C-h' select-pane -L
bind-key -T copy-mode-vi 'C-j' select-pane -D
bind-key -T copy-mode-vi 'C-k' select-pane -U
bind-key -T copy-mode-vi 'C-l' select-pane -R
Test it — the validation routine·text
1. Inside tmux, open a vertical split (prefix + |).
2. In one pane, run nvim.
3. Inside Vim, :vsp another file (so you have a Vim-internal split).
4. Press Ctrl-h / Ctrl-l repeatedly.
   You should walk: left tmux pane ←→ left Vim split ←→
   right Vim split ←→ right tmux pane.
5. Pure flow.  No prefix needed, no thinking about layers.

External links

Exercise

Install vim-tmux-navigator (Vim plugin) and add the matching tmux snippet. Run the validation routine above. The first time it works, take a moment to notice — that's the goal of this whole track. If Ctrl-h/j/k/l ever stops crossing layers later, check that the regex still recognizes your Vim process (rare, but it happens with custom build paths).

Progress

Progress is local-only — sign in to sync across devices.
Spotted a bug or have feedback on this page?Report an Issue

Comments 0

🔔 Reply notifications (sign in)
Sign inPlease sign in to comment.

No comments yet — be the first.