Skip to content

Keybindings

Remap Claude Code's keyboard shortcuts with ~/.claude/keybindings.json, with every context, action, default key and the keystroke syntax.

Every keyboard shortcut in Claude Code is an action bound to a keystroke inside a context. You can change any of them, apart from a handful of reserved keys, by editing ~/.claude/keybindings.json. Run /keybindings to create the file (or open it if it exists). Changes are picked up live; there is no need to restart.

If a key is not reaching Claude Code at all (Shift+Enter submitting, Option doing nothing on a Mac), that is a terminal problem rather than a binding problem. See terminal configuration first.

File format

The file is a JSON object with a bindings array. Each entry names a context and maps keystrokes to action names. Two optional keys help your editor: $schema enables autocompletion and validation, and $docs is just a reference URL.

Here is the file I actually use. It sends on Ctrl+Enter only (so a stray Enter adds a line), puts the external editor on Ctrl+E, frees Ctrl+S, and adds y/n to dialogs:

{
  "$schema": "https://www.schemastore.org/claude-code-keybindings.json",
  "bindings": [
    {
      "context": "Chat",
      "bindings": {
        "enter": "chat:newline",
        "ctrl+enter": "chat:submit",
        "ctrl+e": "chat:externalEditor",
        "ctrl+s": null
      }
    },
    {
      "context": "Confirmation",
      "bindings": {
        "y": "confirm:yes",
        "n": "confirm:no"
      }
    }
  ]
}

Note: ctrl+enter only arrives as a distinct key in terminals that report extended keys. In others it looks like plain Enter, so test before you commit to a layout like this.

Setting an action to null removes the default binding for that key.

Contexts

A context says when a block of bindings is live.

ContextActive when
GlobalAlways
ChatThe main prompt input has focus
AutocompleteA suggestion menu is open
SettingsThe /config settings panel is open
ConfirmationA permission or confirmation dialog is showing
TabsA tabbed component has focus
HelpThe help menu is open
TranscriptThe transcript viewer is open
HistorySearchYou are searching history (Ctrl+R)
TaskA task is running in the foreground
ThemePickerThe theme picker is open
AttachmentsNavigating image attachments in a select dialog
FooterA footer item (tasks, teams, diff, artifacts) is selected
MessageSelectorChoosing a message in the rewind dialog
DiffDialogThe older diff viewer is open
DiffPanelThe fullscreen diff panel is open
ModelPickerThe model picker is open (effort controls)
EffortSliderThe /effort slider is open
SelectAny generic list or select component
PluginThe plugin browser and manager
AbovePromptThe band above the prompt, or a button in it, has focus
AbovePromptInputAn input in that band, or in a mod pane, has focus
AbovePromptSelectA select in that band, or in a mod pane, has focus
PaneA pane drawn by a mod has focus
PaneFieldAn input or select inside a mod pane has focus
AgentsAgent view, opened with claude agents
ScrollScrolling and selection in fullscreen rendering

Versions before v2.1.205 also had a Doctor context with a doctor:fix action for /doctor.

Actions by context

Action names follow namespace:action. "Unbound" means there is no default key, so the action does nothing until you assign one.

Global

ActionDefaultDoes
app:interruptCtrl+CCancels the current operation
app:exitCtrl+DQuits; press twice within 800 ms to confirm
app:redrawUnboundForces a terminal redraw
app:toggleTodosCtrl+TShows or hides Claude's to-do checklist (not the /tasks background-task view)
app:toggleTranscriptCtrl+OToggles the verbose transcript

History

ActionDefaultDoes
history:searchCtrl+ROpens history search
history:previousUpOlder prompt
history:nextDownNewer prompt

Chat

