New to Claude Skills? Learn how to install them →

How to Customize Keyboard Shortcuts in Claude Code

Claude Code reads keyboard shortcuts from ~/.claude/keybindings.json, with contexts, chords, and unbinding, plus a separate keybindingFlavor setting for readline-style word editing.

August 22, 2026
Get Claude Skills
9 min read

Two separate systems, easy to conflate

Claude Code actually has two distinct ways to change what a keystroke does, and it's worth telling them apart before touching either. keybindings.json is a full rebinding system: every action Claude Code exposes, submitting a message, toggling the todo list, cycling permission modes, has a namespaced action name you can map to any keystroke you want, per context. keybindingFlavor, added in Claude Code v2.1.238 (20 August 2026), is narrower: a single settings key that changes how a handful of built-in prompt-editing keys behave, without going through the rebinding system at all. This guide covers both, since knowing which one to reach for saves you from hunting for a setting in the wrong place.

Customizing shortcuts with keybindings.json

Run /keybindings inside a session to create or open your configuration file at ~/.claude/keybindings.json. Changes to the file are detected and applied automatically, no restart needed. The file's shape, per Claude Code's own keybindings reference, is an object with a bindings array, each entry naming a context and a map of keystrokes to actions:

{
  "$schema": "https://www.schemastore.org/claude-code-keybindings.json",
  "$docs": "https://code.claude.com/docs/en/keybindings",
  "bindings": [
    {
      "context": "Chat",
      "bindings": {
        "ctrl+e": "chat:externalEditor",
        "ctrl+u": null
      }
    }
  ]
}

That example rebinds Ctrl+E to open an external editor in the chat context, and unbinds Ctrl+U entirely. $schema and $docs are optional but useful for editor autocompletion while you're editing the file by hand.

Contexts: where a binding applies

Every binding block targets a specific context, so the same keystroke can do different things depending on what's focused. Claude Code defines nineteen of them, among the most commonly customised:

ContextApplies when
GlobalEverywhere in the app
ChatThe main chat input
AutocompleteThe autocomplete menu is open
ConfirmationA permission or confirmation dialog
TranscriptThe transcript viewer
HistorySearchHistory search mode (Ctrl+R)
TaskA background task is running
ScrollConversation scrolling in fullscreen rendering
ModelPickerThe model picker's effort-level controls
PluginThe plugin browse/discover/manage dialog

The full list also includes Settings, Tabs, Help, ThemePicker, Attachments, Footer, MessageSelector, DiffDialog, and Select. Each context exposes its own set of actions, so a Chat-context binding for ctrl+e and a Transcript-context binding for the same key don't collide, they simply apply in different situations.

Actions worth knowing

Actions follow a namespace:action naming pattern. A handful that come up often when people customise their setup:

chat:submit          Submit the current message (default: Enter)
chat:newline         Insert a newline without submitting (default: Ctrl+J)
chat:cycleMode       Cycle permission modes (default: Shift+Tab)
chat:externalEditor  Open the prompt in your default text editor (default: Ctrl+G)
chat:killAgents      Stop all running background subagents in this session
app:toggleTodos      Toggle Claude's to-do checklist (default: Ctrl+T)
history:search       Open history search (default: Ctrl+R)
task:background      Background the current task (default: Ctrl+B)

The reference documents the full action list per context, including less obvious ones like selection:copy and selection:clear for text selection in fullscreen rendering, and diff:previousFile / diff:nextFile for navigating the diff viewer.

Chords

A chord is a sequence of keystrokes separated by spaces in the JSON, pressed one after another rather than simultaneously:

{
  "bindings": [
    {
      "context": "Chat",
      "bindings": {
        "ctrl+k ctrl+s": "chat:externalEditor"
      }
    }
  ]
}

That binds Ctrl+K then Ctrl+S, pressed in sequence, to open the external editor. Claude Code ships a default chord family under Ctrl+X: Ctrl+X Ctrl+K to stop background subagents and Ctrl+X Ctrl+E to open an external editor, both in the Chat context, plus Ctrl+X Ctrl+B to background a task in the Task context.

Keystroke syntax

