‹ Guides

The tmux config we use

The ~/.tmux.conf behind the screenshots on this site — tuned for a small screen, a coding agent, and a phone and a laptop attached at the same time.

Written

Why bother

tmux's defaults were written for a desktop: one client attached, a keyboard with a full modifier row, and a program whose output is text. On a phone every one of those is wrong. The screen is small, a second client is often attached from a laptop, and the thing running inside is increasingly a coding agent that speaks in colour, images and Shift+Enter — none of which survives a stock tmux intact.

This is the file we actually run, not a starter template: every tmux screenshot on this site was taken through it. Copy it whole, or take the parts you want — every line carries a comment saying what it buys, and the sections below explain the choices that are not obvious.

~/.tmux.conf
##### Terminal capabilities #####

# Terminal type + 24-bit true color -- without RGB, gradients show banding
set  -g default-terminal "tmux-256color"
set -as terminal-features ",*:RGB"

# Inline images (sixel, kitty, imgcat) -- otherwise tmux drops them
set  -g allow-passthrough on
set -as terminal-features ",*:sixel"

# Copying inside tmux lands on the phone clipboard (OSC 52)
set  -g set-clipboard on

# ...and over mosh too. tmux sends that OSC 52 with an empty target -- 52;; --
# and mosh forwards only 52;c;, so without this line a copy made inside tmux
# reaches tmux's own paste buffer and never leaves the server. Keep the %p1:
# a format that skips to %p2 loads without complaint and then sends nothing
set -ag terminal-overrides ',*:Ms=\E]52;%p1%sc;%p2%s\a'

# Let Ctrl+Shift+..., Shift+Enter etc. pass through tmux, in CSI u form
# (Claude Code relies on this for Shift+Enter newlines)
set  -s extended-keys on
set -as terminal-features ",*:extkeys"
set  -g extended-keys-format csi-u

# Don't sit on Esc -- the default 500ms wait ruins Alt combos
set -sg escape-time 10

# Focus events and mouse (hold Shift in the terminal for local selection)
set  -g focus-events on
set  -g mouse on

# Forward window titles so the terminal app can name its tabs
set  -g set-titles on
set  -g set-titles-string "tmux · #{pane_title}"

# Scrollback depth -- agents produce a lot of output, give them room
set  -g history-limit 100000

##### Mobile / multiple clients attached at once #####

# Size windows to the most recently active client -- with phone and desktop
# both attached, whoever is typing gets their screen size; a small client
# never locks the layout down
set  -g window-size latest
setw -g aggressive-resize on

# Number windows and panes from 1, refill gaps on close -- 1 is easier to
# reach than 0 on a phone keyboard
set  -g base-index 1
setw -g pane-base-index 1
set  -g renumber-windows on

# Killing the current session jumps to the next one instead of detaching --
# reconnecting from a phone is a pain
set  -g detach-on-destroy off

# Keep messages up long enough to read on a small screen
set  -g display-time 2000
set  -g display-panes-time 2000

##### Code agent alerts #####

# Claude Code and friends ring the bell when done -- no screen flash, just
# mark that window red in the status line
set  -g bell-action any
set  -g visual-bell off
setw -g monitor-bell on

# New output in a background window -> yellow flag in the status line, so
# you can tell at a glance which agent is running and which has stopped
setw -g monitor-activity on
set  -g visual-activity off

##### Colors -- ANSI 16 only + transparent bg, follows the terminal's #####
##### light/dark theme automatically #####

# The trick: never hard-code RGB in the status line. bg=default lets the
# terminal background show through, and colour0-15 come from the terminal's
# own palette -- when it switches light/dark, tmux re-themes with it.

# Status line on top, away from Claude Code's input box at the bottom
set  -g status-position top
set  -g status-interval 5
set  -g status-style "bg=default,fg=default"

# Left: session name; right: ^B hint only while the prefix is held
set  -g status-left "#[fg=colour5,bold] #S #[default]"
set  -g status-left-length 20
set  -g status-right "#{?client_prefix,#[fg=colour3 bold]^B ,}"
set  -g status-right-length 20

# Window list: current window on a blue block, others gray; bell(!) red,
# activity(#) yellow
# reverse makes the text take the terminal background color -- dark text on
# light themes, light text on dark themes, adapts by itself
setw -g window-status-format " #I #W#{?window_flags,#{window_flags}, } "
setw -g window-status-style "fg=colour8"
setw -g window-status-current-format " #I #W "
setw -g window-status-current-style "fg=colour4,bold,reverse"
setw -g window-status-bell-style "fg=colour1,bold"
setw -g window-status-activity-style "fg=colour3"

# Pane borders, messages, copy-mode selection
set  -g pane-border-style "fg=colour8"
set  -g pane-active-border-style "fg=colour4"
set  -g message-style "fg=colour4,bold,bg=default"
set  -g mode-style "bg=colour4,fg=colour0"
set  -g display-panes-colour colour8
set  -g display-panes-active-colour colour4
setw -g clock-mode-colour colour4

##### Key bindings #####

setw -g mode-keys vi

# Splits and new windows inherit the current directory -- no need to cd
# back to wherever the agent is working
bind | split-window -h -c "#{pane_current_path}"
bind - split-window -v -c "#{pane_current_path}"
bind c new-window   -c "#{pane_current_path}"

