How to implement keyboard shortcuts in SwiftUI

iOS 17+ Xcode 16+ Beginner APIs: keyboardShortcut Updated: May 12, 2026
TL;DR

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

  1. .keyboardShortcut("n", modifiers: .command) — The modifier takes a KeyEquivalent (a character literal or a named constant) and an EventModifiers option set. SwiftUI maps the combination to the button's action automatically; no UIKeyCommand setup needed.
  2. 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.
  3. Hidden background Button for non-toolbar shortcuts — Because keyboardShortcut must live on a Button, wrapping an invisible button inside .background {} is the idiomatic way to attach global shortcuts (like ⌘F) that don't correspond to a visible control.
  4. modifiers: [] — Passing an empty EventModifiers array lets you capture unmodified keys (here, Escape) without any modifier key required. Use sparingly to avoid conflicting with system gestures.
  5. .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

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

Last reviewed: 2026-05-12 by the Soarias team.