Modifiers combine with +: ctrl or control, shift, alt/opt/option/meta (Alt on Windows and Linux, Option on macOS), and cmd/command/super/win (Command on macOS, the Windows key on Windows, Super on Linux). That last group only registers in terminals that report the Super modifier, such as ones supporting the Kitty keyboard protocol; most terminals don't send it, so stick to ctrl or meta for anything you want to work everywhere.

A standalone uppercase letter implies Shift, so K and shift+k are equivalent, which matters for vim-style bindings where case carries meaning. Combined with an explicit modifier, though, case is purely stylistic: ctrl+K and ctrl+k are the same binding.

Special keys use plain names: escape/esc, enter/return, tab, space, the arrow keys as up/down/left/right, and backspace/delete.

Unbinding a default

Set the action to null to remove a default shortcut without replacing it:

{
  "bindings": [
    {
      "context": "Chat",
      "bindings": {
        "ctrl+s": null
      }
    }
  ]
}

This works for chords too, and unbinding every chord sharing a prefix frees that prefix for reuse as a single-key binding. Claude Code's own example: the default Ctrl+X family spans ctrl+x ctrl+k and ctrl+x ctrl+e in Chat, plus ctrl+x ctrl+b in Task. To reclaim bare Ctrl+X as a single-key binding, you have to unbind all three explicitly, in both contexts:

{
  "bindings": [
    {
      "context": "Task",
      "bindings": {
        "ctrl+x ctrl+b": null
      }
    },
    {
      "context": "Chat",
      "bindings": {
        "ctrl+x ctrl+k": null,
        "ctrl+x ctrl+e": null,
        "ctrl+x": "chat:newline"
      }
    }
  ]
}

If you unbind only some chords on a shared prefix, pressing that prefix still enters chord-wait mode for whichever bindings remain, so a half-cleared prefix doesn't behave like a free single-key shortcut.

What you can't rebind

Four shortcuts are permanently reserved: Ctrl+C (hardcoded interrupt and cancel), Ctrl+D (hardcoded exit), Ctrl+M (which most terminals treat identically to Enter, since both send a carriage return), and Caps Lock, which never reaches a terminal application in the first place regardless of what you bind it to.

Two more are worth knowing about because they conflict with common terminal multiplexers rather than Claude Code itself: Ctrl+B doubles as the tmux prefix (press twice inside tmux to send it through), and Ctrl+A is the GNU screen prefix. Ctrl+Z also suspends the process to your shell (Unix only, via SIGTSTP) rather than doing anything Claude Code-specific.

Validation

Claude Code checks your file on load and flags parse errors, invalid context names, reserved-shortcut conflicts, terminal multiplexer conflicts, and duplicate bindings within the same context. Warnings go to the debug log; start with claude --debug to see them.

The built-in text-editing shortcuts

Before getting into keybindingFlavor, it helps to see the full set of editing shortcuts it partially changes. These aren't part of keybindings.json either, they're fixed prompt-editing behaviour documented under interactive mode:

Ctrl+A    Move cursor to start of the current line
Ctrl+E    Move cursor to end of the current line
Ctrl+K    Delete to end of line (stores deleted text for pasting)
Ctrl+U    Delete from cursor to line start (stores deleted text; repeat to clear across lines)
Ctrl+W    Delete previous word (stores deleted text)
Ctrl+Y    Paste text most recently deleted with Ctrl+K, Ctrl+U, or Ctrl+W
Alt+Y     After Ctrl+Y, cycle through previously deleted text
Alt+B     Move cursor back one word
Alt+F     Move cursor forward one word
Alt+D     Delete next word
Ctrl+_ (or Ctrl+Shift+-)   Undo last input edit

On macOS, Option+Delete deletes the previous word as an alternative to Ctrl+W, and on Windows, Ctrl+Backspace does the same. The Alt-prefixed shortcuts (Alt+B, Alt+F, Alt+D, Alt+Y, Alt+P) need Option configured as Meta in your terminal on macOS; Claude Code's terminal configuration guide covers the setting for each common terminal app.

This whole group is what keybindingFlavor partially rewrites, not keybindings.json. That distinction is the reason this guide covers both systems together: the table above defines Claude Code's fixed line-editing behaviour, and the setting below is the one lever for changing part of it.

keybindingFlavor: readline-style word editing

