Getting Started with Zellij: A Friendly Guide to the Terminal Workspace
What is Zellij?
Zellij is a terminal multiplexer — the same category as tmux and GNU screen. You run it once, and inside it you get panes (split views), tabs, and sessions that keep running after you close your terminal.
The pitch is right in the tagline: “a terminal workspace with batteries included.” Where tmux hands you a blank screen and a config file, Zellij shows you a status bar that lists your keybindings as you go, ships with a session manager, a file browser, and a plugin system, and does something genuinely useful out of the box on the first run.
It’s written in Rust, it’s open source (MIT), and as of 0.44 it runs natively on Linux, macOS, and Windows.
This post walks through:
- Installing Zellij.
- Your first five minutes — the parts you actually need.
- Panes, tabs, and the mode system.
- Sessions: detaching, reattaching, and surviving a reboot.
- Configuration (
config.kdl) with a sane starter setup. - Layouts — describe your project workspace once, open it forever.
- Driving Zellij from scripts with the CLI.
- Sharing a session in a browser.
- Notes for people coming from tmux.
1. Install
macOS
brew install zellij
Linux
Arch (pacman -S zellij), Alpine (apk add zellij), and Nix (nix-env -iA nixpkgs.zellij) all have current packages. Debian, Ubuntu, and Fedora don’t package it at the time of writing — grab the static binary instead:
curl -L https://github.com/zellij-org/zellij/releases/latest/download/zellij-x86_64-unknown-linux-musl.tar.gz \
| tar xz
sudo mv zellij /usr/local/bin/
Any platform with Rust
cargo install --locked zellij
cargo install compiles from source and takes a few minutes. If you’d rather not wait, cargo binstall zellij downloads the prebuilt binary instead.
Windows
Since 0.44 there are native Windows binaries on the releases page. Extract and run zellij.exe from PowerShell or Windows Terminal.
Verify
zellij --version
zellij setup --check # prints the config/layout/data dirs Zellij is using
zellij setup --check is worth running now — it tells you exactly where Zellij expects your config to live, which saves confusion later (the path differs on macOS; more on that below).
2. Your First Five Minutes
Just run it:
zellij
You’ll land in a single pane with a tab bar on top and a status bar at the bottom. That status bar is the whole onboarding story: it lists the available modes and, once you enter one, the keys inside it. You don’t have to memorize anything on day one — you read the bar.
Four things to try immediately:
| What | Keys |
|---|---|
| New pane | Alt n |
| Move between panes | Alt h / j / k / l (or arrow keys) |
| New tab | Ctrl t then n |
| Detach (leave it running) | Ctrl o then d |
After detaching, run zellij attach to come right back where you left off.
The one gotcha to know up front:
Ctrlqquits — it kills the session and everything in it, no confirmation.Ctrlothenddetaches and leaves everything running. Learn the difference before you learn anything else.
3. Panes, Tabs, and Modes
The mode system
This is the one concept that makes Zellij click. Instead of tmux’s single prefix key followed by a one-shot command, Zellij has modes. You press Ctrl + a letter to enter a mode, then press plain letters to act, and Enter or Esc to go back to Normal.
| Mode | Enter with | For |
|---|---|---|
| Pane | Ctrl p | split, close, fullscreen, float, rename |
| Tab | Ctrl t | new, close, rename, jump by number |
| Resize | Ctrl n | grow/shrink the focused pane |
| Move | Ctrl h | relocate a pane in the layout |
| Scroll | Ctrl s | scrollback, search, edit history |
| Session | Ctrl o | detach, session manager, config, sharing |
| Locked | Ctrl g | pass every key through to the app |
| Tmux | Ctrl b | tmux-compatible bindings |
The status bar shows the keys for whichever mode you’re in, so the practical workflow is: press Ctrl + something, read the bar, press the letter.
Pane mode (Ctrl p)
n new pane f toggle fullscreen
d split down w toggle floating panes
r split right e float ↔ embed the focused pane
s new stacked pane i pin a floating pane (stays on top)
x close pane c rename pane
h/j/k/l move focus z toggle pane frames
Tab mode (Ctrl t)
n new tab 1-9 jump to tab N
x close tab Tab toggle last two tabs
r rename tab b break pane out into its own tab
s sync input to all panes in this tab
s (sync) is a small gem: type once, and the input goes to every pane in the tab. Handy for running the same command on four SSH sessions.
Scroll mode (Ctrl s)
j/k or ↑/↓ line up/down s search
Ctrl f/b page down/up e edit scrollback in $EDITOR
d/u half page Ctrl c jump to bottom
Ctrl s then e is the standout: it dumps the pane’s scrollback into your $EDITOR. Full search, copy, and save on a wall of build output, using the editor you already know.
Shortcuts that work in every mode
These skip the mode dance entirely:
Alt n new pane
Alt h/j/k/l move focus (Alt h/l also switches tabs at the edges)
Alt f toggle floating panes
Alt +/- resize focused pane
Alt [ / ] cycle through swap layouts
Ctrl g lock / unlock
Ctrl q quit (careful)
Alt [ and Alt ] are underrated: Zellij ships with preset arrangements, and cycling them re-flows your existing panes without you having to resize anything by hand.
Floating and stacked panes
Two pane types you don’t get in tmux:
Floating panes (Alt f) hover above the layout like a window. They keep running when hidden, so they’re the natural home for a long build, a docker compose logs -f, or a quick scratch shell. Ctrl p then e converts a floating pane into a tiled one and back.
Stacked panes (Ctrl p then s) pile panes on top of each other as a list, showing one expanded at a time. When you have eight things open and no horizontal room left, this keeps them all reachable with the arrow keys instead of squeezing each into a 12-column sliver.
Mouse
Mouse support is on by default: click to focus a pane or tab, drag a border to resize, Ctrl+drag a floating pane to move it, Ctrl+scroll to resize the focused one. Zellij also detects file paths in output — click one and it opens in your editor in a floating pane, jumping to the right line number when the path has one.
4. Sessions
A session is the container for all your tabs and panes. It lives in a background server process, so closing your terminal (or losing an SSH connection) doesn’t kill it.
# Start a named session
zellij -s myproject
# List what's running
zellij ls
# Attach — to a name, or with no argument for the most recent
zellij attach myproject
zellij attach
# Attach, creating it if it doesn't exist (great for shell aliases)
zellij attach -c myproject
# Clean up
zellij kill-session myproject
zellij kill-all-sessions
Detach with Ctrl o then d. Inside a session, Ctrl o then w opens the session manager: a searchable list of every session, running or not, that you can switch to without dropping to the shell first.
Session resurrection
By default Zellij remembers a session’s structure after it exits — the tabs, panes, working directories, and the commands that were running. Attaching to a dead session recreates the whole arrangement, and each command pane comes back suspended so you can press Enter to re-run it.
To also survive a reboot, turn on serialization in your config:
session_serialization true
serialize_pane_viewport true // also restore what was on screen
scrollback_lines_to_serialize 10000
This is not tmux-resurrect-with-plugins. It’s a config line.
To clear out old dead sessions:
zellij delete-session old-thing
zellij delete-all-sessions -y
Auto-attach on shell start
If you want every terminal window to land in the same session, add this to your ~/.zshrc:
eval "$(zellij setup --generate-auto-start zsh)"
Use bash or fish for those shells. It respects ZELLIJ_AUTO_ATTACH and ZELLIJ_AUTO_EXIT if you want to tune the behavior.
5. Configuration
Zellij configures itself in KDL, a config language that reads like a cleaner JSON with braces and no commas.
Where the file lives
zellij setup --check
- Linux:
~/.config/zellij/config.kdl - macOS:
~/Library/Application Support/org.Zellij-Contributors.Zellij/config.kdl
If you sync dotfiles across machines, the macOS path is annoying. Export ZELLIJ_CONFIG_DIR=~/.config/zellij in your shell profile and both platforms use the same location.
Dump the defaults, fully commented, as a starting point:
mkdir -p ~/.config/zellij
zellij setup --dump-config > ~/.config/zellij/config.kdl
You don’t have to restart to see changes — Zellij watches the config file and applies most settings live. You can also edit settings from inside a session with Ctrl o then c, which opens a UI that writes the file for you.
A reasonable starter config
// ~/.config/zellij/config.kdl
theme "catppuccin-mocha"
// "compact" hides the tab bar and shrinks the status bar to one line —
// more room for your actual work once you know the keys.
default_layout "compact"
// Copy to the system clipboard (macOS; use wl-copy or xclip on Linux)
copy_command "pbcopy"
// Selecting text with the mouse copies it immediately
copy_on_select true
// Survive reboots
session_serialization true
serialize_pane_viewport true
scrollback_lines_to_serialize 10000
scroll_buffer_size 50000
// What to do about a Zellij session running inside a pane (e.g. over SSH).
// "ask" prompts; "descend" always hands keys to the inner session on focus.
nested_session_handling "ask"
keybinds {
// Alt+n collides with some terminal emulators; add a second binding
normal {
bind "Alt Enter" { NewPane; }
}
}
Themes ship built in — zellij setup --dump-config lists them, and the config UI (Ctrl o then c) lets you preview them live.
Turning off pane frames
The borders around each pane cost you two rows and two columns per pane. Ctrl p then z toggles them; pane_frames false in the config makes it permanent. With copy_on_select on and frames off, Zellij gets out of the way considerably.
6. Layouts: Your Project Workspace, Automated
This is the feature that pays for itself. A layout is a KDL file describing tabs, panes, working directories, and commands — so zellij --layout dev reconstructs your entire working setup in one keystroke.
A minimal one:
layout {
pane
pane split_direction="vertical" {
pane
pane command="htop"
}
}
A real one for a web project:
layout {
cwd "."
// Without this, your tabs come up with no tab/status bars.
default_tab_template {
pane size=1 borderless=true {
plugin location="zellij:tab-bar"
}
children
pane size=2 borderless=true {
plugin location="zellij:status-bar"
}
}
tab name="code" focus=true split_direction="vertical" {
pane size="65%" name="editor" {
command "nvim"
args "."
}
pane split_direction="horizontal" {
pane name="shell"
pane name="git" {
command "git"
args "status"
close_on_exit false
}
}
}
tab name="servers" {
pane name="dev server" {
command "npm"
args "run" "dev"
// Comes up paused — press Enter to actually start it
start_suspended true
}
pane name="tests" {
command "npm"
args "test" "--" "--watch"
start_suspended true
}
}
tab name="files" {
pane {
plugin location="zellij:strider"
}
}
}
↓ Download zellij-dev-layout.kdl
Drop it in ~/.config/zellij/layouts/dev.kdl and run:
zellij --layout dev
Anything in the layouts directory can be referenced by bare name. You can also point at a path (zellij --layout ./team-layout.kdl) or even a URL — Zellij will ask for confirmation before running any commands from a remote layout.
Three properties worth knowing:
start_suspended true— the pane is created with the command staged but not running. PressEnterto launch it. Perfect for a dev server you don’t always want up.close_on_exit false— the pane survives the command exiting, showing the exit code, andEnterre-runs it. Command panes are first-class in Zellij, not just shells that happen to have something typed in them.default_tab_template— a custom layout replaces the default one entirely, bars included. If your tabs come up bare, this is why.
Running zellij --layout dev while already inside a session adds those tabs to the current session instead of starting a new one. To force a new session, use zellij --new-session-with-layout dev.
Layouts are the answer to “how do I onboard someone onto this repo” — commit the layout file, and a new teammate gets your exact workspace.
Swap layouts
Alt [ and Alt ] cycle through swap layouts: preset arrangements that Zellij re-flows your current panes into. You can define your own next to a layout file (dev.swap.kdl), but the built-in ones cover most cases — the point is that resizing panes by hand is usually avoidable.
7. Driving Zellij from the CLI
Nearly everything the UI does is available as a command, which makes Zellij scriptable in a way tmux only sort of is.
# Run a command in a new pane
zellij run -- cargo test
# ...in a floating pane
zellij run --floating -- docker compose logs -f
# ...in a specific direction, with a name
zellij run --direction down --name "server" -- npm run dev
# Open a file in $EDITOR in a new pane, at a line number
zellij edit src/main.rs --line-number 42
# Fire actions at the current session
zellij action new-tab --name "deploy"
zellij action go-to-tab 2
zellij action write-chars "echo hello"
zellij action dump-screen /tmp/pane.txt
# Target a session by name from outside it
zellij --session myproject action new-pane
Set up completions and you get three short aliases for the most common cases:
# zsh — add to ~/.zshrc
eval "$(zellij setup --generate-completion zsh)"
zr cargo watch -x test # run in a new pane
zrf htop # run in a floating pane
ze src/main.rs # edit in a new pane
Since 0.44 the automation story got noticeably better for scripts: commands return the pane ID they created, --blocking and --block-until-exit-success let a script wait for a pane’s command to finish, and zellij action list-panes / zellij subscribe let you query state or stream a pane’s output to stdout as JSON. That’s enough to build a real pipeline — spawn panes, wait on them, react to the results — from a shell script.
8. Sharing a Session in the Browser
Zellij ships with a built-in web server. It’s off by default; when you turn it on, any session can be opened in a browser at a persistent, bookmarkable URL.
# Start the web server (defaults to 127.0.0.1:8082)
zellij web --start
# Check on it / stop it
zellij web --status
zellij web --stop
# Create a login token
zellij web --create-token --token-name laptop
# A read-only token for someone who should watch but not type
zellij web --create-read-only-token
Or configure it to always run:
web_server true
web_sharing "on"
web_server_ip "127.0.0.1"
web_server_port 8082
Two things worth being deliberate about:
-
Binding beyond localhost requires TLS. Set
web_server_certandweb_server_keyif you’re exposing this on a network. Don’t put an unauthenticated terminal on0.0.0.0. -
Read-only sharing is the safe default for pairing.
zellij watch <session>attaches read-only from a terminal, andCtrlothensopens the sharing plugin to generate browser tokens.0.44 also added attaching to a remote session straight from your terminal, no SSH tunnel:
zellij attach https://example.com/my-cool-session
9. Coming from tmux
If your fingers already know tmux, press Ctrl b — that enters tmux mode, where ", %, c, ,, n, p, d, z, x, and the arrow keys do what you expect. It’s a bridge, not a full emulation, but it makes the first week painless.
The real differences:
| tmux | Zellij | |
|---|---|---|
| Discovery | read the man page | read the status bar |
| Config | .tmux.conf, restart to apply | config.kdl, hot-reloaded |
| Workspace setup | tmuxinator / a shell script | built-in layouts |
| Session restore | tmux-resurrect plugin | one config line |
| Floating panes | popups (limited) | first-class, persistent |
| Plugins | shell scripts | WebAssembly, any language |
| Browser access | — | built in |
And the honest downsides: Zellij uses more memory than tmux, it isn’t installed on every server you’ll ever SSH into, and if you have a decade of muscle memory and a tuned .tmux.conf, the switch may not buy you much. Zellij’s own docs are relaxed about this — it’s a different set of tradeoffs, not a strict upgrade.
10. Gotchas
Ctrlqkills the session. No confirmation. Rebind it if that makes you nervous:bind "Ctrl q" { SwitchToMode "Normal"; }undershared_except "locked".Altkeys may not reach Zellij. Some terminals sendEsc-prefixed sequences instead. On macOS Terminal / iTerm2, enable “Use Option as Meta key.” Otherwise add your own bindings.Ctrlglocks everything. If Zellij suddenly stops responding to shortcuts, you’re in locked mode — the status bar says so.Ctrlgagain unlocks.- Vim/Emacs users:
Ctrlp,Ctrln,Ctrlb,Ctrls,Ctrlo,Ctrlt, andCtrlhare all taken by Zellij. Either rebind them inconfig.kdlor use locked mode (Ctrlg) while you’re in the editor. - The macOS config path is not
~/.config. SetZELLIJ_CONFIG_DIRif you want it to be. - A custom layout replaces the default one. No
default_tab_template, no bars.
Next Steps
- Plugins: the status bar, tab bar, session manager, and file browser are all plugins — anything compiling to WebAssembly can be one.
Ctrlothenpopens the plugin manager. Worth a look: zjstatus (a highly configurable status bar) and zellij-forgot (a searchable keybinding cheatsheet). - Themes: dozens ship built in;
Ctrlothencpreviews them live. - Multiple pane select:
Alt+left-click marks panes, then close, stack, or break them out to a tab in bulk. - Commit a layout to your repo. It’s the single highest-leverage thing here.