ActionDefaultDoes
chat:submitEnterSends the message
chat:newlineCtrl+JInserts a line break
chat:cancelEscapeCancels the current input
chat:clearInputCtrl+LFull redraw; keeps your input and the conversation
chat:clearScreenCmd+KSame as chat:clearInput (iTerm2 and Terminal.app intercept Cmd+K themselves; see fullscreen)
chat:cycleModeShift+TabCycles permission modes
chat:modelPickerMeta+POpens the model picker
chat:fastModeMeta+OToggles fast mode
chat:thinkingToggleMeta+TToggles extended thinking
chat:queueSubmitCtrl+X EnterSends but always queues behind the current turn, never interrupting; submits even when an autocomplete item is highlighted (v2.1.247+)
chat:sendNowCtrl+Enter, Ctrl+X Ctrl+SSends queued messages plus your draft immediately; with nothing running it just submits, and in shell mode it only queues (v2.1.275+). The chord works in every terminal
chat:undoCtrl+_, Ctrl+Shift+-Undoes the last edit
chat:externalEditorCtrl+G, Ctrl+X Ctrl+EOpens the prompt in your editor; agent view's dispatch input uses the single-key bindings of this action
chat:stashCtrl+SStashes the current prompt
chat:imagePasteCtrl+V (Alt+V on Windows and WSL; WSL binds both)Pastes an image from the clipboard
chat:killAgentsCtrl+X Ctrl+KStops all background subagents and disables artifact auto-replies for the session; press twice within 3 seconds. Works even while a background subagent's permission prompt is open

On Windows without VT mode (Node older than 24.2.0 or 22.17.0, Bun older than 1.2.23), Shift+Tab defaults become Meta+M, for both chat:cycleMode and confirm:cycleMode.

Voice (Chat context)

ActionDefaultDoes
voice:pushToTalkSpaceDictates, hold or tap depending on /voice mode. Only one key at a time; a custom key replaces Space. See voice dictation

Autocomplete

ActionDefaultDoes
autocomplete:acceptTabTakes the suggestion
autocomplete:dismissEscapeCloses the menu
autocomplete:previousUpPrevious suggestion
autocomplete:nextDownNext suggestion

Confirmation and permissions

ActionDefaultDoes
confirm:yesEnterAccepts
confirm:noEscapeDeclines or cancels
confirm:previousUpPrevious option
confirm:nextDownNext option
confirm:nextFieldTabIn the /fast dialog, flips fast mode
confirm:previousFieldUnboundIgnored; kept so old files stay valid
confirm:toggleSpaceToggles a selection
confirm:cycleModeShift+TabOn a file permission prompt, closes an open comment field, or else picks the "allow for this session" option when offered
permission:toggleDebugUnboundIgnored; kept so old files stay valid

Every dialog uses confirm:yes and confirm:no for accept and cancel, even if it is not a yes/no question. So if you bind bare y and n here, they act on all dialogs, including ones that never mention those letters. Dialogs that display y/n as options read the letters themselves anyway.

Up to v2.1.280, y and n were bound by default. If /keybindings created your file before then, those two lines are in it and remain active until you delete them. confirm:toggleExplanation (Ctrl+E, a model-written explanation on Bash and PowerShell prompts) existed before v2.1.257.

In most dialogs, a double Ctrl+C or Ctrl+D closes the dialog rather than quitting; the hint after the first press tells you which.

Transcript

ActionDefaultDoes
transcript:toggleShowAllCtrl+EShows all content (classic renderer only)
transcript:exitq, Ctrl+C, EscapeLeaves the transcript
ActionDefaultDoes
historySearch:nextCtrl+RNext match
historySearch:acceptEscape, TabKeeps the match in the prompt
historySearch:cancelCtrl+CAbandons the search
historySearch:executeEnterRuns the match
historySearch:cycleScopeCtrl+SCycles session, project, everywhere

The first four apply to the classic renderer's inline search, which always covers every project. historySearch:cycleScope only matters in fullscreen, where Ctrl+R opens a dialog instead; that dialog's Enter/Tab (place match) and Esc (cancel) are fixed.

