In Vue 3, how do you bind a Ctrl+Enter submit shortcut on a textarea that Ctrl+Shift+Enter does not trigger, and what does .exact check?
answer
- key alias plus system modifier
- kebab-case KeyboardEvent.key names
- system keys are allowed by default
- .exact rejects unlisted system keys
basics
~10 sUse @keydown.ctrl.enter.exact="submit". .enter matches the key, .ctrl requires Ctrl held, and .exact rejects the event if any other system modifier (Shift, Alt, Meta) is also pressed; without .exact, Ctrl+Shift+Enter would fire too.
solid answer
~30 sVue's key modifiers match `KeyboardEvent.key` — aliases such as `.enter`, `.esc`, `.tab`, `.delete`, or any key name in kebab-case like `.page-down`. System modifiers `.ctrl`, `.shift`, `.alt` and `.meta` require that key to be held, but they **do not exclude** the others, so `@keydown.ctrl.enter` also fires on Ctrl+Shift+Enter. `.exact` adds the missing check: it rejects the event if any system modifier is pressed that is not listed. So `@keydown.ctrl.enter.exact="submit"` fires only for Ctrl+Enter, and Ctrl+Shift+Enter can mean something else. On macOS users expect Cmd+Enter, so bind `@keydown.meta.enter.exact` too. Prefer `keydown` here: with `keyup`, Ctrl must still be held when Enter is released.
go deeper
Recall the key aliases, the system modifiers .ctrl .shift .alt .meta, and that .exact exists for exact combinations.
Explain that system modifiers mean at least this key, and that .exact rejects unlisted system modifiers but ignores ordinary keys.
Design non-overlapping shortcuts: keydown over keyup, filters before .prevent, and a meta binding for macOS users.
Decide when template modifiers stop scaling and a central shortcut registry with conflict detection is worth owning.
## Key modifiers match KeyboardEvent.key On keyboard events (`keydown`, `keyup`, `keypress`), Vue 3 lets you filter by key with a modifier. The runtime compares the modifier with the event's `key` converted to kebab-case: - **Aliases** for common keys: `.enter`, `.tab`, `.delete` (matches both Delete and Backspace), `.esc`, `.space`, `.up`, `.down`, `.left`, `.right`. - **Any other key name** from `KeyboardEvent.key`, written in kebab-case: `@keyup.page-down` fires when `event.key` is `'PageDown'`. Numeric key codes (`@keyup.13`) were a Vue 2 feature; Vue 3 matches key names. ## System modifiers are "at least", not "exactly" `.ctrl`, `.shift`, `.alt` and `.meta` work on keyboard and mouse events. Each one is a guard that lets the handler run only if that key is held. The important detail is what they do **not** check: | Binding | Ctrl+Enter | Ctrl+Shift+Enter | Enter alone | |---|---|---|---| | `@keydown.enter` | fires | fires | fires | | `@keydown.ctrl.enter` | fires | **fires** | no | | `@keydown.ctrl.enter.exact` | fires | no | no | | `@keydown.enter.exact` | no | no | fires | `@keydown.ctrl.enter` fires for any combination that *includes* Ctrl. That is the Vue guide's own example: `@click.ctrl` "will fire even if Alt or Shift is also pressed". ## What .exact checks `.exact` looks at the four system modifiers — Ctrl, Shift, Alt, Meta — and rejects the event if any of them is held **without being listed** on the binding. It does not care about ordinary keys. So: - `@keydown.ctrl.enter.exact` — Ctrl must be held, and Shift, Alt and Meta must not be. - `@click.exact` alone — fires only when **no** system modifier is held, a clean way to separate a plain click from Ctrl-click or Shift-click selection in a list. ## The comment box shortcut A comment editor wants Enter to insert a newline, Ctrl+Enter to post, and Ctrl+Shift+Enter to post and close. On macOS the convention is Cmd, which is the `meta` key. ```vue <script setup lang="ts"> import { ref } from 'vue' const body = ref('') function post(close = false) { if (!body.value.trim()) return console.log('post', body.value, { close }) body.value = '' } </script> <template> <textarea v-model="body" @keydown.ctrl.enter.exact.prevent="post()" @keydown.meta.enter.exact.prevent="post()" @keydown.ctrl.shift.enter.exact.prevent="post(true)" /> </template> ``` Why each piece is there: 1. `keydown`, not `keyup`: the guide notes that with `keyup`, a system modifier must still be held when the event fires — `keyup.ctrl` triggers only if you release a key while holding Ctrl, not when you release Ctrl itself. Users often release both at once, so `keyup` shortcuts feel unreliable. 2. `.prevent` comes **after** the filters, so only the matching combination has its default (the newline) cancelled; a plain Enter still inserts a line break. 3. `.exact` on each binding keeps the combinations from overlapping; without it, Ctrl+Shift+Enter would run both `post()` and `post(true)`. ## Edge cases - `.left` and `.right` mean **arrow keys** on keyboard events and **mouse buttons** on mouse events; the compiler decides by the event name. - Key modifiers only filter keyboard events. On a static `click` listener the compiler drops a key modifier such as `.enter` entirely, so the handler runs for every click. - A `meta` binding means different keys on different platforms: Command on macOS keyboards, the Windows key on Windows keyboards.
- In Vue 3, why can @keyup.ctrl.enter feel unreliable compared with @keydown.ctrl.enter?With `keyup`, the Ctrl check runs when Enter is released, so Ctrl must still be held at that moment. Users who let go of both keys together, or Ctrl first, miss the shortcut. `keydown` fires while the combination is being pressed, which matches what users expect.
- In Vue 3, how do you run a list item's click handler only for a plain click, not Ctrl-click or Shift-click?Use `@click.exact="select"`. With no system modifiers listed, `.exact` rejects the event if Ctrl, Shift, Alt or Meta is held, leaving those combinations free for `@click.ctrl.exact` and `@click.shift.exact` handlers such as toggling or range selection.
- In Vue 3, what does the .delete key modifier match?Both the Delete and the Backspace keys. It is one of Vue's key aliases; the runtime matches `event.key` against the alias and its mapped name, so a single `@keydown.delete` covers both keys users treat as delete.
saying these in an interview costs you the question
- @keydown.ctrl.enter fires only when Ctrl is the sole modifier held.
- .exact requires that no other keys at all are pressed, letters included.
- Vue 3 key modifiers match numeric key codes like .13.
- keyup.ctrl fires when the Ctrl key itself is released.
- Putting .prevent first is fine because the key filter still applies to it.