Description: AlpineJS allows you to easily listen for specific key presses (e.g., .enter, .escape) or key combinations (e.g., .ctrl.enter) on input elements or globally on the window. This is essential for creating rich, keyboard-navigable user interfaces.
AlpineJS uses the @ symbol as a shorthand for x-on: to listen for browser events. For keyboard events, you typically use @keydown or @keyup.
To listen for a specific key, append its name (or an alias) as a modifier to the event. For example:
<input @keydown.enter="submitForm">
<div @keyup.escape="closeModal"></div>
AlpineJS provides convenient aliases for common keys. These are typically kebab-cased if multi-word:
.enter.escape (or .esc).space.arrow-up, .arrow-down, .arrow-left, .arrow-right.tab.delete (handles both "Delete" and "Backspace" keys).backspace.home, .end, .page-up, .page-down.f1, .f2, ..., .f12@keydown.a="handleAKey" for the 'a' key. For non-alphanumeric keys, use their event.key value in kebab-case (e.g., @keydown.media-play-pause="togglePlayback").These check if a system modifier key was pressed simultaneously with the main key. They correspond to properties on the native browser event object (e.g., event.shiftKey).
.shift: Checks for the Shift key..alt: Checks for the Alt key (Option key on macOS)..ctrl: Checks for the Control key. This is an alias for .control..meta: Checks for the Meta key (Command key ⌘ on macOS, Windows key ⊞ on Windows)..cmd: A macOS-specific alias for .meta. AlpineJS intelligently maps .cmd to .meta for cross-platform convenience, effectively treating .cmd.enter as "Command+Enter" on Mac and "Meta+Enter" on other OSes (though users typically associate Cmd with Mac). For combinations like "Ctrl+Enter" meant for all platforms, .ctrl.enter is preferred..control: The explicit modifier for the Control key.<input @keydown.shift.enter="submitWithShift">
<button @click.alt="showAlternateOptions">Click or Alt+Click Me</button>
You can chain multiple modifiers together. The order of chained modifiers (e.g., .ctrl.shift vs .shift.ctrl) generally doesn't matter.
<!-- Listens for Ctrl + Shift + S -->
<div @keydown.window.ctrl.shift.s.prevent="complexSaveAction"></div>
.window and .document:
To listen for keyboard events globally, append .window or .document to the event name. This is useful for shortcuts like closing a modal with Escape, regardless of what element has focus.
<div x-data="{ open: true }" @keydown.window.escape="open = false">
Modal Content (Press ESC to close)
</div>
AlpineJS also supports .prevent (calls event.preventDefault()) and .stop (calls event.stopPropagation()), which can be combined with key modifiers.
<!-- Prevents default form submission on Enter -->
<form @submit.prevent="handleSubmit">
<input type="text" @keydown.enter.prevent="customEnterAction">
</form>
keyup vs. keydown inappropriately:
keydown fires when the key is first pressed down and will continue to fire if the key is held (key repeat). keyup fires only when the key is released.
For actions like submitting a form with "Enter" or navigating UI elements with arrow keys, keydown often provides a more responsive feel. However, be mindful of repeated firing if the key is held; you might need to add logic to handle this or use .prevent carefully.
For actions that should occur after typing or to avoid multiple triggers from a held key (e.g., initiating a search after text input), keyup might be more suitable. For text input changes, the input event (@input) is generally preferred over key events for capturing the value.
.window, .document) firing too often or when not intended:
When you attach a keyboard listener to .window or .document (e.g., @keydown.window.escape="closeModal"), the handler function will be called whenever that key combination is pressed, regardless of what part of your application is currently active or visible.
If you have multiple components that could potentially react to the same global key press (e.g., multiple modals that all listen for Escape), you must ensure your component's logic correctly determines if it *should* act. Typically, this involves checking a state variable within the handler: if (this.isModalOpen) { this.closeModal(); }. Otherwise, all listening components might try to act simultaneously.
Many key combinations (e.g., Ctrl+S/Cmd+S for Save, Ctrl+R/Cmd+R for Reload, F5 for Refresh, Alt+LeftArrow for Back) are standard browser or operating system shortcuts. While AlpineJS allows you to use the .prevent modifier (e.g., @keydown.ctrl.s.prevent="myCustomSave") to try and override the default browser action, this can be unreliable or lead to a frustrating user experience if they expect standard behavior.
It's often best to avoid overriding universally expected browser behavior unless it's absolutely core to your application's functionality (e.g., a web-based IDE overriding Ctrl+S). If you must override, clearly communicate this to your users. Always test thoroughly across different browsers and operating systems.
Try pressing Enter or Shift+Enter in the field above.
Press Escape anywhere on the page to close if open.
Press Escape to close me, or click the button / outside.
Demonstrates .space.prevent and a click handler.
Try pressing Ctrl+S (or Cmd+S on Mac). We've added .prevent, but browsers may still have their own behavior. The action log should update.
Focus the page (click the background) and try ArrowUp or ArrowDown.