Skip to content

tmux

tmux and vim: seamless splits, fast Escape, copy-paste

vim-tmux-navigator crosses Ctrl-h/j/k/l between vim splits and tmux panes; escape-time 10 fixes Neovim's laggy Escape key. Config for both.

Nadia Okonkwo7 min read

tmux and vim (or Neovim) integrate at three separate points: vim-tmux-navigator makes Ctrl-h/j/k/l move between vim splits and tmux panes as if they were the same grid, a one-line tmux setting fixes Neovim's laggy Escape key, and a couple of clipboard settings make yanking in either one reach the other. None of these happen by default — each needs its own line of config, on top of the core bindings in tmux commands.

Seamless navigation with vim-tmux-navigator

Without this plugin, moving out of a vim split and into the next tmux pane means two different key sequences depending on which side of the boundary you're on — Ctrl-w plus a direction inside vim, prefix plus a direction outside it. vim-tmux-navigator collapses that into one consistent set of keys on both sides.

C-hC-jC-kC-l

It needs configuration in both places — tmux and vim don't know about each other otherwise.

On the tmux side, this block detects whether the current pane is running vim (or Neovim, or fzf) and either forwards the keystroke to it or moves the tmux pane, depending:

~/.tmux.conf
vim_pattern='(\S+/)?g?\.?(view|l?n?vim?x?|fzf)(diff)?(-wrapped)?'
is_vim="ps -o state= -o comm= -t '#{pane_tty}' \
    | grep -iqE '^[^TXZ ]+ +${vim_pattern}$'"
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'

The -n means these bindings work without the prefix at all — Ctrl-h alone, not prefix then Ctrl-h. That's deliberate: the whole point is not having to think about which tool is focused.

On the vim side, install the plugin itself — for Neovim with a plugin manager like lazy.nvim:

{
  "christoomey/vim-tmux-navigator",
  cmd = {
    "TmuxNavigateLeft", "TmuxNavigateDown",
    "TmuxNavigateUp", "TmuxNavigateRight", "TmuxNavigatePrevious",
  },
  keys = {
    { "<c-h>", "<cmd><C-U>TmuxNavigateLeft<cr>" },
    { "<c-j>", "<cmd><C-U>TmuxNavigateDown<cr>" },
    { "<c-k>", "<cmd><C-U>TmuxNavigateUp<cr>" },
    { "<c-l>", "<cmd><C-U>TmuxNavigateRight<cr>" },
    { "<c-\\>", "<cmd><C-U>TmuxNavigatePrevious<cr>" },
  },
},

For classic vim 8+, cloning the plugin into your package path (for example ~/.vim/pack/plugins/start/vim-tmux-navigator) is enough — it defines the same Ctrl-h/j/k/l mappings itself, no extra keybinding config needed on the vim side.

Without the plugin, Ctrl-h/j/k/l just move the cursor

If you only add the tmux-side config and skip the vim plugin, Ctrl-h/j/k/l inside vim will do whatever vim already binds them to (often nothing, or cursor movement in some modes) instead of crossing into tmux. Both halves are required.

Fixing Escape lag in Neovim

tmux waits up to escape-time milliseconds after receiving an Escape byte before deciding it was a standalone Escape key and not the start of a longer key sequence (like an arrow key, which also starts with Escape). The default is 500ms, which is fine for shells but noticeable and annoying inside an editor — pressing Escape to leave insert mode in Neovim has a visible half-second hesitation before it registers.

~/.tmux.conf
set -sg escape-time 10

Why 10, not 0

0 is the most responsive setting and works for most people, but an escape-time of exactly 0 has been reported to break some multi-key sequences that legitimately start with Escape. 10ms is short enough to feel instant to a human and long enough to avoid that edge case — it's the generally recommended middle ground.

If you use tmux-sensible (see tmux plugins), note that it already sets a short escape-time on its own — check what you've got with tmux show-options -g escape-time before assuming this line is missing.

Truecolor: making a colorscheme look right inside tmux

Neovim's truecolor colorschemes render as a muddy, wrong-looking 256-color approximation inside tmux unless tmux is explicitly told the terminal underneath supports RGB:

~/.tmux.conf
set -g default-terminal "tmux-256color"
set -as terminal-features ",*:RGB"

