I'm not particularly dogmatic about tools. If something works well for years, great; but I'm always open to trying alternatives when they promise meaningful improvements. In a previous post, I wrote about a shell script I built to automate tmux session management. That setup served me well for a long time, handling dozens of project switches every day with minimal friction.
Then, a couple of months ago, I came across Zellij. At first glance, it looked like another terminal multiplexer trying to reinvent tmux. I was skeptical, tmux works, it's battle-tested, and I had already invested time building tooling around it. But something about Zellij's approach to configuration caught my attention: native layout files written in a clean configuration language.
After trying it for a few weeks, I realized Zellij wasn't just "tmux with a different config format." It solved some of the pain points I had accepted as inevitable, and it made my custom script significantly simpler.
What Stayed the Same
The core workflow concept remained unchanged. I still wanted to type dev my-project
and instantly have a pre-configured development environment ready to go. The muscle
memory, the command syntax, even the directory structure for storing configurations,
all of that stayed exactly the same.
This was important to me. I didn't want to relearn my entire workflow. I wanted the benefits of a better tool without throwing away what already worked.
What Changed: A Much Simpler Script
Here's the new script in its entirety:
#!/bin/sh
#
# Setup Zellij
#
PARAM=${1:-dev}
if [ "${PARAM}" != "." ]; then
zellij a ${PARAM} || zellij -s ${PARAM} -n "${HOME}/.config/tmux-dev/dev/${PARAM}.kdl"
else
zellij a ${PARAM} || zellij -s $(basename "${PWD}") -n "${HOME}/.config/tmux-dev/dev/default.kdl"
fi
Compare this to the 40+ lines of tmux scripting. The Zellij version is eight lines, and most of that is comments and whitespace. The logic is simple: try to attach to an existing session, and if that fails, create a new one using the specified layout file.
All the complexity I used to manage manually—creating windows, splitting panes,
navigating between them, setting working directories—now lives in Zellij's layout
files. Instead of scripting these operations in shell, I declare them in .kdl
configuration files that Zellij understands natively.
The Power of Native Layouts
Zellij's layout system is where the real improvement lies. Instead of shell scripts that set environment variables and execute tmux commands in sequence, I now write declarative layouts that describe the desired end state.
Here's my typical layout structure:
layout {
cwd "~/code/my-project"
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="Backend" focus=true split_direction="vertical" {
pane focus=true
pane split_direction="horizontal" {
pane
pane
}
}
tab name="Frontend" split_direction="vertical" {
pane
pane split_direction="horizontal" {
pane
pane
}
}
}
This creates two tabs. Each tab has a large vertical pane on the left and two
smaller horizontal panes stacked on the right. The left pane is where I typically
run AI assistants like Claude Code or work in nvim as I'm gradually adopting it
as my primary editor, having a full-height terminal makes it easier to follow long
conversations, code reviews, or see more of a file at once. The top-right pane
runs the development server, and the bottom-right is for ad-hoc commands: git
operations, running tests, or installing dependencies.
The layout is consistent and predictable, but now it's also more flexible than
what I had with tmux. If a project needs a different arrangement, I just modify
the .kdl file. No need to debug shell scripting logic or figure out the right
sequence of tmux commands.
Real-World Example
Here's the actual layout I use for this site:
layout {
cwd "~/Developer/devtimestories"
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="Homepage" cwd="homepage" focus=true split_direction="vertical" {
pane focus=true
pane split_direction="horizontal" {
pane
pane
}
}
tab name="DTS" split_direction="vertical" {
pane
pane
}
}
The "Homepage" tab is where I spend most of my time. The left pane is for editing
in nvim or running Claude Code, while the right side has npm run dev in the
top pane and a general-purpose shell in the bottom. The "DTS" tab is simpler—just
two vertical panes for any root-level operations or documentation work.
Navigation and Workflow
One of Zellij's strengths is its navigation model. I use Option + Arrow Keys to move between panes in any direction. If I press Option + Right at the rightmost pane, it cycles to the next tab. Press it again at the last tab, and it wraps back to the first. This makes navigation feel seamless—I don't think about "switching tabs" versus "switching panes." I just move in the direction I want to go.
There are probably more efficient keybindings buried in Zellij's configuration, but this setup is intuitive enough that I don't have to remember complex shortcuts. The cognitive overhead is minimal, which is exactly what I want from a tool like this.
Trade-offs and Considerations
Switching to Zellij wasn't without trade-offs. The biggest one is maturity: tmux has been around for over a decade and is installed on virtually every server I've ever SSH'd into. Zellij is newer, and while it's stable for daily use, it doesn't have the same ubiquity. If I need to work on a remote machine, I still fall back to tmux.
There's also the learning curve of .kdl syntax, though I found it straightforward.
If you're already comfortable with declarative configuration formats like YAML or
TOML, KDL will feel familiar.
Finally, plugin support and ecosystem tooling are still evolving. Tmux has a massive collection of plugins built over years. Zellij's plugin system is newer, though the core functionality has been solid enough that I haven't felt the need for third-party extensions.
Why It Worked for Me
What made this transition successful was that I didn't try to rewrite everything
at once. I kept the same command (dev my-project), the same directory structure
for configurations, and the same mental model of "one command to load an environment."
I just swapped out the underlying implementation.
The result is that my workflow feels the same, but the maintenance burden dropped
significantly. When I need to adjust a layout, I edit a declarative file instead
of debugging shell script logic. When I add a new project, I copy an existing
.kdl layout and tweak a few paths. It's faster, clearer, and less error-prone.
Conclusion
I'm not advocating that everyone abandon tmux for Zellij. Tmux is a proven tool that will continue to work reliably for years. But if you've built custom scripting around tmux and find yourself fighting with layout management or window positioning, it's worth taking a look at Zellij's native layout system.
For me, the transition was smooth precisely because I had already thought through what I wanted from a development environment manager. The tmux script taught me what mattered: fast context switching, predictable layouts, and minimal cognitive overhead. Zellij just happened to offer a better way to implement those requirements.