Configuring tmux with Lua using tpane
Recently I have been configuring tmux around my AI workflows. For example, I wanted Super+a to toggle a sidebar pane with the pi CLI, taking 35% of the current window. I also wanted Super+g to expand that pane to full screen, then press it again to go back to 35%.
I managed to do it with tmux.conf and a few shell scripts, but it was not great. Every time I wanted to change the behavior, I had to touch more shell code and the config kept getting harder to read.
One thing I always liked about Neovim is how it handles configuration and plugins with Lua. It is easy to read, easy to extend and easy to split into small files.
With that in mind, I built tpane: a small tool that lets me configure tmux with Lua. It still uses tmux under the hood, it just gives the configuration a nicer layer with bindings, plugins, widgets and reusable panes.
Quick Start
Install the latest release:
curl -fsSL https://raw.githubusercontent.com/phcurado/tpane/main/install.sh | sh
Then add tpane at the end of your ~/.config/tmux/tmux.conf:
run-shell -b 'tpane'
Now create ~/.config/tmux/tpane/init.lua:
tpane.use("sensible")
tpane.use("themes")
tpane.theme("Catppuccin Mocha")
tpane.opt.mouse = true
tpane.opt.mode_keys = "vi"
tpane.bind("h", tpane.pane.select("left"))
tpane.bind("j", tpane.pane.select("down"))
tpane.bind("k", tpane.pane.select("up"))
tpane.bind("l", tpane.pane.select("right"))
local battery = tpane.widgets.battery({ every = "30s" })
tpane.statusline {
position = "top",
left = { tpane.widgets.session, tpane.widgets.tabs },
right = { battery, tpane.widgets.clock, tpane.widgets.date, tpane.widgets.prefix },
}
Reload tmux.
That is enough to start using Lua for your tmux config.
Status Bar Widgets
One of the main reasons I wanted this was the status bar. In tpane, the status bar is built from widgets:
tpane.statusline {
left = { tpane.widgets.session, tpane.widgets.tabs },
right = { tpane.widgets.clock },
}
Some widgets run background jobs, like CPU, memory, battery or media player status:
local cpu = tpane.widgets.cpu({ every = "2s" })
local memory = tpane.widgets.memory({ every = "5s" })
local battery = tpane.widgets.battery({ every = "30s" })
tpane.statusline {
right = { cpu, memory, battery, tpane.widgets.clock },
}
You can also write your own widgets using Lua functions:
local cwd = tpane.widget(function(ctx)
return ctx.pane and ctx.pane.cwd_basename or ""
end)
Want to track the Bitcoin price in your tmux statusline? You can use a background job:
local btc = tpane.job({
every = "5m",
timeout = "5s",
cmd = [[curl -fsSL 'https://api.coingecko.com/api/v3/simple/price?ids=bitcoin&vs_currencies=usd' | sed -n 's/.*"usd":\([0-9.]*\).*/₿ $\1/p']],
})
tpane.statusline {
right = { btc },
}
Plugins and Themes
tpane ships with a few built-in plugins:
tpane.use("sensible")
tpane.use("vim-navigator")
tpane.use("yank")
tpane.use("themes")
The themes plugin includes the iTerm2 color schemes collection:
tpane.use("themes")
tpane.theme("Gruvbox Dark", { transparent = true })
You can list available themes with:
tpane themes
Git plugins are also supported, so external configuration can be shared and installed from a repository:
tpane.use("my-plugin", {
repo = "https://github.com/example/tpane-my-plugin.git",
branch = "main",
})
Reusable Panes
Another feature I use a lot is reusable panes. You can register a pane once and toggle it later. The process keeps running when the pane is hidden.
Here is the simple version for logs:
tpane.register_pane("logs", {
side = "bottom",
size = "25%",
command = "tail -f logs/app.log",
})
tpane.bind("L", function(pane)
tpane.toggle(pane, "logs")
end)
The same idea works for an AI sidebar. This is close to the setup I wanted in the first place:
tpane.register_pane("pi", {
tag = "agent",
command = "pi",
side = "right",
size = "35%",
full = true,
title = "pi",
label = "pi",
})
tpane.bind("a", function(pane)
tpane.toggle(pane, "pi")
end)
tpane.bind("C-g", function(pane)
tpane.expand(pane)
end, { prefix = false })
Pretty simple, but still flexible enough to adapt to your workflow. If you use the Claude or Codex CLIs, you can just change the command value and get a similar setup.
Wrapping Up
This is still a small tool, but it already replaced most of my tmux.conf. If you like tmux but wish the config felt closer to Neovim, it is worth checking out.