Home/Guides/tmux Split Panes

Guide

Open a Project in tmux Split Panes Automatically

Every time you start working on a project, you go through the same ritual: split the terminal, navigate to the project in each pane, start the dev server in one, open the editor in another, maybe run tests in a third. Here is how to automate that entire sequence.

The Manual tmux Pane Setup (What We Are Replacing)

If you do this by hand in tmux, the sequence looks something like this:

# Create a new session
$ tmux new-session -s api-gateway -c ~/dev/mycompany/api-gateway
 
# Split horizontally
Ctrl+B %
 
# In the right pane, split vertically
Ctrl+B "
 
# Go to left pane, start editor
Ctrl+B {left}
$ nvim .
 
# Go to top-right pane, start server
Ctrl+B {right}
$ go run .
 
# Go to bottom-right pane, run tests
Ctrl+B {down}
$ go test ./... -v

That is about 12 keystrokes and 30 seconds, assuming you remember the exact sequence. Do this 5 times a day across different projects and you are spending 2-3 minutes daily on pane choreography. Now let us automate it.

Using tmuxinator (Config-Heavy Approach)

tmuxinator is the most popular tmux workspace manager. It uses YAML config files to define session layouts:

# ~/.config/tmuxinator/api-gateway.yml
name: api-gateway
root: ~/dev/mycompany/api-gateway
 
windows:
- editor:
layout: main-vertical
panes:
- nvim .
- go run .
- go test ./... -v
# Launch the workspace
$ tmuxinator start api-gateway

tmuxinator is solid and battle-tested. The downsides:

  • !You need to create a YAML config file for every project. With 30 repos, that is 30 config files to maintain.
  • !It requires Ruby as a runtime dependency.
  • !It only works with tmux. If you also use iTerm2 or WezTerm, you need separate solutions.
  • !It does not discover projects. You still need to remember the config name and type it out.

Using tmuxp

tmuxp is similar to tmuxinator but written in Python and supports both YAML and JSON configs:

# .tmuxp.yaml in project root
session_name: api-gateway
start_directory: ./
windows:
- window_name: dev
layout: main-vertical
panes:
- shell_command: nvim .
- shell_command: go run .
- shell_command: go test ./... -v

tmuxp has the advantage of placing config files in the project root, which keeps things co-located with the code. However, it shares the same fundamental limitations: tmux-only, manual config creation, Python dependency, no project discovery.

Using son (Zero-Config Approach)

son takes a different approach. Instead of requiring a config file for every project, it provides sensible defaults and only needs configuration when you want something specific. Most projects work with zero config:

# No config needed for most projects
$ son
? Select project: api-gateway
Opening api-gateway in tmux with split layout...
 
# son detects your terminal automatically:
tmux detected → uses tmux split-window
iTerm2 detected → uses AppleScript split panes
WezTerm detected → uses WezTerm CLI

son discovers your projects, presents them through fzf, and opens the workspace in whichever terminal you are using. No YAML, no Ruby, no Python.

Layout Presets

son includes built-in layout presets you can use without writing any config:

single

shell

One pane. Good for quick edits.

split

editor
shell

Two panes side by side. The default.

3-pane

editor
server
tests

Editor left, server and tests right.

grid

1
2
3
4

Four equal panes. For microservices.

Startup Hooks (Auto-Run Commands)

The real power of automated workspaces is running commands automatically when panes open. Instead of navigating to a pane and typing npm run dev, the command runs for you.

In tmuxinator and tmuxp, you define commands in the config file. In son, you use the .son.toml file in the project root. Here is a real example:

# ~/dev/mycompany/api-gateway/.son.toml
layout = "3-pane"
editor = "nvim"
 
# Hook that runs before panes open
[hooks]
before = "source .env.local"
 
# Pane definitions
[[panes]]
name = "server"
cmd = "go run ."
 
[[panes]]
name = "tests"
cmd = "go test ./... -v -count=1"

Here is a more complex example for a fullstack monorepo:

# ~/dev/mycompany/fullstack-app/.son.toml
layout = "grid"
editor = "code"
 
[hooks]
before = "nvm use 20"
 
[[panes]]
name = "frontend"
cmd = "cd frontend && npm run dev"
 
[[panes]]
name = "backend"
cmd = "cd backend && npm run dev"
 
[[panes]]
name = "db"
cmd = "docker compose up postgres redis"
 
[[panes]]
name = "shell"
cmd = ""

This opens a 4-pane grid layout. The frontend dev server starts in one pane, the backend API in another, Docker databases in the third, and a clean shell in the fourth. All of that from typing son and selecting the project.

Comparison: tmuxinator vs tmuxp vs son

Featuretmuxinatortmuxpson
Config required per projectYes (YAML)Yes (YAML/JSON)No (optional)
Project discoveryNoNoYes
Fuzzy searchNoNoYes (fzf)
Frecency sortingNoNoYes
Multi-terminal supporttmux onlytmux onlytmux, iTerm2, WezTerm
Runtime dependencyRubyPythonNone (Go binary)
Layout presetsBuilt-in tmux layoutsBuilt-in tmux layoutssingle, split, 3-pane, grid
Startup hooksYesYesYes

When to Use What

  • --Use tmuxinator or tmuxp if you exclusively use tmux, you have a small number of projects with complex window layouts (multiple windows, not just panes), and you do not mind maintaining YAML configs.
  • --Use son if you want zero-config defaults, project discovery, frecency sorting, and support for multiple terminal applications. It is especially useful when you have many projects and want the workspace setup to be automatic rather than manually configured.
  • --Combine them if you want to. son handles project discovery and selection; tmuxinator handles complex multi-window layouts. They are not mutually exclusive.

Stop splitting panes by hand.

Install son and let your workspace assemble itself.

$ brew install abdussamet032/tap/son