Separate from all of the above, keybindingFlavor changes how a small set of built-in text-editing keys behave in the prompt input, added in Claude Code v2.1.238 and documented under interactive mode. The default value is "classic". Setting it to "readline" makes those keys follow GNU readline conventions, the same word-boundary rules Bash uses:

{
  "keybindingFlavor": "readline"
}

Under "readline", a word becomes a run of letters and digits, so punctuation like _, ., and / counts as a word boundary rather than part of a word:

  • Ctrl+W deletes back to the previous whitespace, instead of stopping at punctuation.
  • Alt+F and Alt+D stop at the end of the current word rather than the space after it.
  • Ctrl+Y can paste back whatever Alt+D most recently deleted, same as it already does for Ctrl+K and Ctrl+U.

The difference is concrete. Type fix the bug in src/utils/foo.ts into the prompt and press Ctrl+W. Under the classic default, that removes only foo.ts, since punctuation like / breaks the word. Under "readline", the whole path back to the previous space, src/utils/foo.ts, disappears in one keystroke, matching what Ctrl+W does in an ordinary Bash prompt.

Before v2.1.239 (21 August 2026), "readline" applied only to Ctrl+W; Alt+F, Alt+D and Ctrl+Y picked up the same convention in that later release.

One thing worth being explicit about: keybindingFlavor is not part of the keybindings.json rebinding system covered above. These specific word-editing commands aren't exposed as actions you can remap in that file, so keybindingFlavor is the only lever for changing how they behave, and it's a two-value switch rather than a full keybinding.

Vim mode: a third, separate layer

If you've turned on vim mode via /config → Editor mode, there's a third layer to keep straight. Vim mode handles input at the text level, cursor movement, mode switching (INSERT vs NORMAL), and motions. keybindings.json handles actions at the component level, submitting, toggling the todo list, and so on. The two operate independently: in vim mode, Escape switches INSERT to NORMAL rather than triggering the chat:cancel action, while most Ctrl-prefixed shortcuts still pass through to the keybinding system underneath.

Vim's own keys aren't remappable through keybindings.json at all. To map a two-key INSERT-mode sequence, the classic jj to return to NORMAL mode being the standard example, use the separate vimInsertModeRemaps setting instead:

{
  "editorMode": "vim",
  "vimInsertModeRemaps": { "jj": "<Esc>" }
}

Each entry must be exactly two printable characters mapped to "<Esc>", the only supported target; anything else in either position is ignored. Typing the first character inserts it normally; the second, if it arrives within one second, removes both and switches to NORMAL mode. Type the same two characters with a longer pause, or as a real word, and they stay as literal text. This setting is read only from your user settings file, the --settings flag, or managed settings, never from a project's .claude/settings.json or .claude/settings.local.json, so a checked-out repository can't quietly remap your keystrokes.

Troubleshooting

A binding in keybindings.json doesn't seem to apply. Check the context is spelled exactly right (context names are case-sensitive against the documented list) and that the action name uses the correct namespace:action form. Run with --debug to see Claude Code's own validation warnings for the file.

I unbound part of a chord family and the prefix still doesn't work as a single key. Unbind every chord sharing that prefix, in every context that defines one. A half-unbound prefix still enters chord-wait mode for whatever's left.

Ctrl+W isn't deleting what I expect. Check keybindingFlavor, not keybindings.json; this specific behaviour lives in the separate settings key, not the rebindable action system, and isn't listed as an action anywhere in keybindings.json.

Vim mode and a custom keybinding seem to fight each other. Remember they're separate layers: vim mode owns text-level input and motions, keybindings.json owns component-level actions. A vim key you want changed almost certainly needs vimInsertModeRemaps, not a keybindings.json entry.

Where to go next

For the settings file hierarchy that keybindingFlavor and vimInsertModeRemaps both live in, see Claude Code's own settings reference. For the fullscreen renderer these bindings apply inside, see Claude Code's output styles explained. Browse the current Claude Code catalogue at getclaudeskills.com/platforms/claude-code.

Verified 22 August 2026 directly against Claude Code's own keybindings and interactive mode documentation at code.claude.com, including the exact behaviour change to keybindingFlavor introduced in v2.1.239.

Frequently asked questions