---
title: "WezTerm Mouse Bindings"
aliases: [wezterm-mouse, wezterm-mouse-config]
tags: [wezterm, mouse, terminal, dotfiles, lua]
sources: [raw/Mouse Binding - Wez's Terminal Emulator.md]
created: 2026-04-17
updated: 2026-04-17
---

## Overview

WezTerm mouse bindings are fully configurable via Lua. By default, terminal applications can capture mouse events — WezTerm hands them through unless you intervene with a modifier key.

## Mouse Reporting Bypass

When an app enables mouse tracking, WezTerm passes all mouse events to it rather than matching bindings. To force your bindings to fire anyway, hold `SHIFT` (default bypass modifier).

- Change the bypass key via `config.bypass_mouse_reporting_modifiers`
- Use `mouse_reporting = true` in a binding entry to target app-captured state (generally avoid this)

## Default Mouse Assignments

| Event | Modifiers | Action |
|-------|-----------|--------|
| Single Left Down | `NONE` | `SelectTextAtMouseCursor("Cell")` |
| Double Left Down | `NONE` | `SelectTextAtMouseCursor("Word")` |
| Triple Left Down | `NONE` | `SelectTextAtMouseCursor("Line")` |
| Single Left Down | `SHIFT` | `ExtendSelectionToMouseCursor("Cell")` |
| Single Left Down | `ALT` | `SelectTextAtMouseCursor("Block")` |
| Single Left Up | `NONE` | `CompleteSelectionOrOpenLinkAtMouseCursor(...)` |
| Single Left Up | `SHIFT` | `CompleteSelectionOrOpenLinkAtMouseCursor(...)` |
| Single Left Drag | `NONE` | `ExtendSelectionToMouseCursor("Cell")` |
| Single Left Drag | `ALT` | `ExtendSelectionToMouseCursor("Block")` |
| Double Left Drag | `NONE` | `ExtendSelectionToMouseCursor("Word")` |
| Triple Left Drag | `NONE` | `ExtendSelectionToMouseCursor("Line")` |
| Single Middle Down | `NONE` | `PasteFrom("PrimarySelection")` |
| Single Left Drag | `SUPER` / `CTRL+SHIFT` | `StartWindowDrag` |

Run `wezterm show-keys` to inspect the effective bindings at runtime.

Disable all defaults with:
```lua
config.disable_default_mouse_bindings = true
```

## Configuring Custom Mouse Bindings

```lua
local wezterm = require 'wezterm'
local act = wezterm.action

config.mouse_bindings = {
  -- Right click sends a string
  {
    event = { Down = { streak = 1, button = 'Right' } },
    mods = 'NONE',
    action = act.SendString 'woot',
  },
  -- CTRL-Click opens hyperlinks
  {
    event = { Up = { streak = 1, button = 'Left' } },
    mods = 'CTRL',
    action = act.OpenLinkAtMouseCursor,
  },
  -- Disable Down event to prevent leaking to app (see gotcha below)
  {
    event = { Down = { streak = 1, button = 'Left' } },
    mods = 'CTRL',
    action = act.Nop,
  },
}
```

### Binding Entry Fields

| Field | Required | Description |
|-------|----------|-------------|
| `event` | yes | `Down`, `Up`, or `Drag` + `streak` + `button` |
| `mods` | yes | Modifier keys (same syntax as key bindings) |
| `action` | yes | `KeyAssignment` action to perform |
| `mouse_reporting` | no | Match only when app mouse reporting is active (default `false`) |
| `alt_screen` | no | `'Any'` (default), `true`, or `false` — match only in/out of alt screen |

### Event Lua Representation

```lua
-- Triple Left Down
event = { Down = { streak = 3, button = "Left" } }

-- Double Left Up
event = { Up = { streak = 2, button = "Left" } }

-- Single Left Drag
event = { Drag = { streak = 1, button = "Left" } }
```

Arbitrary click streaks are supported — `streak = 4` for quadruple-click, etc.

## Mouse Wheel Bindings

Wheel events use `WheelUp` / `WheelDown` as the button value:

```lua
-- CTRL+Scroll to resize font
{
  event = { Down = { streak = 1, button = { WheelUp = 1 } } },
  mods = 'CTRL',
  action = act.IncreaseFontSize,
},
{
  event = { Down = { streak = 1, button = { WheelDown = 1 } } },
  mods = 'CTRL',
  action = act.DecreaseFontSize,
},
```

`streak` and `amount` are fixed at `1` for wheel events in pattern matching. Use `window:current_event` inside a callback to get the actual delta.

Note: default scroll bindings use `alt_screen = false` — wheel maps to arrow keys in alt screen mode instead.

## Gotcha: Binding Only the 'Up' Event

If you bind `Up` but not `Down`, the `Down` event is still forwarded to the running app (e.g. tmux, vim). This causes the app to see a Down without a matching Up — leading to stuck selection state or other glitches.

**Fix:** always pair an `Up` binding with a `Down = act.Nop` to suppress the Down event:

```lua
-- Good: suppress Down so app never sees it
{ event = { Up   = { streak = 1, button = 'Left' } }, mods = 'CTRL', action = act.OpenLinkAtMouseCursor },
{ event = { Down = { streak = 1, button = 'Left' } }, mods = 'CTRL', action = act.Nop },
```

## Key Takeaways

- Default bindings cover selection (cell/word/line/block), clipboard, link-open, and window drag
- Apps can capture mouse events; hold `SHIFT` (or configured bypass mod) to force your bindings
- Custom bindings go in `config.mouse_bindings` as a Lua table
- Every entry needs `event`, `mods`, and `action`; `mouse_reporting` and `alt_screen` are optional filters
- Wheel events use `{ WheelUp = 1 }` / `{ WheelDown = 1 }` as the button value
- Always pair an `Up` binding with a `Down = act.Nop` to avoid leaking events to apps
- `wezterm show-keys` shows the live effective binding table

## Related

- [[wiki/dotfiles/wezterm-key-bindings|WezTerm Key Bindings]] — keyboard equivalent, same `mods` syntax
- [[wiki/dotfiles/wezterm-key-tables|WezTerm Key Tables]] — modal layers that also affect input routing
- [[wiki/dotfiles/wezterm-keyboard-concepts|WezTerm Keyboard Concepts]] — how WezTerm handles input at the OS level
- [[wiki/dotfiles/wezterm-config|WezTerm Config]] — config file structure and live reload

## Sources

- [wezterm.org/config/mouse.html](https://wezterm.org/config/mouse.html)
