Zellij Cheatsheet

Every keybind · every CLI flag · every layout primitive

How modes work: Zellij is modal. From NORMAL press a "switch" key (Ctrl+P, Ctrl+T, …) to enter another mode. In that mode, single-letter keys do things. Press Enter or Esc to return to NORMAL. The status bar always shows which mode you're in.

NORMAL mode entry keys

Alt+penter PANE mode (custom — Ctrl+P freed for nvim)
Ctrl+Tenter TAB mode
Ctrl+Nenter RESIZE mode
Ctrl+Senter SCROLL mode
Alt+oenter SESSION mode (custom — Ctrl+O freed for nvim jumplist)
Alt+h / Alt+lprevious / next zellij tab (custom — overrides default MoveFocusOrTab)
Alt+← / Alt+→MoveFocusOrTab — pane focus, crosses tab edge
Ctrl+Genter LOCKED — all keys passthrough
Ctrl+Qquit zellij & kill session
Custom in your config: Ctrl+P and Ctrl+O moved to Alt+p / Alt+o so nvim keeps <C-p> and <C-o>. Zellij tabs on Alt+h / Alt+l so Ctrl+H/L pass through to nvim window-direction & bash. Wezterm tabs moved to Ctrl+Alt+H / Ctrl+Alt+L.
Heads-up: Ctrl+Q kills the session. To leave it running use SESSIONdetach.

PANE  (Alt+p — was Ctrl+P)

nnew pane (auto direction)
dsplit down (horizontal)
rsplit right (vertical)
xclose focused pane
h j k l / arrowsmove focus
pcycle focus through panes
ffullscreen toggle (zoom)
ztoggle pane frames
wtoggle floating panes
etoggle embed ↔ float for focused pane
crename pane (Enter to commit)
itoggle pinned (floating pane stays on top of all tabs)

TAB  (Ctrl+T)

