[FEATURE] Add user-configurable keyboard shortcut actions

Open 💬 20 comments Opened Oct 8, 2025 by fj

Preflight Checklist

  • [x] I have searched existing requests and this feature hasn't been requested yet
  • [x] This is a single feature request (not multiple features)

Problem Statement

It is frequently the case that Claude's keyboard shortcuts collide with terminal or OS shortcuts, or an action that would be helpful for a user workflow to take (e.g. a custom slash command) is not bound to a shortcut at all. Claude Code does not currently expose a way to have user-configurable keyboard shortcuts.

Proposed Solution

Users should be able to bind keystrokes or keychords which correspond to some set of Claude-exposed hooks or custom slash commands.

This could be configurable, in, say, a settings.json manner similar to the way that VSCode and VSCodium do. As an example, imagine a schema and instantiation like:

"settings.user.keybindings" : [
  {
    "keybinding": "ctrl+C",
    "action": ["/clear"]
  }
  {
    "keybinding": "ctrl+M A",
    "action": ["/compact", "/custom_one", "/custom_two"]
  }
]

In this example, hitting Ctrl+C causes the TUI to receive the /clear command. Hitting Ctrl+M followed by A causes the TUI to receive the /compact command followed by /custom_one and then /custom_two.

Additional desired behaviors:

  • Keybinding actions are in a stack, such that the first receiver of any keybinding wins. The stack for each keybinding is initialized with the Claude defaults at initialization time, and the user defaults are then added next. This means that whenever one of Claude's default keybindings conflicts with a user keybinding, the _user_ keybinding takes precedence over the Claude default.
  • When keybindings are updated this mapping is reset without needing to restart Claude.

Alternative Solutions

Some other issues propose specific keyboard shortcuts, e.g. #9153. Most of the alternatives rely on using a specific upstream terminal or UI that encodes these macros for the benefit of the Claude TUI. These are generally unreliable because Claude Code doesn't expose a standard interface to receive these, and therefore is in the best position to implement keyboard shortcuts as a feature, rather than something upstream emulating keypresses.

Priority

High - Significant impact on productivity

Feature Category

CLI commands and flags

Use Case Example

Two examples:

  • Imagine that you use terminal multiplexer like tmux or screen with a keyboard shortcut that conflicts with Claude. Without this feature, there will be no way to send Claude the keyboard shortcut because you can't override the settings. If there were a user-configurable capability for keyboard shortcuts, this wouldn't be a problem.
  • You frequently send a sequence of custom slash commands. Rather than typing these out each time, it would be nicer if you could use a keyboard shortcut for this instead. Without this feature, there's no way to do this either.

View original on GitHub ↗

20 Comments

github-actions[bot] · 9 months ago

Found 2 possible duplicate issues:

  1. https://github.com/anthropics/claude-code/issues/8315
  2. https://github.com/anthropics/claude-code/issues/796

This issue will be automatically closed as a duplicate in 3 days.

  • If your issue is a duplicate, please close it and 👍 the existing issue instead
  • To prevent auto-closure, add a comment or 👎 this comment

🤖 Generated with Claude Code

fj · 9 months ago

Responding to potential duplicates:

  • This is not a duplicate of #796 because that specifically asks for _Readline-compatible_ keybinding configurations. This is a request for a more general capability than that.
  • This is not a duplicate of #8315 because that was closed as a duplicate of #5376, which is about Ctrl+B keybindings on MacOS. #5376 was marked as fixed because Ctrl+B no longer conflicts, but the general idea outlined in this ticket is unresolved.
TyceHerrman · 9 months ago

I accidentally made a duplicate https://github.com/anthropics/claude-code/issues/9490 - already closed it but it's helpful in that I linked a bunch of disparate issues related to keyboard shortcut remapping to show the range of conflicts people are experiencing. I mentioned a new conflict not documented yet - ctrl+g for external text editor conflicts with Zellij's lock mode.

mbinde · 7 months ago

adding my own since I'm tired of consuming all my tokens by accidentally turning on thinking mode and not noticing it:

Description:

Currently, Claude Code uses Tab to toggle thinking mode on/off. This
keybinding is easy to trigger accidentally during normal typing workflows.
I'd like to request support for customizable keybindings in the settings
configuration.

Proposed Solution

Add a keybindings section to ~/.claude/settings.json (and project-level
.claude/settings.json):

{
"alwaysThinkingEnabled": false,
"keybindings": {
"toggleThinking": "Shift+Tab",
// Future extensibility for other shortcuts
"clearScreen": "Ctrl+L",
"cancelRequest": "Ctrl+C"
}
}

Use Case

The Tab key is frequently used during:

  • Auto-completion workflows
  • Navigation between UI elements
  • Normal typing patterns (muscle memory)

