CLI Recipes & Scripting
This page provides task-oriented examples for controlling Zellij from the command line and shell scripts. For a full reference of all available actions, see CLI Actions. For patterns oriented toward non-interactive, machine-driven control (output polling, event loops, concurrency), see Programmatic Control.
- Targeting Specific Panes and Tabs
- Sending Input to Another Pane
- Watching Pane Output in Real Time
- Starting and Controlling Background Sessions
- Scripting Pane and Tab Creation
- Inspecting Session State
- Controlling Floating Panes
- Borderless Panes
- Toggling Pane Visibility
- Changing Pane Colors
- Blocking Panes
- Scrollback and Screen Capture
- Session Management
- Working with Plugins from the CLI
- Layout Overrides at Runtime
Targeting Specific Panes and Tabs
Many CLI actions accept --pane-id or --tab-id flags, allowing commands to be directed at specific panes or tabs without changing focus. This eliminates the need to switch focus before issuing a command.
Pane IDs
Every terminal pane exposes its ID through the $ZELLIJ_PANE_ID environment variable. Pane IDs are specified as terminal_N, plugin_N, or a bare integer N (equivalent to terminal_N).
Discover all pane IDs in the current session:
zellij action list-panes
Sample output:
PANE_ID TYPE TITLE
terminal_1 terminal /bin/bash
plugin_0 plugin tab-bar
terminal_2 terminal vim main.rs
Or as JSON for structured processing:
zellij action list-panes --json
Sample output:
[
{
"id": 1,
"is_plugin": false,
"is_focused": true,
"title": "/bin/bash",
"is_floating": false,
"tab_id": 0,
"tab_name": "Tab #1",
"pane_command": "bash",
"pane_cwd": "/home/user/project"
}
]
(JSON output includes many additional fields - see Inspecting Session State for full details)
Tab IDs
Tab IDs can be discovered with:
zellij action list-tabs
Sample output:
TAB_ID POSITION NAME
0 0 Tab #1
1 1 editor
2 2 logs
Or as JSON:
zellij action list-tabs --json
Sample output:
[
{
"position": 0,
"name": "Tab #1",
"active": true,
"tab_id": 0
}
]
(JSON output includes many additional fields - see Inspecting Session State for full details)
Or get the current tab info:
zellij action current-tab-info
Sample output:
name: Tab #1
id: 0
position: 0
Examples
Clear a specific pane without focusing it:
zellij action clear --pane-id terminal_3
Scroll to the top of a specific pane:
zellij action scroll-to-top --pane-id terminal_5
Close a specific tab by its ID:
zellij action close-tab --tab-id 3
Sending Input to Another Pane
Commands can be sent directly to any pane by ID. There is no need to change focus first.
Send keystrokes to a specific pane:
zellij action send-keys --pane-id terminal_3 "Enter" "ctrl c"
Write a string of characters one-by-one to a specific pane:
zellij action write-chars --pane-id terminal_3 "echo hello"
Paste text (using bracketed paste mode) to a specific pane (faster and more robust than write-chars):
zellij action paste --pane-id terminal_3 "multi-line\ntext content"
Watching Pane Output in Real Time
The zellij subscribe command streams the rendered output of one or more panes to stdout. This is useful for monitoring builds, logs, or any running process without keeping the pane visible.
Monitor a pane's output:
zellij subscribe --pane-id terminal_1
Filter live output for errors using JSON mode and jq:
zellij subscribe --pane-id terminal_1 --format json | jq --unbuffered 'select(.event == "pane_update") | .viewport[] | select(test("ERROR"))'
Use JSON format with jq for structured processing:
zellij subscribe --pane-id terminal_1 --format json | jq 'select(.event == "pane_update") | .viewport[]'
Monitor multiple panes simultaneously:
zellij subscribe --pane-id terminal_1 --pane-id terminal_2 --format json
Starting and Controlling Background Sessions
A Zellij session can be created in the background without attaching to it. This is useful for headless workflows, CI pipelines, and scripted environments.
Create a background session:
zellij attach --create-background my-session
Create a background session with a specific layout:
zellij attach --create-background my-session options --default-layout compact
Create a background session with a custom layout file:
zellij attach --create-background my-session options --default-layout /path/to/layout.kdl
Once a background session is running, actions can be issued against it using the global --session flag:
Send keystrokes to a pane in the background session:
zellij --session my-session action paste "make build" --pane-id terminal_1 &&
zellij --session my-session action send-keys --pane-id terminal_1 "Enter"
Open a new pane in the background session:
PANE_ID=$(zellij --session my-session action new-pane)
zellij --session my-session action paste "npm test" --pane-id $PANE_ID &&
zellij --session my-session action send-keys --pane-id $PANE_ID "Enter"
Subscribe to a pane's output in the background session (see Zellij Subscribe):
zellij --session my-session subscribe --pane-id terminal_1 --format json
Dump the screen of a specific pane in the background session:
zellij --session my-session action dump-screen --pane-id terminal_1 --full
Full Scripted Workflow Example
#!/bin/bash
# Create a background session with a layout
zellij attach --create-background ci-runner options --default-layout compact
# Open a pane and capture its ID
BUILD_PANE=$(zellij --session ci-runner action new-pane --name "build")
# Start a build
zellij --session ci-runner action paste --pane-id $BUILD_PANE "cargo build 2>&1" &&
zellij --session ci-runner action send-keys --pane-id $BUILD_PANE "Enter"
# Monitor the build output for relevant lines, exit when the pane closes
zellij --session ci-runner subscribe --pane-id $BUILD_PANE --format json \
| jq --unbuffered 'select(.event == "pane_update") | .viewport[] | select(test("error|warning|Finished"))'
Scripting Pane and Tab Creation
Several CLI actions return the ID of the created pane or tab, making it possible to chain commands in scripts.
Actions that return a pane ID:
new-paneeditlaunch-pluginlaunch-or-focus-plugintoggle-floating-panes(when a new floating pane is created, this happens when no floating panes exist in that tab)show-floating-panes(when a new floating pane is created, this happens when no floating panes exist in that tab)
Actions that return a tab ID:
new-tabgo-to-tab-name --create(when a new tab is created)
Capture a new pane ID and send commands to it:
PANE_ID=$(zellij action new-pane --name "my worker")
zellij action paste --pane-id $PANE_ID "python worker.py" &&
zellij action send-keys --pane-id $PANE_ID "Enter"
Create a floating pane with specific coordinates and use its ID:
PANE_ID=$(zellij action new-pane --floating --width 80 --height 24 --x 10% --y 10%)
zellij action paste --pane-id $PANE_ID "htop" &&
zellij action send-keys --pane-id $PANE_ID "Enter"
Create a tab and capture its ID:
TAB_ID=$(zellij action new-tab --name "tests" --layout /path/to/test-layout.kdl)
Inspecting Session State
Information about the current session can be queried for use in scripts or external status bars. For a full reference of these commands, see the CLI Actions page.
Listing Panes
List all panes with full details:
zellij action list-panes --all
Sample output:
TAB_ID TAB_POS TAB_NAME PANE_ID TYPE TITLE COMMAND CWD FOCUSED FLOATING EXITED X Y ROWS COLS
0 0 Tab #1 terminal_1 terminal /bin/bash bash /home/user/project true false false 0 1 24 80
0 0 Tab #1 plugin_0 plugin tab-bar zellij:tab-bar - false false false 0 0 1 80
1 1 Tab #2 terminal_2 terminal vim main.rs vim main.rs /home/user/project/src true false false 0 1 24 80
Get JSON output for programmatic use:
zellij action list-panes --json | jq '.[] | select(.is_focused == true)'
Sample output:
{
"id": 1,
"is_plugin": false,
"is_focused": true,
"is_fullscreen": false,
"is_floating": false,
"is_suppressed": false,
"title": "/bin/bash",
"exited": false,
"exit_status": null,
"is_held": false,
"pane_x": 0,
"pane_content_x": 1,
"pane_y": 1,
"pane_content_y": 2,
"pane_rows": 24,
"pane_content_rows": 22,
"pane_columns": 80,
"pane_content_columns": 78,
"cursor_coordinates_in_pane": [0, 5],
"terminal_command": null,
"plugin_url": null,
"is_selectable": true,
"tab_id": 0,
"tab_position": 0,
"tab_name": "Tab #1",
"pane_command": "bash",
"pane_cwd": "/home/user/project"
}
Listing Tabs
List all tabs with state and layout info:
zellij action list-tabs --state --layout
Sample output:
TAB_ID POSITION NAME ACTIVE FULLSCREEN SYNC_PANES FLOATING_VIS SWAP_LAYOUT LAYOUT_DIRTY
0 0 Tab #1 true false false false default false
1 1 editor false false false false - false
2 2 logs false false false true default true
Get JSON output:
zellij action list-tabs --json
Sample output:
[
{
"position": 0,
"name": "Tab #1",
"active": true,
"panes_to_hide": 0,
"is_fullscreen_active": false,
"is_sync_panes_active": false,
"are_floating_panes_visible": false,
"other_focused_clients": [],
"active_swap_layout_name": "default",
"is_swap_layout_dirty": false,
"viewport_rows": 24,
"viewport_columns": 80,
"display_area_rows": 26,
"display_area_columns": 80,
"selectable_tiled_panes_count": 2,
"selectable_floating_panes_count": 0,
"tab_id": 0,
"has_bell_notification": false,
"is_flashing_bell": false
}
]
Current Tab Info
Get information about the currently active tab:
zellij action current-tab-info
Sample output:
name: Tab #1
id: 0
position: 0
Get full details as JSON:
zellij action current-tab-info --json
Sample output:
{
"position": 0,
"name": "Tab #1",
"active": true,
"panes_to_hide": 0,
"is_fullscreen_active": false,
"is_sync_panes_active": false,
"are_floating_panes_visible": false,
"other_focused_clients": [],
"active_swap_layout_name": "default",
"is_swap_layout_dirty": false,
"viewport_rows": 24,
"viewport_columns": 80,
"display_area_rows": 26,
"display_area_columns": 80,
"selectable_tiled_panes_count": 2,
"selectable_floating_panes_count": 0,
"tab_id": 0,
"has_bell_notification": false,
"is_flashing_bell": false
}
Other Queries
List connected clients:
zellij action list-clients
Query all tab names:
zellij action query-tab-names
Controlling Floating Panes
Create a floating pane with specific coordinates:
zellij action new-pane --floating --x 10% --y 10% --width 80% --height 80%
Show or hide all floating panes:
zellij action show-floating-panes
zellij action hide-floating-panes
Pin a floating pane so it stays on top:
zellij action toggle-pane-pinned --pane-id terminal_5
Reposition and resize a floating pane:
zellij action change-floating-pane-coordinates --pane-id terminal_5 --x 20 --y 10 --width 50% --height 50%
Borderless Panes
A pane can be created without a border using the --borderless flag. Combined with --pinned, this creates a persistent overlay that appears as part of the terminal UI itself - with no visible frame separating it from the rest of the screen.
For example, a small pane pinned to the top-right corner that continuously displays the current git branch and status:
zellij action new-pane --floating --borderless true --pinned true \
--width "20%" --height 1 --x "75%" --y 2 \
-- bash -c 'while true; do printf "\r%-40s" "$(git -C /home/user/project branch --show-current) $(git -C /home/user/project status --short | wc -l) changed"; sleep 5; done'
This creates a single-line overlay that stays on top of all other panes, has no border, and continuously refreshes - functioning like a custom status bar element.
Another example - a persistent resource monitor pinned to a corner:
zellij action new-pane --floating --borderless true --pinned true \
--width 30 --height 5 --x "100%" --y "100%" \
-- watch -n2 -t "free -h | head -3"
Toggle the borderless state of an existing pane:
zellij action toggle-pane-borderless --pane-id terminal_5
Explicitly set the borderless state:
zellij action set-pane-borderless --pane-id terminal_5 --borderless true
Toggling Pane Visibility
A tiled pane can be floated and a floating pane can be embedded. This is useful for background tasks - a long-running process can be kept in a floating pane whose visibility is toggled as needed, keeping the main workspace uncluttered.
Float a tiled pane or embed a floating pane:
zellij action toggle-pane-embed-or-floating --pane-id terminal_3
Toggle the visibility of all floating panes in the current tab:
zellij action toggle-floating-panes
A common pattern is to start a background task in a floating pane, hide floating panes to keep the workspace clean, and show them again when the task needs attention:
# Start a long-running build in a floating pane
PANE_ID=$(zellij action new-pane --floating --name "build")
zellij action paste --pane-id $PANE_ID "cargo build --release 2>&1" &&
zellij action send-keys --pane-id $PANE_ID "Enter"
# Hide floating panes to focus on other work
zellij action hide-floating-panes
# Later, show them again to check progress
zellij action show-floating-panes
Changing Pane Colors
The foreground and background colors of a pane can be changed at runtime with set-pane-color. Colors are specified as hex (eg. "#00e000") or rgb notation (eg. "rgb:00/e0/00"). This can be used to visually distinguish panes, flash a pane to get the user's attention, or color-code panes by purpose.
Set both foreground and background:
zellij action set-pane-color --pane-id terminal_3 --fg "#00e000" --bg "#001a3a"
Set only the background:
zellij action set-pane-color --pane-id terminal_3 --bg "#3a0000"
Reset colors back to the terminal defaults:
zellij action set-pane-color --pane-id terminal_3 --reset
Flash a pane red briefly to get attention (from a script):
zellij action set-pane-color --pane-id terminal_3 --bg "#5a0000"
sleep 1
zellij action set-pane-color --pane-id terminal_3 --reset
When no --pane-id is specified, the command defaults to the pane identified by the $ZELLIJ_PANE_ID environment variable, making it easy to use from within a pane's own shell:
zellij action set-pane-color --bg "#001a3a"
Blocking Panes
A pane can be opened with blocking flags that cause the calling process to wait until the command in the pane completes. This is powerful for scripting multi-step workflows where each step depends on the previous one.
Waiting for a Command to Finish
Block until the command exits and the user closes the pane (by pressing Ctrl-c):
zellij action new-pane --blocking -- cargo test
Or equivalently with zellij run:
zellij run --blocking -- cargo test
The calling shell will not continue until the pane has been closed. The user can review the output, then press Ctrl-c to close the pane and unblock.
Waiting for Success or Failure
The --block-until-exit-success flag unblocks only when the command exits with status 0. If it fails, the pane stays open and the user can press Enter to retry - the calling process remains blocked until the command succeeds (or the pane is closed manually):
zellij action new-pane --block-until-exit-success -- cargo build
echo "Build succeeded, continuing..."
If cargo build fails, the pane will display the error and wait. The user can fix the issue in another pane, then go back and press Enter to retry. The script only continues once the build passes.
This retry can also be triggered remotely from another pane or script:
zellij action send-keys --pane-id terminal_3 "Enter"
Similarly, --block-until-exit-failure unblocks only when the command exits with a non-zero status:
zellij action new-pane --block-until-exit-failure -- ./run-server.sh
echo "Server crashed, running cleanup..."
And --block-until-exit unblocks when the command exits regardless of its status:
zellij action new-pane --block-until-exit -- ./my-task.sh
echo "Task finished (exit status does not matter), moving on..."
Scripted Multi-Step Workflows with Human Intervention
These flags are especially useful for workflows that may require human intervention at certain steps. The script pauses at each blocking pane, and the user can inspect, fix, and retry as needed before the script continues:
#!/bin/bash
# Step 1: run tests - retry until they pass
zellij action new-pane --block-until-exit-success --name "tests" -- cargo test
# Step 2: build release - retry until it succeeds
zellij action new-pane --block-until-exit-success --name "release build" -- cargo build --release
# Step 3: deploy - wait for it to finish regardless of outcome
zellij action new-pane --block-until-exit --name "deploy" -- ./deploy.sh
echo "Pipeline complete."
At each step, if the command fails, the pane remains open. The user can investigate the failure, make fixes in other panes, and press Enter in the blocking pane to retry. The script advances only after each step succeeds (or completes, for --block-until-exit).
Blocking Panes in New Tabs
The same blocking flags are available on new-tab for its initial command:
zellij action new-tab --block-until-exit-success -- cargo test
Scrollback and Screen Capture
Dump the viewport of the focused pane to stdout:
zellij action dump-screen
Dump full scrollback with ANSI styling to a file:
zellij action dump-screen --path /tmp/capture.txt --full --ansi
Dump a specific pane's content:
zellij action dump-screen --pane-id terminal_3 --full
Open the scrollback of a specific pane in the default editor:
zellij action edit-scrollback --pane-id terminal_3
Session Management
Rename the current session:
zellij action rename-session "my-project"
Save session state immediately (for session resurrection):
zellij action save-session
Switch to a different session:
zellij action switch-session other-session
Detach from the current session:
zellij action detach
Working with Plugins from the CLI
Launch a new plugin instance:
zellij action launch-plugin file:/path/to/plugin.wasm --floating
Send data to a plugin via pipe:
echo "some data" | zellij pipe --name my-pipe --plugin "my-plugin-alias"
Reload a plugin during development (see Plugin Development):
zellij action start-or-reload-plugin file:/path/to/plugin.wasm
Layout Overrides at Runtime
Replace the current tab's layout with a different one:
zellij action override-layout /path/to/new-layout.kdl
Keep existing panes that do not fit the new layout:
zellij action override-layout /path/to/layout.kdl --retain-existing-terminal-panes --retain-existing-plugin-panes
Apply the layout only to the active tab:
zellij action override-layout /path/to/layout.kdl --apply-only-to-active-tab