---
title: "WezTerm Key Tables (Modal Keybindings)"
aliases: [wezterm-modal-keys, key-table-activation, wezterm-leader-modes]
tags: [wezterm, keybindings, lua, terminal, modal]
sources: [raw/Key Tables - Wez's Terminal Emulator.md]
created: 2026-04-17
updated: 2026-04-17
---

## Overview

Key tables let you define **named sets of keybindings** that activate on demand — enabling modal keybinding workflows similar to Vim modes. Available since WezTerm `20220408-101518-b908e2dd`.

Defined via the `key_tables` config option; triggered via the `ActivateKeyTable` action.

---

## How It Works

1. Define named tables in `config.key_tables = { ... }`
2. Bind a trigger key (often prefixed with `LEADER`) to `act.ActivateKeyTable { name = '...' }`
3. WezTerm pushes that table onto its **activation stack** — keys in that table take effect
4. Exit the mode via `PopKeyTable`, timeout, or `one_shot = true`

---

## Activation Stack

Each WezTerm window maintains a **stack** of active key tables:

| Action | Effect |
|--------|--------|
| `ActivateKeyTable` | Push a table onto the stack |
| `PopKeyTable` | Pop the top entry |
| `ClearKeyTableStack` | Clear entire stack |
| Config reload | Stack is also cleared automatically |

Key resolution walks **top → bottom** of the stack until a match is found (layering behavior added in `20220624-141144-bd1b7c5d`).

---

## `ActivateKeyTable` Options

| Field | Type | Description |
|-------|------|-------------|
| `name` | string | Name of the key table to activate |
| `one_shot` | bool | If `true`, table pops after one key press (default: `true`) |
| `timeout_milliseconds` | int | Auto-pop after N ms of inactivity |
| `replace_current` | bool | Implicitly pop current entry before pushing |

---

## Example: Pane Resize & Activation Modes

```lua
local wezterm = require 'wezterm'
local act = wezterm.action
local config = {}

-- Show active key table in status bar
wezterm.on('update-right-status', function(window, pane)
  local name = window:active_key_table()
  if name then name = 'TABLE: ' .. name end
  window:set_right_status(name or '')
end)

config.leader = { key = 'Space', mods = 'CTRL|SHIFT' }
config.keys = {
  -- LEADER + r → resize mode (persistent until Escape)
  { key = 'r', mods = 'LEADER',
    action = act.ActivateKeyTable { name = 'resize_pane', one_shot = false } },

  -- LEADER + a → activate-pane mode (auto-exits after 1 second)
  { key = 'a', mods = 'LEADER',
    action = act.ActivateKeyTable { name = 'activate_pane', timeout_milliseconds = 1000 } },
}

config.key_tables = {
  resize_pane = {
    { key = 'LeftArrow',  action = act.AdjustPaneSize { 'Left', 1 } },
    { key = 'h',          action = act.AdjustPaneSize { 'Left', 1 } },
    { key = 'RightArrow', action = act.AdjustPaneSize { 'Right', 1 } },
    { key = 'l',          action = act.AdjustPaneSize { 'Right', 1 } },
    { key = 'UpArrow',    action = act.AdjustPaneSize { 'Up', 1 } },
    { key = 'k',          action = act.AdjustPaneSize { 'Up', 1 } },
    { key = 'DownArrow',  action = act.AdjustPaneSize { 'Down', 1 } },
    { key = 'j',          action = act.AdjustPaneSize { 'Down', 1 } },
    { key = 'Escape',     action = 'PopKeyTable' },
  },
  activate_pane = {
    { key = 'LeftArrow',  action = act.ActivatePaneDirection 'Left' },
    { key = 'h',          action = act.ActivatePaneDirection 'Left' },
    { key = 'RightArrow', action = act.ActivatePaneDirection 'Right' },
    { key = 'l',          action = act.ActivatePaneDirection 'Right' },
    { key = 'UpArrow',    action = act.ActivatePaneDirection 'Up' },
    { key = 'k',          action = act.ActivatePaneDirection 'Up' },
    { key = 'DownArrow',  action = act.ActivatePaneDirection 'Down' },
    { key = 'j',          action = act.ActivatePaneDirection 'Down' },
  },
}
return config
```

---

## Showing Active Mode in Status Bar

```lua
wezterm.on('update-right-status', function(window, pane)
  local name = window:active_key_table()
  window:set_right_status(name and ('TABLE: ' .. name) or '')
end)
```

Useful for knowing which modal layer is active.

---

## Key Takeaways

- Key tables = modal keybinding layers, like Vim modes in a terminal
- `one_shot = false` keeps the mode active until `Escape` / `PopKeyTable`
- `timeout_milliseconds` auto-exits modes after inactivity — good for "quick prefix" modes
- Stack layering (v`20220624+`) means tables compose rather than replace
- Config reload clears the stack — useful escape hatch when you get stuck
- Always show active table in status bar (`window:active_key_table()`) while developing
- Pair with `config.leader` for a two-key prefix workflow without holding many modifiers

---

## Related

- [[wiki/dotfiles/wezterm-key-bindings|WezTerm Key Bindings]] — base key config, Leader key, modifiers
- [[wiki/dotfiles/wezterm-default-keybindings|WezTerm Default Keybindings]] — full default key table reference
- [[wiki/dotfiles/wezterm-config|WezTerm Config]] — config file structure and live reload

---

## Sources

- `raw/Key Tables - Wez's Terminal Emulator.md` (clipped from wezterm.org/config/key-tables.html)