Accidentally toggling thinking mode disrupts the flow. A less collision-prone
keybinding (like Shift+Tab, Option+Tab, or Ctrl+T) would improve the UX.

Alternative Solutions

  1. Disable built-in shortcuts: Allow users to disable specific keybindings

entirely

  1. Keybinding conflicts warning: Detect and warn about common keybinding

conflicts

  1. Modifier-only shortcuts: Require modifier keys (Shift/Ctrl/Option) for all

toggle actions

Additional Context

  • Current settings file location: ~/.claude/settings.json
  • Current settings support: alwaysThinkingEnabled, statusLine, etc.
  • Similar tools (VS Code, terminal emulators) provide extensive keybinding

customization

wuwenrufeng · 7 months ago

I'd also like to bind F5 to /start-server for quick server startup Shift+F5 stop server during development.

jonatanblue · 7 months ago

Just adding that Ctrl+B is the default Tmux mapping for entering command mode. Claude is using it to background processes and has worked around the conflict by requiring double tap.

This means if I accidentally press Ctrl+B twice when trying to send a command to Tmux (which happens often, such as opening a new pane while a Claude command is running) the currently running Claude command will be backgrounded.

The only way to avoid this is to remap the Tmux shortcut to something else and unlearn years muscle memory.

Stefan-Schmidbauer · 7 months ago

Use Case: Stream Deck / External Control Device Integration

I'd like to add an use case for this feature: integration with external control devices like Stream Deck, macro keyboards, or accessibility tools.

The mode-switching problem: With the current cyclic Shift+Tab toggle, you can't assign a button to jump directly to a specific mode. Pressing the same button multiple times cycles through all modes unpredictably.

What would help: Direct keyboard shortcuts to jump to specific modes, e.g.:

  • Alt+Ctrl+Shift+PPlan Mode
  • Alt+Ctrl+Shift+AAsk before edits
  • Alt+Ctrl+Shift+EEdit automatically

With configurable shortcuts, users could map Stream Deck buttons reliably and build better automation around Claude Code.

github-actions[bot] · 6 months ago

This issue has been inactive for 30 days. If the issue is still occurring, please comment to let us know. Otherwise, this issue will be automatically closed in 30 days for housekeeping purposes.

fj · 6 months ago

The issue is still occurring.

gjermundgaraba · 5 months ago

+1 on this, my use case is that I want to have a shortcut for "/resume"

sf1tzp · 5 months ago

I came here after finding #16176 closed. I am in the habit of pressing Escape to get to Normal mode (vim, and even use vim bindings in my terminal)... I've accidentally interrupted claude a few times doing this and would appreciate a method to unbind Escape.

G-S-Paris · 4 months ago

Adding my own since it looks like every other thread is getting closed:

Escape is bound to chat:cancel (interrupt work) and confirm:no (deny permission) by default. For vim and tmux users, Escape is pressed constantly — exiting insert mode, canceling a tmux prefix sequence, etc. This regularly causes accidental interruptions and permission denials mid-session. There's no way to rebind or unbind these keys. The feature already exists Claude Code already has a full keybinding customization system built in (as of 2.1.69): - ~/.claude/keybindings.json with schema support - Context-aware bindings (Chat, Confirmation, Global, etc.) - Chord support (ctrl+k ctrl+c) - Unbinding via null - Validation via /doctor - A built-in keybindings-help skill It's gated behind the tengu_keybinding_customization_release feature flag, which defaults to false. Request Please GA (or at minimum opt-in enable) the keybinding customization feature. The implementation looks complete and production-ready. This would immediately unblock vim/tmux/screen users who are currently unable to avoid accidental Escape-triggered interruptions. Environment - Claude Code 2.1.69 - Linux, tmux, zsh, vim keybindings
trent-40hero · 4 months ago