nnew tab
xclose tab
rrename tab
h l / arrowsprevious / next tab
19jump to tab N
stoggle sync (input broadcast to all panes in tab)
bbreak pane out into its own tab
] / [move pane to next / previous tab
Tabjump to most-recent tab

RESIZE  (Ctrl+N)

h j k l / arrowsshrink toward / grow away from that edge
+ / -grow / shrink uniformly
=reset to even split
Resize is "press repeatedly to nudge". The mode stays until you press Enter.

SCROLL  (Ctrl+S)

k / jup / down one line
Ctrl+u / Ctrl+dhalf-page up / down
PgUp / PgDnfull page up / down
eopen scrollback in $EDITOR
/enter SEARCH submode
senter SEARCH submode (alt)

SEARCH submode

type then Entersearch forward
n / pnext / previous match
ctoggle case sensitivity
wtoggle whole-word
otoggle wrap-around

SESSION  (Alt+o — was Ctrl+O)

ddetach (session keeps running)
wsession manager — list / switch / resurrect
cconfiguration browser
pplugin manager
aabout
Detach is the magic. Hit Alt+o d, close wezterm, reopen, run zellij attach NAME — everything resumes.

MOVE  (Ctrl+H)

h j k l / arrowsmove focused pane in that direction
nmove pane to next position (snake order)
pmove pane to previous position
Tabcycle pane positions

LOCKED  (Ctrl+G)

Every key is forwarded to the running program. Use this when you're inside vim, htop, tig, or anything that uses the same Ctrl-prefixed keys as zellij.

Ctrl+Gunlock — return to NORMAL
Status bar shows a padlock icon while locked.

Pane shortcuts from NORMAL

Some pane keys don't need PANE mode — Alt-prefixed shortcuts work from NORMAL:

Alt+nnew pane
Alt+h/j/k/lmove focus
Alt+[ / Alt+]previous / next pane (focus cycle)
Alt+ftoggle float
Alt+= / Alt+-grow / shrink focused pane
Alt+i / Alt+omove tab left / right
i3 collision: on Mod4 you're fine, but if you ever use Mod1 (Alt) in i3, Alt+letter shortcuts will conflict.

Start & attach

zellijstart default session (or attach if one named $(zellij list-sessions) exists)
zellij -s NAMEstart a named session
zellij attach NAMEattach to an existing session
zellij attach -c NAMEattach, create if missing
zellij attach -b NAMEattach in background (detaches first)
zellij attach --force NAMEattach even if already attached (steals)

Listing & killing

zellij lslist active & resurrectable sessions
zellij list-sessionssame, long form
zellij kill-session NAMEstop a specific session
zellij kill-all-sessionsstop everything (prompts)
zellij delete-session NAMEremove a serialized session from disk
zellij delete-all-sessionswipe all serialized sessions

Layouts

zellij --layout NAMEstart with a named layout from ~/.config/zellij/layouts/
zellij --layout ./path.kdllayout from a path
zellij setup --dump-layout defaultprint built-in default layout
zellij setup --dump-layout compactprint built-in compact layout

Run / Edit (one-shot)

zellij run -- CMD ARGSopen new pane running CMD (uses attached session)
zellij run -d right -- CMDopen in a new right-split pane
zellij run -f -- CMDopen as a floating pane
zellij run -i -- CMDclose pane after CMD exits
zellij edit FILEopen FILE in $EDITOR as a new pane
zellij edit -f FILEedit in a floating pane
Run these from inside a zellij session — they target the current session by default.

Actions (scriptable)

Send any keybind action from outside — great for scripts, polybar, sxhkd, i3 binds.

zellij action new-panesplit a new pane
zellij action new-pane -d rightnew vertical split (right)
zellij action new-tabnew tab
zellij action go-to-tab 2jump to tab 2
zellij action move-focus left|right|up|downmove focus
zellij action close-paneclose focused pane
zellij action toggle-pane-framesshow / hide pane borders
zellij action rename-tab "build"rename current tab
zellij action write-chars "ls -la\n"type into focused pane
zellij action dump-screen FILEdump current pane contents

Setup & introspection

zellij setup --checkvalidate config + show paths
zellij setup --dump-configprint full default config (KDL)
zellij setup --generate-completion bashgenerate shell completion
zellij setup --generate-auto-start bashsnippet that auto-attaches on shell start
zellij options --simplified-ui truelaunch with simplified UI (no nerd-font glyphs)
zellij --debuglog to /tmp/zellij-*

Convert from tmux/screen

zellij convert-config FILEconvert old YAML zellij config
zellij convert-layout FILEconvert old YAML layout
zellij convert-theme FILEconvert old YAML theme

Locations

~/.config/zellij/config.kdlmain config
~/.config/zellij/layouts/*.kdlnamed layouts
~/.config/zellij/themes/*.kdluser themes
~/.config/zellij/plugins/*.wasmuser plugins
~/.cache/zellij/runtime cache
~/.local/share/zellij/data: sessions, plugin store
Config reload is automatic — save the file and zellij picks it up.

Top-level options

default_shell "bash"
default_layout "compact"
theme "catppuccin-mocha"
pane_frames false
simplified_ui false
mouse_mode true
on_force_close "detach"
copy_command "xclip -selection clipboard"
copy_on_select false
scrollback_lines_to_serialize 10000

Persistence

session_serialization true
pane_viewport_serialization true
serialize_pane_viewport true
scrollback_lines_to_serialize 10000
Effect: on detach, zellij saves the layout + scrollback to disk. Next zellij attach rebuilds them.

Themes

Set theme "NAME". Bundled: default, dracula, gruvbox-dark, gruvbox-light, solarized-dark, tokyo-night, tokyo-night-storm, nightfox, catppuccin-mocha, catppuccin-frappe, catppuccin-macchiato, catppuccin-latte, everforest-dark, everforest-light + several more.

themes {
  tokyo-night-custom {
    fg 192 202 245
    bg 26 27 38
    black 15 15 20
    red 247 118 142
    // green, yellow, blue, magenta, cyan, white, orange
  }
}

Keybind syntax (your config)

keybinds {
  normal {
    // Free Ctrl+P / Ctrl+O for nvim, expose them on Alt instead.
    unbind "Ctrl p"
    unbind "Ctrl o"
    bind "Alt p" { SwitchToMode "Pane"; }
    bind "Alt o" { SwitchToMode "Session"; }

    // Tab nav on Alt+h/l so Ctrl+H/L stay free for nvim & bash.
    bind "Alt h" { GoToPreviousTab; }
    bind "Alt l" { GoToNextTab; }
  }
  locked {
    bind "Ctrl g" { SwitchToMode "Normal"; }
  }
}
Modes: normal pane tab resize scroll search session move locked tmux. shared_except and shared_among let you bind once across many. unbind removes a default before you replace it.

Common actions for binds

SwitchToMode "Pane"change mode
NewPane "Down"|"Right"split
CloseFocusclose pane
MoveFocus "Left"focus
Resize "Increase Left"resize
NewTab / GoToTab 1tabs
ScrollUp / HalfPageScrollDownscroll
Detach / Quitleave
Run { cmd "htop"; cwd "/"; }spawn command
EditScrollbackopen buffer in $EDITOR
TogglePaneFrames / ToggleFloatingPanesUI
ToggleMouseModemouse on/off

UI options

ui {
  pane_frames {
    rounded_corners true
    hide_session_name false
  }
}
show_startup_tips false
show_release_notes false
scroll_buffer_size 10000
styled_underlines true

What a layout is

A KDL file describing tabs and panes for a session. Launch with zellij --layout NAME — zellij looks in ~/.config/zellij/layouts/NAME.kdl then falls back to built-ins.

A layout is consumed once, when the session is created. After that, edits don't change a running session.

Minimal example

layout {
  pane
}

One tab, one pane, your default shell. That's the cheapest valid layout.

Splits

layout {
  pane split_direction="vertical" {
    pane
    pane split_direction="horizontal" {
      pane
      pane
    }
  }
}

split_direction on a parent dictates how its children are arranged.

Sizing

pane size="70%"
pane size=10     // rows or columns
pane             // no size = "fill remaining"
Pixels / cells / percents are honored differently per direction. Leave panes unsized to fill.

Panes that run things

pane command="htop"
pane command="watch" {
  args "-n" "1" "date"
}
pane command="nvim" cwd="~/dev/myproj" {
  args "+15" "src/main.rs"
}
pane edit="README.md"
command + args spawns a process. edit opens a file in $EDITOR. Both replace the default shell.

Tabs

layout {
  tab name="code" focus=true {
    pane size="70%" name="editor"
    pane split_direction="vertical" {
      pane name="shell"
      pane name="logs"
    }
  }
  tab name="scratch" {
    pane
  }
}
Your dev layout — already at ~/.config/zellij/layouts/dev.kdl.

Templates & reuse

layout {
  pane_template name="watcher" {
    pane command="watch" {
      args "-n" "2" "git" "status"
    }
  }
  tab {
    watcher
    pane
  }
}

Define once, reference by name. tab_template works the same for tab structure.

Floating panes

layout {
  pane
  floating_panes {
    pane command="btm" {
      width "60%"
      height "60%"
      x "20%"
      y "20%"
    }
  }
}

Plugins as panes

pane size=1 borderless=true {
  plugin location="zellij:status-bar"
}
pane size=2 borderless=true {
  plugin location="zellij:tab-bar"
}

Built-in plugins: status-bar, tab-bar, compact-bar, strider, session-manager, configuration, plugin-manager, about.

Lifecycle

Active session = running zellij process with at least one attached client. Detached = the process is alive, no client connected. Resurrectable = the process is gone but its serialized state is on disk; attach rebuilds it.

createzellij -s NAME or zellij attach -c NAME
attachzellij attach NAME (or just zellij if one exists)
detachAlt+o d (keeps process)
killzellij kill-session NAME or Ctrl+Q (ends process; serialized state may remain)
resurrectzellij attach NAME against a resurrectable session
deletezellij delete-session NAME (wipes serialized state)

Inside session manager

Open with Alt+o then w.

navigate sessions
Enterswitch to session (detaches current)
Ctrl+rrename session
Ctrl+ddisconnect other clients from this session
Delkill / delete session
Escclose manager

Multi-attach

Two terminals can attach to the same session. Both see the same panes; input from either goes to the focused pane. Useful for pairing.

# terminal A
zellij -s pair
# terminal B (other person, or you SSHed in)
zellij attach pair
To kick the other client without killing the session: session-manager → Ctrl+d.

Where state lives

~/.local/share/zellij/$VER/cacheplugin cache
~/.cache/zellij/$VER/<session>runtime, control sockets
~/.local/share/zellij/$VER/sessions/<name>serialized panes & scrollback (when session_serialization is on)
Lose a session you wanted? Check the sessions dir — chances are it's resurrectable with zellij attach NAME.

Auto-attach on shell start

Optional. Add this to ~/.bashrc to make every wezterm tab attach to a single main session:

# Auto-attach to or create a zellij session called "main"
# unless we're already inside zellij or in a non-interactive shell.
if [[ -z "$ZELLIJ" ]] && [[ $- == *i* ]]; then
  zellij attach -c main
fi
Tradeoff: every shell joins the same session. Spawning a "raw" terminal becomes harder. Try the manual path for a week first.

Common gotchas

config didn't reloadsyntax error — run zellij setup --check
Ctrl+Q killed workuse Alt+o d to detach instead
Ctrl+O / Ctrl+P go to nvimexpected — this config rebound them to Alt+o / Alt+p so nvim keeps its jumplist and file-picker
vim key collisionsenter LOCKED mode with Ctrl+G
weird font glyphszellij options --simplified-ui true
scrollback lost on attachset scrollback_lines_to_serialize 10000 (default 0)
nothing on zellij ls after rebootexpected — runtime state is in /tmp; serialized state is on disk and resurrects via attach