WezTerm: A GPU-Accelerated Terminal Configured in Lua
WezTerm is a terminal emulator that takes the approach that your terminal should be as configurable as your text editor — using code, not a settings panel. Configuration is written in Lua, every behavior is programmable, and the multiplexer (tabs, panes, sessions) is built in rather than bolted on with tmux.
Photo by Daniil Komov on Unsplash
It's GPU-accelerated via wgpu, cross-platform (Linux, macOS, Windows), and handles ligatures and color profiles correctly without plugins.
Why Developers Switch to WezTerm
- Lua configuration: Full programmability, not just option flags
- Built-in multiplexer: Tabs, splits, and panes without tmux
- GPU rendering: Fast and smooth, especially with large scrollback
- Correct ligature support: Works out of the box with Fira Code, JetBrains Mono
- Multiplexer protocol: Persistent sessions over SSH
- Wide color support: True color, Sixel graphics, and image rendering
- Cross-platform: Same config file works on Linux, macOS, and Windows
Installation
# macOS (Homebrew)
brew install --cask wezterm
# Linux (AppImage)
curl -LO https://github.com/wez/wezterm/releases/latest/download/WezTerm-ubuntu22.04.AppImage
chmod +x WezTerm-ubuntu22.04.AppImage
sudo mv WezTerm-ubuntu22.04.AppImage /usr/local/bin/wezterm
# Linux (Debian/Ubuntu package)
curl -fsSL https://apt.fury.io/wez/gpg.key | sudo apt-key add -
echo "deb https://apt.fury.io/wez/ * *" | sudo tee /etc/apt/sources.list.d/wezterm.list
sudo apt update && sudo apt install wezterm
# Arch Linux
yay -S wezterm
Basic Configuration
Create ~/.config/wezterm/wezterm.lua:
local wezterm = require 'wezterm'
local config = wezterm.config_builder()
-- Font
config.font = wezterm.font('JetBrains Mono', { weight = 'Regular' })
config.font_size = 14.0
-- Color scheme
config.color_scheme = 'Tokyo Night'
-- Window appearance
config.window_background_opacity = 0.95
config.macos_window_background_blur = 20
config.window_padding = {
left = 12, right = 12,
top = 8, bottom = 8,
}
-- Tab bar
config.enable_tab_bar = true
config.tab_bar_at_bottom = true
config.hide_tab_bar_if_only_one_tab = true
-- Scrollback
config.scrollback_lines = 10000
return config
Like what you're reading? Subscribe to DevTools Guide — free weekly guides in your inbox.
Tabs and Panes
WezTerm's pane system is more flexible than tmux's because it's configured in Lua:
-- Default keybindings (or override them)
-- Ctrl+Shift+T = new tab
-- Ctrl+Shift+W = close tab
-- Ctrl+Shift+% = vertical split
-- Ctrl+Shift+" = horizontal split
-- Ctrl+Shift+Z = zoom pane
-- Ctrl+Shift+←↑→↓ = move between panes
-- Custom keybindings
config.keys = {
-- New tab with current directory
{
key = 't',
mods = 'CTRL|SHIFT',
action = wezterm.action.SpawnCommandInNewTab {
cwd = wezterm.home_dir,
},
},
-- Split horizontally
{
key = 'd',
mods = 'CTRL|SHIFT',
action = wezterm.action.SplitHorizontal { domain = 'CurrentPaneDomain' },
},
-- Navigate panes with vim keys
{
key = 'h', mods = 'CTRL|SHIFT',
action = wezterm.action.ActivatePaneDirection 'Left',
},
{
key = 'l', mods = 'CTRL|SHIFT',
action = wezterm.action.ActivatePaneDirection 'Right',
},
}
Leader Key (tmux-style)
If you use tmux bindings, WezTerm supports a leader key:
config.leader = { key = 'a', mods = 'CTRL', timeout_milliseconds = 1000 }
config.keys = {
-- Leader + % = vertical split
{
key = '%',
mods = 'LEADER',
action = wezterm.action.SplitHorizontal { domain = 'CurrentPaneDomain' },
},
-- Leader + " = horizontal split
{
key = '"',
mods = 'LEADER',
action = wezterm.action.SplitVertical { domain = 'CurrentPaneDomain' },
},
-- Leader + z = zoom
{
key = 'z',
mods = 'LEADER',
action = wezterm.action.TogglePaneZoomState,
},
-- Leader + c = new tab
{
key = 'c',
mods = 'LEADER',
action = wezterm.action.SpawnTab 'CurrentPaneDomain',
},
}
Status Bar with Dynamic Content
The status bar is programmatic — you can show git branch, clock, system info:
wezterm.on('update-status', function(window, pane)
-- Git branch
local cwd = pane:get_current_working_dir()
local branch = ''
if cwd then
local cmd = 'git -C ' .. cwd.file_path .. ' branch --show-current 2>/dev/null'
local handle = io.popen(cmd)
if handle then
branch = handle:read('*a'):gsub('%s+', '')
handle:close()
end
end
-- Time
local time = wezterm.strftime('%H:%M')
window:set_right_status(wezterm.format({
{ Foreground = { Color = '#a9b1d6' } },
{ Text = ' ' .. (branch ~= '' and ' ' .. branch or '') .. ' ' .. time .. ' ' },
}))
end)
Multiplexer: Persistent Remote Sessions
WezTerm has a built-in multiplexer protocol that works over SSH, keeping sessions alive:
config.unix_domains = {
{ name = 'local' },
}
config.ssh_domains = {
{
name = 'homelab',
remote_address = '192.168.1.10',
username = 'hailey',
multiplexing = 'WezTerm',
},
}
Connect to a persistent session:
wezterm connect homelab
This is different from tmux — WezTerm syncs the window layout and renders remotely, so your tabs and splits persist even if the local connection drops.
Font and Ligature Setup
config.font = wezterm.font_with_fallback({
'JetBrains Mono',
'Nerd Font Symbols',
'Noto Color Emoji',
})
-- Enable ligatures
config.harfbuzz_features = { 'calt=1', 'clig=1', 'liga=1' }
Popular fonts that work well: JetBrains Mono, Fira Code, Cascadia Code, Maple Mono.
Colorschemes
WezTerm includes 800+ built-in colorschemes:
-- List all schemes
wezterm list-fonts --list-system
-- (run in terminal)
-- Or browse at: wezfurlong.org/wezterm/colorschemes/
config.color_scheme = 'Catppuccin Mocha' -- or 'Tokyo Night', 'Gruvbox Dark', etc.
Comparison to Alacritty and Kitty
| Feature | WezTerm | Alacritty | Kitty |
|---|---|---|---|
| Config language | Lua (code) | TOML | Python (code) |
| Built-in multiplexer | ✓ | ✗ | ✓ |
| GPU acceleration | ✓ | ✓ | ✓ |
| Cross-platform | Linux/macOS/Windows | Linux/macOS/Windows | Linux/macOS |
| Image protocol | Sixel + iTerm2 | ✗ | Kitty protocol |
| Ligatures | ✓ | Experimental | ✓ |
Alacritty is the simplest and most minimal. Kitty is the most feature-complete on Linux/macOS. WezTerm is the best cross-platform option with the most programmable configuration.
