How to implement keyboard shortcuts in SwiftUI
Attach .keyboardShortcut("n", modifiers: .command) to any
Button and SwiftUI automatically registers the shortcut
whenever that view is in the responder chain — no UIKit wrangling required.
Button("New Note") {
createNote()
}
.keyboardShortcut("n", modifiers: .command)
// ⌘N triggers createNote() on any connected keyboard
Full implementation
The example below builds a notes list view with four keyboard shortcuts: create, delete, search, and dismiss.
Each shortcut is scoped to its own Button, so SwiftUI only
activates it when the button is reachable in the current view hierarchy. The
@FocusState var keeps the search field
keyboard-aware so ⌘F both focuses the field and sets the shortcut in one step.
import SwiftUI
struct Note: Identifiable {
let id = UUID()
var title: String
}
struct NotesView: View {
@State private var notes: [Note] = [
Note(title: "Grocery list"),
Note(title: "Meeting agenda"),
Note(title: "Book recommendations"),
]
@State private var selection: Note.ID? = nil
@State private var searchText = ""
@FocusState private var searchFocused: Bool
var filteredNotes: [Note] {
searchText.isEmpty
? notes
: notes.filter { $0.title.localizedCaseInsensitiveContains(searchText) }
}
var body: some View {
NavigationStack {
List(filteredNotes, selection: $selection) { note in
Text(note.title)
}
.searchable(text: $searchText)
.navigationTitle("Notes")
.toolbar {
// ⌘N — create a new note
ToolbarItem(placement: .primaryAction) {
Button("New Note", systemImage: "square.and.pencil") {
let newNote = Note(title: "Untitled")
notes.insert(newNote, at: 0)
selection = newNote.id
}
.keyboardShortcut("n", modifiers: .command)
}
// ⌘⌫ — delete the selected note
ToolbarItem(placement: .destructiveAction) {
Button("Delete", systemImage: "trash", role: .destructive) {
guard let id = selection else { return }
notes.removeAll { $0.id == id }
selection = nil
}
.keyboardShortcut(.delete, modifiers: .command)
.disabled(selection == nil)
}
}
// ⌘F — focus the search field
.background {
Button("Focus Search") { searchFocused = true }
.keyboardShortcut("f", modifiers: .command)
.hidden() // invisible; only the shortcut matters
}
// Escape — clear search text
.background {
Button("Clear Search") {
searchText = ""
searchFocused = false
}
.keyboardShortcut(.escape, modifiers: [])
.hidden()
}
}
}
}
#Preview {
NotesView()
}
How it works
-
.keyboardShortcut("n", modifiers: .command) — The modifier takes a
KeyEquivalent(a character literal or a named constant) and anEventModifiersoption set. SwiftUI maps the combination to the button's action automatically; noUIKeyCommandsetup needed. -
Named KeyEquivalents for special keys — Lines like
.keyboardShortcut(.delete, modifiers: .command)use built-in constants (.delete,.escape,.return,.space,.tab, arrow keys) instead of character literals, which avoids locale-specific surprises. -
Hidden background Button for non-toolbar shortcuts — Because
keyboardShortcutmust live on aButton, wrapping an invisible button inside.background {}is the idiomatic way to attach global shortcuts (like ⌘F) that don't correspond to a visible control. -
modifiers: [] — Passing an empty
EventModifiersarray lets you capture unmodified keys (here, Escape) without any modifier key required. Use sparingly to avoid conflicting with system gestures. - .disabled(selection == nil) — SwiftUI grays out disabled buttons and stops their shortcut from firing, so the ⌘⌫ delete shortcut is inert when nothing is selected — no guard logic needed at the shortcut level.
Variants
Multi-modifier shortcuts (⌘⇧S for Save As)
Button("Save As…") {
saveAs()
}
.keyboardShortcut("s", modifiers: [.command, .shift])
// Combine any subset of:
// .command .shift .option .control .capsLock .numericPad
Scoping shortcuts to a modal sheet
Shortcuts on buttons inside a presented sheet are automatically scoped to that sheet — they become inactive when the sheet is dismissed. This means you can safely reuse the same key combination (e.g. ⌘W) in both the root view and a sheet without conflicts. No additional scoping API is required; SwiftUI's responder chain handles it.
Common pitfalls
-
⚠️ Shortcuts only fire with a hardware keyboard. On iOS/iPadOS,
keyboardShortcuthas no effect when the software keyboard is shown — it targets external (Bluetooth/USB) keyboard users and Mac Catalyst. Don't rely on it for primary navigation on iPhone. -
⚠️ Attaching to non-Button views silently does nothing. The
.keyboardShortcutmodifier must be placed on aButton(or a view that resolves to one, like a toolbar item). Placing it on aTextorVStackcompiles fine but never triggers. Wrap in an invisibleButtonas shown above. -
⚠️ Accessibility: don't rely solely on shortcuts. Users without an external keyboard
need an alternative path. Ensure every shortcut-triggered action is also reachable via a visible button,
swipe action, or context menu — and add
.accessibilityLabelto toolbar buttons so VoiceOver announces them properly.
Prompt this with Claude Code
When using Soarias or Claude Code directly to implement this:
Implement keyboard shortcuts in SwiftUI for iOS 17+. Use keyboardShortcut with KeyEquivalent and EventModifiers. Add shortcuts for at least: create (⌘N), delete (⌘⌫), and dismiss (Escape). Make it accessible (VoiceOver labels on all toolbar buttons). Add a #Preview with realistic sample data.
In the Soarias Build phase, paste this prompt into the implementation panel after your screen mockup is approved — Claude Code will wire up the shortcuts and generate the full toolbar boilerplate in one pass.
Related
FAQ
-
Does this work on iOS 16?
keyboardShortcutwas introduced in SwiftUI 2 (iOS 14 / macOS 11), so the modifier itself compiles fine on iOS 16. However, the full responder-chain scoping andEventModifiersoption set behaviour is most reliable on iOS 17+, which is why this guide targets iOS 17. If you must support iOS 16, test thoroughly — shortcuts inside sheets in particular can behave inconsistently on older OS versions. -
Can I show the shortcut hint in a menu or tooltip?
Yes — when a
Buttonwith.keyboardShortcutappears inside a SwiftUIMenu, the OS automatically renders the key combination as a trailing hint next to the menu item (e.g. New Note ⌘N). No extra code needed. For standalone toolbar buttons, the shortcut is discoverable via the system's keyboard shortcut overlay (⌘? on iPadOS). -
What's the UIKit equivalent?
In UIKit you'd override
keyCommandson aUIViewControllerand return an array ofUIKeyCommandobjects, each with amodifierFlagsbitmask and a selector target. SwiftUI'skeyboardShortcutgenerates exactly this under the hood, saving you the boilerplate and the responder-chain plumbing.
Last reviewed: 2026-05-12 by the Soarias team.