---
title: "DroppyShortcutRecorder, DroppyKit"
description: "Droppy&#x27;s own shortcut recorder, for a droplet that keeps shortcuts of its own (DroppyKit 1.14.0)."
canonical: "https://getdroppy.app/docs/droppykit/api/droppy-shortcut-recorder"
last-updated: "2026-09-09"
---

# DroppyShortcutRecorder

Droppy's own shortcut recorder, for a droplet that keeps shortcuts of its own (DroppyKit 1.14.0).

`@MainActor struct DroppyShortcutRecorder`

The control every shortcut in Droppy's Settings records through: one native button showing the chord or "Record shortcut", and the recording bubble it opens, where the keycaps press as the keys are held, a chord that cannot be taken turns red and one that can turns green and is saved as the keys are let go, with Clear and Reset to default under the caps. The host draws it, so it is Droppy's control and not a copy.

```
DroppyShortcutRecorder(
    shortcut: $model.shortcut,
    isTaken: { model.isAlreadyBound($0) }
)
```

The bubble refuses what Droppy refuses (a bare key that types, a chord a Droppy shortcut holds) and whatever `isTaken` says is yours already. A value the droplet keeps that a `DropletKeyboardShortcut` cannot say (a double tap, say) shows as `displayTitle`. While the bubble records, `onRecordingChanged` is `true`: pause the droplet's own hotkeys then, or the chord being recorded fires them instead.

For shortcuts Droppy registers for you, use [`DropletShortcutsService`](https://getdroppy.app/docs/droppykit/api/droplet-shortcuts-service): those record on Droppy's Shortcuts page. Outside Droppy (the harness) the recorder is a disabled button with the same title.

## Initializers

### init(shortcut:displayTitle:allowsClear:width:onReset:isTaken:onRecordingChanged:)

`@MainActor init(shortcut: Binding<DropletKeyboardShortcut?>, displayTitle: String? = nil, allowsClear: Bool = true, width: CGFloat = 168, onReset: (@MainActor () -> Void)? = nil, isTaken: (@MainActor (DropletKeyboardShortcut) -> Bool)? = nil, onRecordingChanged: (@MainActor (Bool) -> Void)? = nil)`

Creates a recorder.

## Properties

### allowsClear

`let allowsClear: Bool`

Whether the bubble offers Clear.

### body

`@MainActor var body: some View { get }`

### displayTitle

`let displayTitle: String?`

Shown in place of the chord when `shortcut` is `nil` but the droplet keeps a value it cannot say as one. Clear clears it too.

### isTaken

`let isTaken: (@MainActor (DropletKeyboardShortcut) -> Bool)?`

`true` for a chord the droplet already uses elsewhere, which the bubble then refuses like one of Droppy's own.

### onRecordingChanged

`let onRecordingChanged: (@MainActor (Bool) -> Void)?`

`true` while the bubble records, `false` when it closes.

### onReset

`let onReset: (@MainActor () -> Void)?`

Restores the droplet's default; the bubble offers Reset to default when set.

### shortcut

`let shortcut: Binding<DropletKeyboardShortcut?>`

The recorded chord, or `nil` for none.

### width

`let width: CGFloat`

The button's width. Droppy's own recorders are 168 points.