another possible solution....
<h2 style="color: rgb(204, 204, 204); font-family: -apple-system, &quot;system-ui&quot;, &quot;Segoe UI&quot;, Roboto, sans-serif; font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; letter-spacing: normal; orphans: 2; text-align: start; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; white-space: normal; background-color: rgb(24, 24, 24); text-decoration-thickness: initial; text-decoration-style: initial; text-decoration-color: initial;">Summary</h2><p style="white-space: pre-wrap; margin-top: 0.1em; margin-bottom: 0.2em; color: rgb(204, 204, 204); font-family: -apple-system, &quot;system-ui&quot;, &quot;Segoe UI&quot;, Roboto, sans-serif; font-size: 13px; font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-align: start; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; background-color: rgb(24, 24, 24); text-decoration-thickness: initial; text-decoration-style: initial; text-decoration-color: initial;">The VS Code extension doesn't expose a command to programmatically send a message to the chat input. This prevents users from creating keybindings that open Claude Code and submit a prompt in one step.</p><h2 style="color: rgb(204, 204, 204); font-family: -apple-system, &quot;system-ui&quot;, &quot;Segoe UI&quot;, Roboto, sans-serif; font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; letter-spacing: normal; orphans: 2; text-align: start; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; white-space: normal; background-color: rgb(24, 24, 24); text-decoration-thickness: initial; text-decoration-style: initial; text-decoration-color: initial;">Use Case</h2><p style="white-space: pre-wrap; margin-top: 0.1em; margin-bottom: 0.2em; color: rgb(204, 204, 204); font-family: -apple-system, &quot;system-ui&quot;, &quot;Segoe UI&quot;, Roboto, sans-serif; font-size: 13px; font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-align: start; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; background-color: rgb(24, 24, 24); text-decoration-thickness: initial; text-decoration-style: initial; text-decoration-color: initial;">I want a keybinding that opens a new Claude Code tab and runs <code style="font-family: monospace; color: rgb(208, 208, 208); background-color: rgb(60, 60, 60); padding: 2px 4px; border-radius: 3px; font-size: 0.9em; word-break: break-word;">/prime</code> automatically:</p><div class="codeBlockWrapper_-a7MRw" style="position: relative; margin: 8px 0px; color: rgb(204, 204, 204); font-family: -apple-system, &quot;system-ui&quot;, &quot;Segoe UI&quot;, Roboto, sans-serif; font-size: 13px; font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-align: start; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; white-space: normal; background-color: rgb(24, 24, 24); text-decoration-thickness: initial; text-decoration-style: initial; text-decoration-color: initial;"><button class="copyButton_CEmTFw copyButton_-a7MRw" title="Copy code" aria-label="Copy code to clipboard" style="color: rgb(204, 204, 204); font-family: -apple-system, &quot;system-ui&quot;, &quot;Segoe UI&quot;, Roboto, sans-serif; font-size: 13px; background: none 0% 0% / auto repeat scroll padding-box border-box rgb(31, 31, 31); border-color: rgb(69, 69, 69); border-style: solid; border-width: 0.833333px; border-image: none 100% / 1 / 0 stretch; cursor: pointer; opacity: 0; display: flex; border-radius: 4px; justify-content: center; align-items: center; padding: 4px; transition: opacity 0.15s, background 0.15s; position: absolute; top: 4px; right: 4px;"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" fill="currentColor" aria-hidden="true" data-slot="icon" class="copyIcon_CEmTFw"><path fill-rule="evenodd" d="M15.988 3.012A2.25 2.25 0 0 1 18 5.25v6.5A2.25 2.25 0 0 1 15.75 14H13.5v-3.379a3 3 0 0 0-.879-2.121l-3.12-3.121a3 3 0 0 0-1.402-.791 2.252 2.252 0 0 1 1.913-1.576A2.25 2.25 0 0 1 12.25 1h1.5a2.25 2.25 0 0 1 2.238 2.012ZM11.5 3.25a.75.75 0 0 1 .75-.75h1.5a.75.75 0 0 1 .75.75v.25h-3v-.25Z" clip-rule="evenodd"></path><path d="M3.5 6A1.5 1.5 0 0 0 2 7.5v9A1.5 1.5 0 0 0 3.5 18h7a1.5 1.5 0 0 0 1.5-1.5v-5.879a1.5 1.5 0 0 0-.44-1.06L8.44 6.439A1.5 1.5 0 0 0 7.378 6H3.5Z"></path></svg></button><pre style="overflow-x: auto; white-space: pre; box-sizing: border-box; border-radius: 4px; max-width: 100%; margin: 0px; padding: 8px;"><code class="language-json" style="font-family: monospace; color: rgb(208, 208, 208); background-color: rgb(60, 60, 60); padding: 0px; border-radius: 3px; font-size: 0.9em; word-break: break-word;">{
"key": "ctrl+alt+c",
"command": "runCommands",
"args": {
"commands": [
"claude-vscode.editor.open",
{
"command": "claude-vscode.sendMessage",
"args": { "text": "/prime" }
}
]
}
}
</code></pre></div><p style="white-space: pre-wrap; margin-top: 0.1em; margin-bottom: 0.2em; color: rgb(204, 204, 204); font-family: -apple-system, &quot;system-ui&quot;, &quot;Segoe UI&quot;, Roboto, sans-serif; font-size: 13px; font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-align: start; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; background-color: rgb(24, 24, 24); text-decoration-thickness: initial; text-decoration-style: initial; text-decoration-color: initial;">This isn't possible today. The extension registers <code style="font-family: monospace; color: rgb(208, 208, 208); background-color: rgb(60, 60, 60); padding: 2px 4px; border-radius: 3px; font-size: 0.9em; word-break: break-word;">open</code>, <code style="font-family: monospace; color: rgb(208, 208, 208); background-color: rgb(60, 60, 60); padding: 2px 4px; border-radius: 3px; font-size: 0.9em; word-break: break-word;">focus</code>, <code style="font-family: monospace; color: rgb(208, 208, 208); background-color: rgb(60, 60, 60); padding: 2px 4px; border-radius: 3px; font-size: 0.9em; word-break: break-word;">newConversation</code>, etc. but nothing to insert or submit text.</p><h2 style="color: rgb(204, 204, 204); font-family: -apple-system, &quot;system-ui&quot;, &quot;Segoe UI&quot;, Roboto, sans-serif; font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; letter-spacing: normal; orphans: 2; text-align: start; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; white-space: normal; background-color: rgb(24, 24, 24); text-decoration-thickness: initial; text-decoration-style: initial; text-decoration-color: initial;">Precedent</h2><p style="white-space: pre-wrap; margin-top: 0.1em; margin-bottom: 0.2em; color: rgb(204, 204, 204); font-family: -apple-system, &quot;system-ui&quot;, &quot;Segoe UI&quot;, Roboto, sans-serif; font-size: 13px; font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-align: start; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; background-color: rgb(24, 24, 24); text-decoration-thickness: initial; text-decoration-style: initial; text-decoration-color: initial;">GitHub Copilot Chat exposes <code style="font-family: monospace; color: rgb(208, 208, 208); background-color: rgb(60, 60, 60); padding: 2px 4px; border-radius: 3px; font-size: 0.9em; word-break: break-word;">workbench.action.chat.send</code> which accepts a message string — this is a well-established pattern in VS Code extensions with chat interfaces.</p><h2 style="color: rgb(204, 204, 204); font-family: -apple-system, &quot;system-ui&quot;, &quot;Segoe UI&quot;, Roboto, sans-serif; font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; letter-spacing: normal; orphans: 2; text-align: start; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; white-space: normal; background-color: rgb(24, 24, 24); text-decoration-thickness: initial; text-decoration-style: initial; text-decoration-color: initial;">Proposed Commands</h2>
Command | Args | Behavior
-- | -- | --
claude-vscode.sendMessage | { "text": "string" } | Insert text and submit
claude-vscode.insertText (optional) | { "text": "string" } | Insert text without submitting

