Back to musings
Coding

Streamline Your Development Workflow with TMUX

5 min read

If you're an experienced coder, you've probably been there: juggling multiple terminal windows running your type checker, frontend server, and multiple local backend services. Keeping track of multiple terminals is tedious, and switching between them interrupts your flow. This pattern is particularly prevalent in the beginning stages of a project while you're still working locally.

Terminal Juggling ๐Ÿคนโ€โ™‚๏ธ

Most modern web development requires running multiple processes simultaneously, such as:

  • TypeScript watcher: tsc --noEmit --watch
  • Development server: vite dev or npm run dev
  • Backend services: npx convex dev, npm run start, etc.

Managing these in separate terminals means constant window switching, and if you close the wrong window, you have to restart everything.

The Solution: TMUX + package.json Scripts

TMUX is a terminal multiplexer that lets you manage multiple terminal sessions within a single window. By combining TMUX with some clever package.json scripts, you can create a development environment that spins up or down with a single command.

Here's the setup:

{
	"scripts": {
		"dev:all": "SESSION=\"${1:-my-project}\"; tmux kill-session -t $SESSION 2>/dev/null ; tmux new-session -s $SESSION 'npm run typecheck:watch' \\; split-window -h 'npm run backend' \\; split-window -v 'npm run dev' \\; select-pane -t 0",
		"dev:kill": "SESSION=\"${1:-my-project}\"; tmux kill-session -t $SESSION 2>/dev/null || echo 'No $SESSION session found'"
	}
}

How It Works

Let's break down the dev:all script:

SESSION=\"${1:-my-project}\"  # Use first argument or default to "my-project"
tmux kill-session -t $SESSION 2>/dev/null  # Clean up any existing session
tmux new-session -s $SESSION 'npm run typecheck:watch'  # Create session with first process
\; split-window -h 'npm run backend'  # Split horizontally for second process
\; split-window -v 'npm run dev'  # Split vertically for third process
\; select-pane -t 0  # Select the first pane

The dev:kill script simply terminates the session when you're done.

Setup Instructions

  1. Install TMUX (if not already installed):

    # macOS
    brew install tmux
    
    # Ubuntu/Debian
    sudo apt-get install tmux
    
    # Windows (WSL)
    sudo apt-get install tmux
    
  2. Add the scripts to your package.json:

    {
    	"scripts": {
    		"dev:all": "SESSION=\"${1:-my-project}\"; tmux kill-session -t $SESSION 2>/dev/null ; tmux new-session -s $SESSION 'npm run typecheck:watch' \\; split-window -h 'npm run backend' \\; split-window -v 'npm run dev' \\; select-pane -t 0",
    		"dev:kill": "SESSION=\"${1:-my-project}\"; tmux kill-session -t $SESSION 2>/dev/null || echo 'No $SESSION session found'"
    	}
    }
    
  3. Customize the commands to match your project's needs:

    • Replace 'npm run typecheck:watch' with your TypeScript watcher
    • Replace 'npm run backend' with your backend command
    • Replace 'npm run dev' with your frontend dev server
    • Change my-project to your preferred default session name

Usage Examples

Default session name:

npm run dev:all    # Starts all processes in "my-project" session
npm run dev:kill   # Stops the "my-project" session

Custom session name:

npm run dev:all my-blog     # Starts session named "my-blog"
npm run dev:kill my-blog    # Stops the "my-blog" session

Essential TMUX Navigation

Once your session is running, use these shortcuts:

Navigation:

  • Ctrl+B then โ† โ†’ โ†‘ โ†“ - Move between panes
  • Ctrl+B then o - Cycle through panes

Session Management:

  • Ctrl+B then d - Detach (leave processes running)
  • tmux attach -t session-name - Reattach to session
  • tmux list-sessions - See all active sessions

Pane Management:

  • Ctrl+B then x - Close current pane (with confirmation)
  • Ctrl+B then % - Split current pane vertically
  • Ctrl+B then " - Split current pane horizontally

Benefits of This Approach

  1. Single Command Startup: One command starts everything
  2. Persistent Sessions: Detach and reattach without stopping processes
  3. Project Isolation: Different projects can run simultaneously with unique session names
  4. Consistent Workflow: Same setup across all projects
  5. Easy Cleanup: One command stops all processes

Advanced Customizations

Different Layouts: Modify the split commands for different pane arrangements:

# Stacked layout (three horizontal panes)
tmux new-session -s $SESSION 'cmd1' \; split-window -v 'cmd2' \; split-window -v 'cmd3'

More Processes: Add more processes with additional splits:

tmux new-session -s $SESSION 'cmd1' \; split-window -h 'cmd2' \; split-window -v 'cmd3' \; split-window -v 'cmd4'

Environment Variables: Pass environment variables to your processes:

tmux new-session -s $SESSION 'NODE_ENV=development npm run dev'

Real-World Example

Here's how it looks for a typical React + Node.js project:

{
	"scripts": {
		"typecheck:watch": "tsc --noEmit --watch",
		"backend": "nodemon src/server.js",
		"dev": "vite dev --port 3000",
		"dev:all": "SESSION=\"${1:-my-app}\"; tmux kill-session -t $SESSION 2>/dev/null ; tmux new-session -s $SESSION 'npm run typecheck:watch' \\; split-window -h 'npm run backend' \\; split-window -v 'npm run dev' \\; select-pane -t 0",
		"dev:kill": "SESSION=\"${1:-my-app}\"; tmux kill-session -t $SESSION 2>/dev/null || echo 'No $SESSION session found'"
	}
}

Now you can start your entire development environment with npm run dev:all and switch between your type checker, backend server, and frontend dev server seamlessly.

In Conclusion...

This TMUX + package.json approach transforms a messy multi-terminal setup into a clean, manageable workflow. It's especially valuable when working on multiple projects or when you need to quickly start/stop your entire development environment. If you like opening and switching between multiple Ghostty tabs while you work, this workflow is not for you.

Give it a try in your next project - you'll wonder how you ever worked without it!

Share this post