Task, theme, help

ContextActionDefaultDoes
Tasktask:backgroundCtrl+B, Ctrl+X Ctrl+BSends the running task to the background
ThemePickertheme:toggleSyntaxHighlightingCtrl+TToggles syntax highlighting
Helphelp:dismissEscapeCloses help

Tabs

ActionDefaultDoes
tabs:nextTab, RightNext tab
tabs:previousShift+Tab, LeftPrevious tab

These work while the tab row has focus; in some dialogs (/help, /sandbox) they also work from inside the content. Up and Down move focus between the tab row and the content, and lists only respond while focused.

Attachments

ActionDefaultDoes
attachments:nextRightNext image
attachments:previousLeftPrevious image
attachments:removeBackspace, DeleteRemoves the selected image
attachments:exitDown, EscapeLeaves attachment navigation
ActionDefaultDoes
footer:nextRightNext item
footer:previousLeftPrevious item
footer:upUp, Ctrl+PUp (deselects at the top)
footer:downDown, Ctrl+NDown
footer:openSelectedEnterOpens the item
footer:clearSelectionEscapeDeselects
footer:closexStops the selected agent or workflow, or dismisses its row if finished
footer:dismissUnboundNo effect; kept for compatibility (it used Backspace/Delete before v2.1.281)

With a footer item selected, Enter opens it even if you remapped Enter in Chat. Chat keys the footer does not use, such as Shift+Tab, keep working.

Select

ActionDefaultDoes
select:nextDown, J, Ctrl+NNext option
select:previousUp, K, Ctrl+PPrevious option
select:pageUpPageUpPage up
select:pageDownPageDownPage down
select:firstHomeFirst option
select:lastEndLast option
select:acceptEnterChooses
select:cancelEscapeCancels

