`key-bindings-syntax.md` told you to run `wev` and read `sym: XXXX` — a placeholder nobody fills in, for a tool that is not on the machine: `command -v wev` is empty, `pacman -Ss --sync wev` is empty on CachyOS, and the AUR carries it as `wev-git`. The bind token is an XKB keysym, and the complete list is already on every box that links libxkbcommon, so the page now leads with the grep against `/usr/include/xkbcommon/xkbcommon-keysyms.h` (run here: `a A Escape Left Return Tab`), and keeps `wev` as the "press the key and see" option with its real package name and where to get it. `screenshots.md` line 35 read `(FIXME upstream).`, which scans as unfinished work in a page that is finished: the defect is upstream's, tracked in the two issue links six lines below. Reworded to say that. Refs #64
73 lines
2.5 KiB
Markdown
73 lines
2.5 KiB
Markdown
---
|
|
type: reference
|
|
title: Niri Key Bindings Syntax
|
|
description: Configuration syntax for niri key bindings — KDL format, modifiers, bind options, and finding key names with wev.
|
|
tags:
|
|
- niri
|
|
- keybindings
|
|
- kdl
|
|
- configuration
|
|
verified: 2026-08-28
|
|
---
|
|
|
|
# Key Bindings Syntax
|
|
|
|
Key bindings go in the `binds {}` section of the config file (`~/.config/niri/config.kdl`):
|
|
|
|
```kdl
|
|
binds {
|
|
Mod+T { spawn "alacritty"; }
|
|
Mod+Left { focus-column-left; }
|
|
}
|
|
```
|
|
|
|
## Modifiers
|
|
|
|
- `Ctrl` or `Control`
|
|
- `Shift`
|
|
- `Alt`
|
|
- `Super` or `Win`
|
|
- `ISO_Level3_Shift` or `Mod5` (AltGr)
|
|
- `ISO_Level5_Shift` (for lv5 options like `lv5:caps_switch`)
|
|
- `Mod` — Special: equals `Super` on TTY, equals `Alt` in nested window
|
|
|
|
## Bind Options
|
|
|
|
```kdl
|
|
binds {
|
|
Mod+T repeat=false { spawn "alacritty"; } // Do not repeat on hold
|
|
Mod+T cooldown-ms=500 { spawn "alacritty"; } // Rate-limit
|
|
Super+Alt+L allow-when-locked=true { spawn "swaylock"; } // Works when locked
|
|
Mod+Q hotkey-overlay-title="Close Window" { close-window; } // Custom title in overlay
|
|
Mod+Escape allow-inhibiting=false { ... } // Always works (even with inhibitors)
|
|
}
|
|
```
|
|
|
|
## Finding Key Names
|
|
|
|
Niri binds by XKB **keysym name**, and the authoritative list is already on the box — no
|
|
tool needed:
|
|
|
|
```bash
|
|
grep -oE 'XKB_KEY_[A-Za-z0-9_]+' /usr/include/xkbcommon/xkbcommon-keysyms.h \
|
|
| sed 's/^XKB_KEY_//' | grep -ixE 'a|return|left|tab|escape'
|
|
```
|
|
|
|
To read the keysym off a key you actually press, use
|
|
[`wev`](https://git.sr.ht/~sircmpwn/wev), the Wayland equivalent of `xev`. It is NOT in the
|
|
enabled repositories — checked 2026-10-21, `pacman -Ss --sync wev` returns nothing — and the
|
|
AUR builds it as `wev-git` (`yay -S wev-git`; `aur/wev-git 1.1.0.r1.gb09b664-1`).
|
|
|
|
Shifted keys require Shift + the unshifted key (for example, `Mod+Shift+Comma` for `<` on US QWERTY).
|
|
|
|
## Constraints
|
|
|
|
- One action per keybind. Each `{ ... }` block holds exactly one action; multiple actions like `{ move-window-up; consume-or-expel-window-up; }` are rejected at parse time.
|
|
- No duplicate keys. The same key combo (e.g. `Mod+CTRL+Left`) can only appear once in `binds {}`. Adding a new binding for an existing key causes a parse error.
|
|
- Valid consume/expel actions: `consume-or-expel-window-left`, `consume-or-expel-window-right`, and `expel-window-from-column`. The `consume-or-expel-window-up`/`-down` variants do NOT exist.
|
|
|
|
## Related
|
|
|
|
* [Default Key Bindings](/tools/niri/default-bindings.md) — What niri ships with out of the box
|
|
* [Action Reference](/tools/niri/action-reference.md) — All available actions
|