Skip to main content
WezTerm provides a powerful event system that allows you to customize behavior and respond to various lifecycle events in the terminal, GUI, and multiplexer. Events are registered using the wezterm.on() function and can be used to extend WezTerm’s functionality.

Event System Architecture

WezTerm’s event system consists of three main categories:
  1. GUI Events - Emitted by the GUI layer during application lifecycle
  2. Window Events - Emitted by window and pane operations
  3. Multiplexer (Mux) Events - Emitted by the multiplexer layer

Registering Event Handlers

Event handlers are registered using the wezterm.on() function:
local wezterm = require 'wezterm'

wezterm.on('event-name', function(param1, param2, ...)
  -- Your event handler code
  -- Optionally return a value if the event expects one
end)

return {}

Event Handler Parameters

Each event receives different parameters depending on its purpose. Common parameter types include:
window
Window object
Represents the GUI window. Provides methods to manipulate window state, set status bars, and perform actions.
pane
Pane object
Represents a terminal pane. Provides access to pane content, user variables, and metadata.
tab
TabInformation
Contains information about a tab including its title, active pane, and state.
config
table
The effective configuration for the window or event context.

Event Types

Synchronous vs Asynchronous Events

Synchronous Events execute on the main thread and must return quickly:
  • format-tab-title - Must return immediately to avoid blocking the UI
  • mux-is-process-stateful - Quick decision on process state
  • augment-command-palette - Returns additional palette entries
These events cannot call asynchronous functions like wezterm.run_child_process(). Asynchronous Events can perform longer-running operations:
  • gui-startup - Can spawn multiple windows and configure workspaces
  • update-status - Can fetch remote data or perform computations
  • window-resized - Can adjust configuration based on dimensions

Fire-and-Forget Events

Some events are informational and don’t expect a return value:
  • bell - Notifies that a bell was rung
  • window-focus-changed - Notifies of focus state changes
  • window-config-reloaded - Notifies of configuration reload

Intercepting Events

Some events allow you to override default behavior by returning a value:
  • open-uri - Return false to prevent default URI opening
  • new-tab-button-click - Return false to prevent default action

Example: Multi-Event Configuration

Here’s an example that uses multiple events together:
local wezterm = require 'wezterm'
local mux = wezterm.mux
local config = {}

-- Startup: Configure initial layout
wezterm.on('gui-startup', function(cmd)
  local tab, pane, window = mux.spawn_window(cmd or {})
  pane:split { size = 0.3 }
end)

-- Update status bar with current time
wezterm.on('update-status', function(window, pane)
  local date = wezterm.strftime '%Y-%m-%d %H:%M:%S'
  window:set_right_status(date)
end)

-- Log when window loses/gains focus
wezterm.on('window-focus-changed', function(window, pane)
  if window:is_focused() then
    wezterm.log_info('Window gained focus')
  else
    wezterm.log_info('Window lost focus')
  end
end)

-- Custom tab titles
wezterm.on('format-tab-title', function(tab, tabs, panes, config, hover, max_width)
  local title = tab.active_pane.title
  if tab.is_active then
    return {
      { Background = { Color = 'blue' } },
      { Text = ' ' .. title .. ' ' },
    }
  end
  return title
end)

return config

Event Best Practices

Performance Considerations

  1. Keep synchronous events fast - Don’t perform heavy computations or I/O
  2. Avoid loops in config-reloaded - Only call set_config_overrides() when values actually change
  3. Cache expensive operations - Store results when possible instead of recalculating

Error Handling

wezterm.on('update-status', function(window, pane)
  local success, result = pcall(function()
    -- Your potentially failing code
    return some_operation()
  end)
  
  if success then
    window:set_right_status(result)
  else
    wezterm.log_error('Status update failed: ' .. result)
  end
end)

Event Ordering

Some events fire in a specific order:
  1. mux-startup - First event when mux server starts
  2. gui-startup - When GUI starts (doesn’t fire for wezterm connect)
  3. gui-attached - After attaching to a domain
Understanding event order helps you structure your configuration correctly.

Common Patterns

Workspace-Specific Configuration

wezterm.on('update-status', function(window, pane)
  local workspace = window:active_workspace()
  local color = workspace == 'coding' and 'green' or 'blue'
  window:set_right_status(wezterm.format({
    { Background = { Color = color } },
    { Text = ' ' .. workspace .. ' ' },
  }))
end)

Dynamic Color Schemes

wezterm.on('window-focus-changed', function(window, pane)
  local overrides = window:get_config_overrides() or {}
  if window:is_focused() then
    overrides.color_scheme = 'Tokyo Night'
  else
    overrides.color_scheme = 'Tokyo Night (Dim)'
  end
  window:set_config_overrides(overrides)
end)

Custom URI Handlers

wezterm.on('open-uri', function(window, pane, uri)
  if uri:match('^jira:') then
    local issue = uri:sub(6)
    wezterm.open_with(string.format('https://jira.company.com/browse/%s', issue))
    return false -- Prevent default handling
  end
  -- Allow default handling for other URIs
end)

See Also

Build docs developers (and LLMs) love