Skip to main content

Tmux Integration

Neovim inside tmux is one of the most powerful terminal development setups. This lesson covers seamless navigation between tmux panes and Neovim splits, clipboard sharing, and session persistence.

Core Idea

The goal is to make Ctrl+h/j/k/l move your focus transparently between Neovim windows AND tmux panes — without thinking about whether you're in Neovim or tmux.

vim-tmux-navigator

This plugin makes Ctrl+h/j/k/l work across both Neovim splits and tmux panes seamlessly:

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

Corresponding tmux config:

~/.tmux.conf
# Smart pane switching with awareness of Neovim splits
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 -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

Clipboard Sharing

By default, Neovim's yank and the tmux clipboard are separate. Bridge them:

lua/config/options.lua
-- Use system clipboard (shared with tmux via terminal clipboard)
vim.opt.clipboard = "unnamedplus"

On Linux servers, install xclip or use OSC52 for clipboard over SSH:

sudo apt install -y xclip

For SSH clipboard sharing, add to your Neovim config:

-- OSC52 clipboard (works over SSH without X11)
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("*"),
},
}
note

OSC52 requires your terminal emulator to support it (iTerm2, Kitty, WezTerm, etc.).

~/bin/dev.sh
#!/bin/bash
SESSION="dev"
PROJECT="/srv/myproject"

tmux has-session -t "$SESSION" 2>/dev/null && tmux attach -t "$SESSION" && exit

tmux new -s "$SESSION" -n "editor" -d
tmux send-keys -t "$SESSION:editor" "cd $PROJECT && nvim ." Enter

tmux new-window -t "$SESSION" -n "shell"
tmux send-keys -t "$SESSION:shell" "cd $PROJECT" Enter

tmux new-window -t "$SESSION" -n "logs"
tmux send-keys -t "$SESSION:logs" "tail -f /var/log/myapp.log" Enter

tmux select-window -t "$SESSION:editor"
tmux attach -t "$SESSION"

Color and True Color in Tmux + Neovim

~/.tmux.conf
# Enable true color
set -g default-terminal "tmux-256color"
set -ga terminal-overrides ",*256col*:Tc"
~/.config/nvim/lua/config/options.lua
vim.opt.termguicolors = true
warning

Without Tc override in tmux, colors will look washed out in Neovim. This is one of the most common tmux + Neovim setup issues.

What's Next