<p style="white-space: pre-wrap; margin-top: 0.1em; margin-bottom: 0.2em; color: rgb(204, 204, 204); font-family: -apple-system, &quot;system-ui&quot;, &quot;Segoe UI&quot;, Roboto, sans-serif; font-size: 13px; font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-align: start; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; background-color: rgb(24, 24, 24); text-decoration-thickness: initial; text-decoration-style: initial; text-decoration-color: initial;">The first alone would cover most use cases. The second would be nice for composing prompts with multiple keybindings.</p>

LiamClarkeNZ · 3 months ago

I would love to be able to remap the behaviour of return so that it enters a naive newline instead of submitting the chat, and then bind something else to "submit chat".

HerbWarren · 3 months ago

Another ctrl-g conflict: Chrome's organization-managed Gemini shortcut intercepts
ctrl-g globally when Chrome is open. Since the Chrome profile is managed by my
organization, I can't disable this shortcut. Being able to rebind plan mode's
editor shortcut would solve this so I could actually view plans easily from Claude.

rw3iss · 2 months ago

Escape... is a killer (literally 😄). Please let us remap it.
Ideally escape would just not kill the current agent process (only clear the input).

jdlane2 · 2 months ago

+1 — calling out the specific ctrl+c case here since it's currently listed as explicitly non-rebindable in ~/.claude/keybindings.json (along with ctrl+d, ctrl+m).

The conflict with the universal copy shortcut is painful on Windows in particular, where ctrl+c = copy is muscle memory. I regularly lose sessions by reflex when trying to copy text from the chat.

Two acceptable resolutions:

  1. Allow ctrl+c to be remapped/unbound via keybindings.json (preferred — fits this issue's scope)
  2. Change the default exit/interrupt to a different combo (e.g. ctrl+shift+c) and let users opt back in to the legacy binding

Today there's no escape hatch — even setting "ctrl+c": null produces a /doctor error.

blackclavus · 1 month ago

+1 on this. My specific use case: I want to bind ctrl+alt+c to /copy so I can quickly copy the last response without typing the command each time. A generic action like chat:command:/copy or chat:runCommand that accepts any slash command would cover this and many other workflows. Would love to see this land.

fruitymcdoo · 11 days ago

Seconding this. I'm tired of accidentally cancelling a long running query by accidentally pressing escape. At least please let us rebind or remove Escape as a shortcut to cancel thinking.

yngvark · 8 days ago

I constantly use escape to navigate, but then press escape one time too many and either exit claude or interrupt workflows (that might have been running for a long time, using subagents possibly).

Using the same key for navigation and interruption is a problem!