List panels (/skills, /mcp, /tasks) honour your bindings for all four paging actions. Most other lists, /model included, honour select:first and select:last but always page with PageUp and PageDown. (Before v2.1.280 those lists ignored Home/End; before v2.1.283 /mcp's tool list used fixed paging keys.)

Message selector (rewind)

The rewind list uses Select actions and your Select bindings. MessageSelector has no actions of its own; use it to override a Select action for this list only:

{
  "bindings": [
    { "context": "MessageSelector", "bindings": { "space": "select:accept" } }
  ]
}

Before v2.1.283 the list had messageSelector:up, :down, :top, :bottom and :select. Old bindings to those names still work, mapped to the matching Select action. Home and End jump to the ends (formerly Shift+K and Shift+J).

Model picker and effort slider

ContextActionDefaultDoes
ModelPickermodelPicker:decreaseEffortLeftLower effort
ModelPickermodelPicker:increaseEffortRightHigher effort
ModelPickermodelPicker:thisSessionOnlysUse the highlighted model for this session only
EffortSlidereffortSlider:decreaseEffortLeftLower effort (v2.1.284+)
EffortSlidereffortSlider:increaseEffortRightHigher effort (v2.1.284+)
EffortSlidereffortSlider:toggleUltracodeTabUltracode on or off for this session, where offered (v2.1.284+)
EffortSlidereffortSlider:thisSessionOnlysApply the focused effort to this session only (v2.1.257+)

The slider's Enter and Escape cannot be rebound. See model configuration for effort levels.

Plugin

ActionDefaultDoes
plugin:toggleSpaceSelects or deselects a plugin
plugin:installIInstalls selected plugins
plugin:favoriteFFavourites a plugin so it sorts near the top of Installed

Settings

The settings panel reuses two actions. Changes save immediately, so Escape closes without discarding anything.

ActionDefaultDoes
settings:search/Search settings
settings:retryRRetry loading usage data after an error
select:acceptEnter, SpaceChange the setting or open its submenu
confirm:noEscapeClose the panel

Agents (agent view)

Requires v2.1.257 or later.

ActionDefaultDoes
agents:switchViewCtrl+SGroup sessions by state or by directory
agents:togglePinCtrl+TPin or unpin the selected session
agents:findCtrl+FFind sessions by name using the n: filter (v2.1.288+)
agents:renameCtrl+RRename the selected session (v2.1.288+)
agents:previousGroupCtrl+Up, Meta+UpPrevious group header (v2.1.288+)
agents:nextGroupCtrl+Down, Meta+DownNext group header (v2.1.288+)

In agent view an Agents binding wins over any Chat or Global binding on the same key, so Ctrl+S groups rather than stashing. Bindings there fire on single keystrokes, so chords (like Ctrl+X Ctrl+E) do not work; the dispatch input's external editor follows chat:externalEditor's single-key binding, Ctrl+G by default.

Scroll and selection (fullscreen)

ActionDefaultDoes
scroll:lineUpwheelupOne line up
scroll:lineDownwheeldownOne line down
scroll:pageUpPageUpHalf a viewport up
scroll:pageDownPageDownHalf a viewport down
scroll:halfPageUpUnboundHalf a viewport up (alias for vi-style maps)
scroll:halfPageDownUnboundHalf a viewport down
scroll:fullPageUpUnboundFull viewport up
scroll:fullPageDownUnboundFull viewport down
scroll:topCtrl+HomeStart of conversation
scroll:bottomCtrl+EndLatest message; resumes auto-follow
selection:copyCtrl+Shift+C / Cmd+CCopies the selection
selection:clearUnboundClears the selection (v2.1.234+)
selection:extendLeftShift+LeftExtend one column left
selection:extendRightShift+RightExtend one column right
selection:extendUpShift+UpExtend one row up, scrolling at the edge
selection:extendDownShift+DownExtend one row down, scrolling at the edge
selection:extendLineStartShift+HomeExtend to line start
selection:extendLineEndShift+EndExtend to line end

MacBook users: Ctrl+End cannot be typed, so give scroll:bottom something reachable. A vi-flavoured example:

{
  "bindings": [
    {
      "context": "Scroll",
      "bindings": {
        "ctrl+u": "scroll:halfPageUp",
        "ctrl+f": "scroll:halfPageDown",
        "meta+g": "scroll:bottom"
      }
    }
  ]
}

Ctrl+D would be the natural vi partner for Ctrl+U, but it is reserved for exit, hence Ctrl+F here.

Diff viewer (older dialog)

These reach only the older diff viewer, which /diff opens outside fullscreen after you disable the built-in cc-plugin-diff mod in /plugin. With the mod enabled, /diff shows the newer dialog. Files naming these actions load fine either way.

ActionDefaultDoes
diff:dismissEscapeCloses, or returns from detail to the file list
diff:previousSourceLeftPrevious diff source
diff:nextSourceRightNext diff source
diff:previousFileUp, KPrevious file, or up a line in detail view
diff:nextFileDown, JNext file, or down a line in detail view
diff:backUnboundBack (Escape already does this; Left was removed in v2.1.203)

The file list also honours Select actions (select:previous, select:next, and Enter via select:accept). Bind them in a DiffDialog block to change them for this list only. diff:viewDetails from before v2.1.283 still works as select:accept.

The detail view adds pager keys within DiffDialog: scroll:pageUp (PageUp), scroll:pageDown (PageDown), scroll:fullPageUp (Shift+Space, B), scroll:fullPageDown (Space), scroll:top (G, Home), scroll:bottom (Shift+G, End). The Scroll context defaults are unaffected.

Diff panel (fullscreen)

app:cycleDiffBase lives in DiffPanel; the rest are Global. The built-in cc-plugin-diff mod draws this panel and handles the first three below; the toggle actions only reach the older panel shown when that mod is disabled.

ActionDefaultDoes
app:cycleDiffBaseCtrl+X BCompare against this session, uncommitted, or branch
app:diffFileListUpCtrl+Up, Meta+UpScroll an overflowing file list up
app:diffFileListDownCtrl+Down, Meta+DownScroll it down
app:toggleReplTabUnboundOpen or close the panel
app:toggleDiffNoiseFilterUnboundHide or show test and generated files
app:toggleDiffPreSessionUnboundExpand or collapse pre-session changes

Band above the prompt

The band is where mods draw buttons, inputs and selects. abovePrompt:toggle and abovePrompt:focus apply in Chat; the others apply in whichever context has focus.

ActionDefaultDoes
abovePrompt:toggleCtrl+X Ctrl+ACollapse the band to a one-row hint or expand it
abovePrompt:focusCtrl+X TabMove focus into the band, then each open pane, then back to the prompt
abovePrompt:nextTabNext control
abovePrompt:previousShift+TabPrevious control
abovePrompt:pressEnterPress a button, submit an input, or pick a select option
abovePrompt:leaveEscapeBack to the prompt
abovePrompt:highlightNextDownNext option in a focused select
abovePrompt:highlightPreviousUpPrevious option in a focused select

Extra defaults: in AbovePrompt, Right/Left also mean next/previous and Space means press; in AbovePromptInput, Down/Up mean next/previous. AbovePrompt also binds Up, Down, PageUp, PageDown, Home and End to the pane scroll actions below, so rebind those in an AbovePrompt block to change them for the band.

Panes

ActionDefaultDoes
pane:scrollUpUpScroll up
pane:scrollDownDownScroll down
pane:pageUpPageUpPage up
pane:pageDownPageDownPage down
pane:topHomeTop
pane:bottomEndBottom
pane:growCtrl+X Left, Ctrl+X UpMore width (beside the transcript) or height (above the prompt)
pane:shrinkCtrl+X Right, Ctrl+X DownLess room
pane:closeCtrl+X XClose the pane (also in PaneField)
pane:nextUnboundNext open pane (Global, when several are open)
pane:previousUnboundPrevious open pane

Pane also maps Tab, Shift+Tab, Enter and Escape to the band actions, and pane fields use AbovePromptInput and AbovePromptSelect. See mod interface for what a mod receives.

Keystroke syntax

Modifiers

Join with +:

WriteMeans
ctrl, controlControl
shiftShift
alt, opt, option, metaAlt on Windows/Linux, Option on macOS
cmd, command, super, winCmd on macOS, Windows key, Super on Linux

The cmd family is only seen in terminals that report Super (kitty keyboard protocol, or xterm modifyOtherKeys). Most do not, so prefer ctrl or meta for anything you want portable. Examples: ctrl+g, shift+tab, meta+o, ctrl+shift+x.

Case

Key names are case-insensitive: ctrl+G equals ctrl+g. For Shift plus a letter, write shift+g.

Non-US layouts

Always write Ctrl shortcuts with Latin letters.

  • Non-Latin layouts (e.g. Cyrillic): if the terminal uses the kitty keyboard protocol and reports physical key positions, Ctrl shortcuts match by US-layout position, so Ctrl plus the physical W key fires ctrl+w. Otherwise Claude Code matches what the terminal sends: an ASCII control code fires the Latin shortcut, but a Cyrillic character matches nothing.
  • Rearranged Latin layouts (e.g. AZERTY): the letter the key types is what matches, so Ctrl and the key labelled A fires ctrl+a.

Chords

Separate keystrokes with spaces, for example ctrl+x ctrl+e. Each press must follow within 3 seconds or the chord is cancelled with a short notice.

Special key names

escape/esc, enter/return, tab, space, up, down, left, right, pageup, pagedown, home, end, backspace, delete, and the mouse wheel events wheelup and wheeldown.

Unbinding and chord prefixes

null removes a binding, chords included. A prefix such as ctrl+x stays reserved (pressing it waits for the second key) while any chord in any active context starts with it. To reclaim the prefix for a single key, unbind every chord that uses it, in the context that defines it.

Default ctrl+x chords:

ContextChords
Chatctrl+x ctrl+k, ctrl+x ctrl+e, ctrl+x enter (v2.1.247+), ctrl+x ctrl+a (v2.1.260+), ctrl+x ctrl+s (v2.1.275+), ctrl+x tab (v2.1.260+)
Taskctrl+x ctrl+b
DiffPanelctrl+x b (v2.1.260+)
Panectrl+x left, ctrl+x right, ctrl+x up, ctrl+x down, ctrl+x x
PaneFieldctrl+x x

For example, to make ctrl+x itself open the external editor:

{
  "bindings": [
    { "context": "Task", "bindings": { "ctrl+x ctrl+b": null } },
    { "context": "DiffPanel", "bindings": { "ctrl+x b": null } },
    {
      "context": "Pane",
      "bindings": {
        "ctrl+x left": null, "ctrl+x right": null,
        "ctrl+x up": null, "ctrl+x down": null, "ctrl+x x": null
      }
    },
    { "context": "PaneField", "bindings": { "ctrl+x x": null } },
    {
      "context": "Chat",
      "bindings": {
        "ctrl+x ctrl+k": null, "ctrl+x ctrl+e": null, "ctrl+x enter": null,
        "ctrl+x ctrl+a": null, "ctrl+x ctrl+s": null, "ctrl+x tab": null,
        "ctrl+x": "chat:externalEditor"
      }
    }
  ]
}

Leave even one chord in place and the prefix keeps waiting for a second key. Bear in mind you lose the default way to kill agents, send now, toggle the band and so on, so rebind whichever of those you use.

Reserved keys

KeyWhy it cannot be rebound
Ctrl+CFixed interrupt/cancel
Ctrl+DFixed exit
Ctrl+MAlways arrives as Enter
Ctrl+[Always arrives as Escape (kitty protocol terminals from v2.1.242)
Ctrl+IAlways arrives as Tab
Ctrl+HThe ASCII backspace byte; Windows handling depends on the terminal and CLAUDE_CODE_BS_AS_CTRL_BACKSPACE (see terminal configuration)
Caps LockNever delivered to terminal apps

Clashes with multiplexers and the shell

KeyTaken by
Ctrl+Btmux prefix (press twice to pass it through)
Ctrl+AGNU screen prefix
Ctrl+ZUnix suspend (SIGTSTP)

Bare keys and text fields

If you bind a plain letter, digit or Space, you can still type it in dialog text fields, such as the Other answer to a question. While such a field is focused, unmodified printable keys go to the field and skip binding lookup. Keys that still run bindings there:

  • non-printing keys: Enter, Escape, Tab, arrows
  • anything with Ctrl, Alt or Cmd
  • the second half of a chord already in progress

At the main prompt it works the other way round: every key is checked against active contexts first, and typed only if nothing claims it.

Vim mode

With Vim editing on (/config → Editor mode), the two systems work at different layers:

  • Vim handles text-level input: motions, modes, operators.
  • Keybindings handle component-level actions: submit, toggle to-dos and so on.
  • Escape switches INSERT to NORMAL and does not fire chat:cancel.
  • Most Ctrl shortcuts pass through to keybindings.
  • Vim keys cannot be remapped here; for an INSERT-mode pair such as jk to Escape use vimInsertModeRemaps (see interactive mode).
  • In NORMAL mode, ? opens help and / opens history search.

Validation

Problems are written to the debug log as warnings. Start with claude --debug to see them. What gets flagged:

  • invalid JSON or wrong structure
  • misspelt modifiers like crtl+k (the unknown part is dropped, so the binding lands on plain k, which is rarely what you want)
  • unknown context names
  • action values that are neither a string nor null
  • unknown action names (the binding is skipped and the default for that key stays)
  • attempts to bind reserved keys
  • the same key bound twice in one context