demo.mp4
A Zellij plugin that manages your tabs so that you don't have to.
I built this because I kept losing track of which tab was which. I wanted to glance at my tab bar and instantly know what's running where - without manually renaming tabs every time I switch projects or start a new tool. Now my tabs just tell me what I need to know.
- Smart renaming - auto-renames tabs based on configurable Jinja2-like templates (powered by MiniJinja) with context-aware variables (
short_dir,short_git_root,program) - Pane-scoped templates - reference specific panes in templates (
pane[0].*,pane[-1].*) powered by MiniJinja - Manual tab control - toggle a tab to manual mode to prevent auto-renaming, then rename it yourself. Clear the tab name to restore auto-management.
- Dashboard UI - tabbed dashboard (Status, Tabs, Panes, Log, Help) with keyboard and mouse navigation
- Configurable polling - reacts to Zellij events (TabUpdate, PaneUpdate, CwdChanged) with a timer fallback
Download the latest zellij-smart-tabs.wasm from GitHub Releases and place it in your Zellij plugins directory:
mkdir -p ~/.config/zellij/plugins
cp zellij-smart-tabs.wasm ~/.config/zellij/plugins/Requires Rust with the wasm32-wasip1 target:
rustup target add wasm32-wasip1git clone https://github.com/yesyouken/zellij-smart-tabs.git
cd zellij-smart-tabs
make build
make install- Zellij 0.44.0+ - requires the
CwdChangedevent and stabletab_idAPI introduced in 0.44.0 - Nerd Font - the default substitutions use Nerd Font icons. Install one from nerdfonts.com and configure your terminal to use it. Without a Nerd Font, icons will appear as missing glyphs.
Alias the plugin and load the plugin on startup. Replace v0.1.0 with the latest version
plugins {
smart-tabs location="https://github.com/YesYouKenSpace/zellij-smart-tabs/releases/download/v0.1.0/zellij-smart-tabs.wasm" {
// where config for the plugin should go
}
}
load_plugins {
smart-tabs // load smart-tabs on startup we only need 1 instance of it
}All configuration is inline in the plugin block.
| Key | Type | Default | Description |
|---|---|---|---|
format |
String | See Format Gallery | Tab name template (Jinja2-like syntax) |
poll_interval |
Number (seconds) | 5 |
Timer fallback interval for polling |
debounce |
Number (seconds) | 0.2 |
Delay before applying tab rename after data changes |
debug |
Bool | true |
Enable debug logging to Zellij log |
sub |
Block | - | Substitution rules (see below) |
Map program names to custom display names using the sub block:
plugins {
// Note that throughout this guide, we will refer to the plugin via "smart-tabs" alias.
smart-tabs location="https://github.com/YesYouKenSpace/zellij-smart-tabs/releases/download/v0.0.2/zellij-smart-tabs.wasm" {
// where config for the plugin should go
sub {
program {
zsh "" // this would hide the program module if it is zsh
nvim "nvim" // if you dont like the icon and just want it verbatim
go "\u{e65e}" // you prefer over the default
}
}
}
}The {{ program }} template variable will show the substituted value. Programs not in the substitution map keep their original name. Use an empty string "" to hide a program from the tab name.
| Program | Substitution | Unicode |
|---|---|---|
nvim |
| \u{e6ae} |
vim |
| \u{37c5} |
claude |
| \u{f069} |
node |
| \u{f0399} |
zsh |
| \u{f489} |
go |
| \u{e627} |
| Status | Substitution | Unicode |
|---|---|---|
idle |
(empty - hidden) | "" |
running |
| \u{f110} |
pending |
| \u{f009a} |
done |
| \u{f05d} |
error |
| \u{ea87} |
These are Nerd Font icons. Make sure your terminal uses a Nerd Font for them to render correctly. Override any substitution in the sub block.
| Variable | Type | Description |
|---|---|---|
short_dir |
String | Last component of the pane's working directory |
cwd |
String | Full path of the pane's working directory |
short_git_root |
String or undefined | Last component of the git repository root path |
git_root |
String or undefined | Full path to the git repository root |
program |
String or undefined | Currently running program (e.g., nvim, claude, opencode) |
status |
String | Pane activity status (freeform, set via pipe). Defaults: idle, running, pending, done, error. |
All variables are also available scoped to specific panes:
pane[N].*— Nth pane's variables (0-indexed)pane[-1].*— last pane (negative indexing supported)
Top-level variables (e.g., {{ short_dir }}) are aliases for pane[0].* (first pane).
A collection of format strings for different workflows. Copy one into your plugin config:
// Default - IDE-style: project + file context + status
format "{% if short_git_root %}{{ short_git_root }}{% else %}{{ short_dir }}{% endif %}{% if program %} \u{eab6} {{ program }}{% endif %}{% if status %} | {{ status }}{% endif %}"
// => my-repo › nvim | ✅
// Minimal - just the directory name
format "{{ short_dir }}"
// => my-project
// Full path
format "{{ cwd }}"
// => /home/user/Projects/my-project
// Program-first - shows what's running, then where
format "{% if program %}{{ program }} @ {% endif %}{{ short_dir }}"
// => nvim @ my-project
// Status indicators only (great with icon substitutions)
format "{{ short_dir }}{% if status %} {{ status }}{% endif %}{% if program %} {{ program }}{% endif %}"
// => my-repo ⏳
// First pane's program (useful with splits)
format "{{ short_dir }}{% if program %} [{{ program }}]{% endif %}"
// => my-project [nvim]
// Multi-pane - show first and second pane directories
format "{{ short_dir }}{% if pane[1] %} | {{ pane[1].short_dir }}{% endif %}"
// => my-project | docs
By default, all tabs are auto-managed - the plugin renames them based on your format template. To manually rename a tab, first set it to manual mode, then rename it.
Run from any terminal pane:
zellij pipe --name set_focused_to_manual --plugin smart-tabsThis sets the focused tab to manual mode. Manual tabs are skipped by auto-rename.
Add this to your Zellij config (~/.config/zellij/config.kdl) to override the r key in tab mode and the esc key in renametab mode.
keybinds {
tab {
bind "r" {
// It sets the tab to manual mode first, then enters rename mode.
MessagePlugin "smart-tabs" {
name "set_focused_to_manual"
}
SwitchToMode "RenameTab"
TabNameInput 0
}
}
renametab {
bind "esc" {
UndoRenameTab
SwitchToMode "tab"
// It sets the tab to managed mode.
MessagePlugin "smart-tabs" {
name "set_focused_to_managed"
}
}
}
}Three ways to restore a manual tab to auto-management:
- Pipe command - run
zellij pipe --name set_focused_to_managed --plugin smart-tabs - Clear the tab name - rename the tab to an empty string (the plugin detects this and switches back to managed)
- Esc in rename mode - if using the recommended keybinding above, pressing
Esccancels the rename and restores managed mode
Programs can report their activity status to the plugin via pipe. The status is available as {{ status }} (first pane) in templates.
# From a program running inside a Zellij pane:
zellij pipe --name pane_status --plugin smart-tabs -- '{"pane_id": "'$ZELLIJ_PANE_ID'", "status": "running"}'
zellij pipe --name pane_status --plugin smart-tabs -- '{"pane_id": "'$ZELLIJ_PANE_ID'", "status": "done"}'
zellij pipe --name pane_status --plugin smart-tabs -- '{"pane_id": "'$ZELLIJ_PANE_ID'", "status": "error"}'
zellij pipe --name pane_status --plugin smart-tabs -- '{"pane_id": "'$ZELLIJ_PANE_ID'", "status": "idle"}'Status is freeform - you can send any string. The default status substitutions are applied automatically. Custom statuses without a substitution are shown as-is.
Use Claude Code hooks to automatically update pane status when Claude starts and finishes work.
Add this to your Claude Code settings (.claude/settings.json or global settings):
{
"hooks": {
"PreToolUse": [
{
"matcher": "",
"hooks": ["zellij pipe --plugin smart-tabs --name pane_status -- '{\"pane_id\":\"'$ZELLIJ_PANE_ID'\",\"status\":\"running\"}'"]
}
],
"PostToolUse": [
{
"matcher": "",
"hooks": ["zellij pipe --plugin smart-tabs --name pane_status -- '{\"pane_id\":\"'$ZELLIJ_PANE_ID'\",\"status\":\"pending\"}'"]
}
],
"Stop": [
{
"matcher": "",
"hooks": ["zellij pipe --plugin smart-tabs --name pane_status -- '{\"pane_id\":\"'$ZELLIJ_PANE_ID'\",\"status\":\"done\"}'"]
}
]
}
}This sets the pane status to running while Claude processes tool calls, pending between calls, and done when Claude finishes. $ZELLIJ_PANE_ID is set automatically by Zellij for processes running inside panes.
For Linux desktop notifications and other integrations, see the helper scripts in scripts/linux/.
The plugin pane shows a tabbed dashboard with keyboard and mouse navigation.
| Key | View | Content |
|---|---|---|
1 |
Status | Plugin version, format template, config values |
2 |
Tabs | Table of all tabs with position, name, CWD, git root, program, status |
3 |
Panes | Table of all panes across all tabs |
4 |
Log | Debug log entries (enable with debug "true") |
5 |
Help | Template variables, keyboard shortcuts, config reference |
| Key | Action |
|---|---|
1-5 |
Switch view |
Tab / Shift+Tab |
Next / previous view |
j / Down |
Scroll down |
k / Up |
Scroll up |
g |
Scroll to top |
G |
Scroll to bottom |
Esc |
Hide plugin pane |
Mouse click on the tab bar switches views. Mouse scroll works within views.
| Feature | zellij-smart-tabs | zellij-tabula | zellij-tab-rename | zellij-tab-name | opencode-zellij-namer |
|---|---|---|---|---|---|
| Type | Rust WASM | Rust WASM + zsh hook | Rust WASM | Rust WASM | TypeScript (OpenCode) |
| Renames | Tabs | Tabs | Tabs | Tabs | Sessions |
| CWD detection | Events + timer | zsh hook | Events | Shell hook (pipe) | OpenCode events |
| Shell support | Any | zsh only | Any | Any (via pipe) | N/A |
| Configurable name format | Yes | No | Yes | Yes | No |
| Manual rename detection | Yes | No | No | No | No |
| Dashboard UI | Yes | No | No | No | No |
| Standalone | Yes | Yes (zsh required) | Yes | Yes | No |
- zellij-tabula - Tab renaming via zsh shell hook
- zellij-tab-rename - Tab renaming with CwdChanged event
- zellij-tab-name - Pipe-based tab renaming with format strings
- opencode-zellij-namer - AI-powered session naming for OpenCode
- Zellij Plugin API
- MiniJinja - Runtime template engine