# Alt+number jumps straight to a window, no prefix -- one less chord on
# a phone keyboard
bind -n M-1 select-window -t 1
bind -n M-2 select-window -t 2
bind -n M-3 select-window -t 3
bind -n M-4 select-window -t 4
bind -n M-5 select-window -t 5

# prefix + hjkl to move between panes
bind h select-pane -L
bind j select-pane -D
bind k select-pane -U
bind l select-pane -R

# Copy mode: v to select, y to copy (via OSC 52 to the phone/local clipboard)
bind -T copy-mode-vi v send -X begin-selection
bind -T copy-mode-vi y send -X copy-selection-and-cancel

# prefix + t: popup scratch terminal for one-off commands without touching
# the agent's layout; closes on exit
bind t display-popup -E -w 80% -h 75% -d "#{pane_current_path}"

# prefix + r: reload config
bind r source-file ~/.tmux.conf \; display "reloaded ~/.tmux.conf"
What tmux hides from your terminal

A multiplexer sits between the program and Term, and a stock one is conservative about what it passes on. allow-passthrough on is what lets inline images reach the screen — sixel, kitty graphics and imgcat all arrive as escape sequences tmux would otherwise swallow. terminal-features ",*:RGB" restores 24-bit colour, without which every gradient in a TUI turns to banding. And set-clipboard on is what makes y in copy mode land on the phone's clipboard over OSC 52, with no helper binary installed on the server.

The three extended-keys lines are the newest of the group and the easiest to miss. They let Ctrl+Shift+…, Shift+Enter and friends through in CSI u form instead of flattening them into their plain equivalents. Claude Code uses Shift+Enter for a newline inside a prompt; without these lines that keystroke arrives as a bare Enter and submits the prompt half-written.

The terminal-overrides line under set-clipboard exists for one specific pairing: tmux running inside a mosh session. An OSC 52 names which clipboard it means, and tmux leaves that name empty — it sends 52;;. mosh matches the target as a literal 52;c;, so it forwards nothing, and a copy made in tmux lands in tmux’s own paste buffer and stops there. The override pins the name to c. Keep the %p1 in the format even though its value is thrown away: a version that skips straight to %p2 loads without complaint and then sends nothing at all.

A phone and a laptop at once

Attach twice and stock tmux sizes every window to the smallest client — so the moment a phone joins, a 200-column laptop is squeezed down to 56 and stays there. window-size latest hands the size to whichever client was last active instead: type on the laptop and it is the laptop's screen, pick up the phone and it re-flows for the phone. aggressive-resize does the same for individual windows.

detach-on-destroy off is there for the same reason. Kill the last pane of a session and stock tmux drops you out to the far-end shell — which, from a phone, means the connection you just spent twenty seconds establishing. Jumping to the next session instead keeps you inside.

Which agent is still working

An agent that finishes a turn rings the bell. With monitor-bell on and visual-bell off, that becomes a red window name in the status line rather than a flash across the screen you are reading, and monitor-activity flags any window that produced output at all in yellow. On a phone, where exactly one window is visible at a time, the status line is the only way to tell which of four agents is still thinking and which is waiting on you.

A status line that follows your theme

Most tmux themes hard-code hex colours, which is why they look wrong the moment the terminal switches between light and dark. This one never names a colour that is not already in the palette: bg=default lets Term's own background show through the status line, and every colour is one of colour0–colour15, which come from the theme you picked in the app.

The result is a status line that re-themes itself. Change theme in Term, or let it follow the system into dark mode, and tmux follows in the same repaint — no config reload, no second theme to keep in sync. reverse on the current window does the same trick for text: it takes the terminal background as its ink, so it reads dark-on-light or light-on-dark by itself.

Keys that suit a thumb

Every binding here removes a chord or a trip across the keyboard. | and - split the way they look. prefix + hjkl moves between panes without going near the arrow keys. prefix + t opens a popup scratch terminal over the current pane — the phone answer to “I need to run one command without disturbing the agent’s layout” — and it closes when you exit.

Alt+1 … Alt+5, with no prefix

Switching windows is the most frequent thing you do in tmux and the most expensive on a phone: prefix, then a digit, is two chords deep. bind -n drops the prefix entirely, and Term’s key bar has a sticky alt, so it comes down to two taps.

Splits that stay where you are

The -c "#{pane_current_path}" on the split and new-window bindings opens the new pane in the directory you are already in. Without it every split starts at $HOME, and the first thing you type on a phone keyboard is a long cd.

Installing it
  1. Copy the file above and save it as ~/.tmux.conf on the server — not on the phone. tmux reads it where the session runs.
  2. Load it once from a shell prompt, with the command below.
  3. After that, prefix + r reloads it following every edit.
the first load, from a shell
tmux source-file ~/.tmux.conf

That middle step looks redundant next to the reload binding, and it is not: prefix + r cannot perform the first load. The binding only exists once the file has been read, and a tmux server reads its config when it starts — which, the first time, was before the file existed. The one reload you cannot do with the shortcut is the one that installs the shortcut. Note also that source-file is a tmux command rather than a shell one: that is why it needs tmux in front of it here, and nothing in front of it inside the config.

Two things to check before any of it takes: this needs tmux 3.4 or newer, because an older build answers “unknown option” at start-up and the setting silently does nothing — tmux -V tells you which you have. And on Debian and Ubuntu the tmux-256color terminfo entry the first line selects ships in the ncurses-term package rather than with tmux itself, so install that alongside it.

See also: Using tmux from a phone · SSH auto-resume · FAQ