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.
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.
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:
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.
set -sg escape-time 10Why 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:
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:
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.