tmux-256color is tmux's own terminfo entry, the current recommendation over the older screen-256color. The terminal-features line is the tmux 3.2+ way of asserting RGB support — the * matches any terminal name, which is the safe default if you're not sure what $TERM your terminal reports; narrow it to your actual terminal (xterm*, alacritty*) if you want to be more specific.

Confirm it worked from inside a session — this should print truecolor, not come back empty:

~ — checking truecolor
[object Object]

Clipboard: yanking between vim and tmux

Two separate boundaries exist here, and it's worth knowing which one you're actually crossing: vim's registers to the system clipboard, and tmux's copy-mode buffer to the system clipboard.

For tmux's side, tmux-yank handles detection of pbcopy, xclip, xsel or wl-copy automatically, so a copy-mode selection reaches the system clipboard without a hand-written binding.

For Neovim specifically, :set clipboard=unnamedplus routes yanks and deletes through the + register to the system clipboard directly, using whichever tool Neovim's clipboard provider finds (xclip, pbcopy, win32yank, depending on platform). Over SSH, where there's no local clipboard tool to shell out to, Neovim 0.10+ can fall back to OSC 52 — an escape sequence that asks the terminal to set the clipboard, which works even across an SSH hop as long as the terminal emulator on your actual desk supports it:

vim.g.clipboard = {
  name = "OSC 52",
  copy = {
    ["+"] = require("vim.ui.clipboard.osc52").copy("+"),
    ["*"] = require("vim.ui.clipboard.osc52").copy("*"),
  },
  paste = {
    ["+"] = require("vim.ui.clipboard.osc52").paste("+"),
    ["*"] = require("vim.ui.clipboard.osc52").paste("*"),
  },
}

Running inside tmux specifically can interfere with Neovim's own automatic OSC 52 detection, so setting set -g set-clipboard on in ~/.tmux.conf alongside the above is worth doing — it tells tmux to pass OSC 52 sequences through rather than swallowing them.

Do you even need tmux if you're already in Neovim?

Honestly, sometimes no. Neovim's built-in :terminal gives you a real shell in a split without leaving the editor, which covers a lot of what people reach for tmux for during a normal editing session — running a quick command, checking git status, tailing a log while you work.

What it doesn't give you is persistence: a :terminal buffer dies with the Neovim process. If you close Neovim, lose your SSH connection, or your laptop sleeps, whatever was running in that terminal split is gone. tmux's actual job — sessions that survive you disconnecting, covered in tmux ssh — is still the reason to keep it around for anything long-running or remote. If your Neovim session and your shell are always local and never need to survive the editor closing, you can reasonably skip tmux and just use :terminal. The moment you're SSHing into a remote box to edit and run things, tmux earns its place back.

None of the above changes if you need to check on that remote Neovim-in-tmux session from somewhere without a terminal handy — a phone, someone else's machine. That's the specific case mtmux (npm i -g mtmux) covers: it serves the same tmux session to a browser tab, unchanged, so a long-running build or test watched from inside Neovim is still visible without SSHing back in just to look.

How do I move between vim and tmux panes with the same keys?

Install vim-tmux-navigator on both sides: the tmux-side config detects whether the focused pane is running vim and either forwards Ctrl-h/j/k/l to it or moves the tmux pane, and the vim-side plugin defines the matching mappings inside vim itself.

Why is Escape slow in Neovim inside tmux?

tmux's default escape-time is 500ms — it waits that long after an Escape byte to see if more of a key sequence is coming. Set set -sg escape-time 10 in ~/.tmux.conf to make it feel instant without breaking multi-key escape sequences.

Why do my Neovim colors look wrong inside tmux?

tmux doesn't assume the terminal supports 24-bit color unless told to. Set default-terminal "tmux-256color" and set -as terminal-features ",*:RGB" in ~/.tmux.conf, then confirm with echo $COLORTERM inside a session — it should print truecolor.

How do I copy from tmux and paste into vim, or the other way around?

tmux-yank handles tmux copy-mode selections reaching the system clipboard. For vim itself, :set clipboard=unnamedplus routes yanks through the system clipboard directly; over SSH, Neovim 0.10+ can use OSC 52 as a fallback when there's no local clipboard tool to call.

Do I need tmux if I already use Neovim's built-in terminal?

Only if you need persistence. :terminal is fine for a quick local command that can die with the editor. tmux is still the right tool for anything long-running or accessed over SSH, since a tmux session survives the connection dropping and :terminal doesn't.

Put this to work — mtmux attaches to the tmux server you already run.

Related reading