Skip to content

Actions

An action is what a key does. Every entry in a layer's bindings, every combo/tap-dance/mod-morph target, and every encoder direction is an action.

Actions come in two spellings — a friendly surface shorthand you write, and the explicit object form. Both are valid input; the app expands shorthand into the object form internally.

Surface shorthand

You writeMeans
"Q"A plain keypress. Keycode by friendly name, firmware alias, or canonical id.
"Ctrl+Shift+C"A modified keypress (a "combo string").
{ "type": "mod_tap", "tap": "A", "mod": "LEFT_GUI" }Mod-Tap preset → tap = key, hold = modifier.
{ "type": "layer_tap", "tap": "SPACE", "layer": "raise" }Layer-Tap preset → tap = key, hold = layer.

mod_tap and layer_tap are presets that lower to tap_hold. All keycodes are validated against the catalog (unknown names are rejected with a message) — see Keycodes & modifiers for how "Q", "Vol Up", "KC_BSPC" and "Ctrl+C" resolve.

Object form — the full catalog

Every action object has a type. Unless noted, fields not listed are absent.

Keys & tap-hold

typeFieldsDescription
key_presskey, mods?A plain (optionally modified) keypress.
tap_holdtap, hold, timingsGeneral tap/hold: tap does one thing, hold another.
mod_taptap, mod, timingsPreset: tap = key, hold = a modifier.
layer_taptap, layer, timingsPreset: tap = key, hold = a layer.
key_togglekeyPress once to latch down, again to release.
key_repeatRepeat the previously pressed key.
grave_escapeEsc normally; Shift/GUI + this sends grave/tilde.

tap target is a bare key string or a key_press object. hold target is { "type": "modifier", "modifier": … } or { "type": "layer", "layer": "<name>" }. Timings (all optional): tappingTermMs, quickTapMs, resolve (timeout | prefer-hold | prefer-tap), flavor (hold-preferred | balanced | tap-preferred | tap-unless-interrupted). Setting a timing/flavor gives ZMK a dedicated generated hold-tap node instead of the global &mt/&lt.

Layers

typeFieldsDescription
layermode, layerLayer switch. mode: momentary | toggle | to | sticky.
sticky_keykeyOne-shot key: applies to the next keypress only.
caps_wordCaps for one word.
transparentFall through to the layer below.
noneExplicitly inert — blocks fall-through.

Output routing

typeFieldsDescription
outputaction, profile?Output routing. profile is valid only with action: "bluetooth".

action: usb | bluetooth | bluetooth_clear | bluetooth_next | bluetooth_prev | bluetooth_disconnect | toggle | none. Wireless actions need a BLE-capable firmware (see capabilities).

json
{ "type": "output", "action": "bluetooth", "profile": 0 }

Lighting

typeFieldsDescription
lightingtarget, action, value fieldsControl underglow / backlight / per-key lighting.
  • target: underglow | backlight | per_key (firmware-gated — per_key is QMK/Keychron only).
  • action: toggle | on | off | brightness_up | brightness_down | hue_up | hue_down | saturation_up | saturation_down | effect_next | effect_previous | speed_up | speed_down | cycle | color | set.
  • Value-carrying actions: color sets an absolute HSB (hue 0–360, saturation/brightness 0–100); set sets an absolute brightness level 0–100.
json
{ "type": "lighting", "target": "underglow", "action": "brightness_up" }

Device, power & reset

typeFieldsDescription
bootloaderReboot into bootloader.
resetReset the keyboard.
soft_offPower off until a hardware reset / dedicated on-key.
studio_unlockUnlock the keyboard for ZMK Studio live editing.
ext_poweractionExternal/peripheral power. action: toggle | on | off.

Mouse / pointer

typeFieldsDescription
mouse_keybuttonClick a pointer button: left | right | middle | mb4 | mb5.
mouse_movedirectionMove the pointer: up | down | left | right.
mouse_scrolldirectionScroll the wheel.

References (defined at the top level)

These point at definitions elsewhere in the config — see Keymap format.

typeFieldsPoints at
macroref, param?A macros[] entry. param feeds a one-param macro.
tap_dancerefA tapDances[] entry.
mod_morphrefA modMorphs[] entry.
hold_tapref, holdParam, tapParamA holdTaps[] entry; the params feed its two inner bindings.
json
{ "type": "macro", "ref": "lock_screen" }

Action categories

The picker and JSON palette group actions into categories:

CategoryAction types
keykey_press, sticky_key, caps_word, transparent, none, key_toggle, key_repeat, grave_escape
tap-holdtap_hold, mod_tap, layer_tap
layerlayer
outputoutput
lightinglighting
mousemouse_key, mouse_move, mouse_scroll
systembootloader, reset, soft_off, studio_unlock, ext_power
macromacro, tap_dance

What each action compiles to

The compiler lowers a canonical action straight to the firmware's native token. A few representative mappings (drawn from the demo compile):

ActionZMK .keymapQMK keymap.c
"Q"&kp QKC_Q
"Ctrl+C"&kp LC(C)LCTL(KC_C)
mod_tap (plain)&mt LSHFT ALSFT_T(KC_A)
tap_hold w/ flavor/timinga generated &ht_… node (flavor = "hold-preferred")LT(…) / mod-tap macro
layer_tap&lt 1 SPACELT(1, KC_SPC)
layer momentary&mo 2MO(2)
output BT profile&bt BT_SEL 0(warn — wired QMK)
lighting underglow toggle&rgb_ug RGB_TOGRGB_TOG
lighting per_key effect-next(warn — no per-key on ZMK)RGB_MOD
comboa combo_<name> nodecombo table entry
macroa zmk,behavior-macro nodea macro
tap_dancea zmk,behavior-tap-dance nodea tap-dance entry
encoder cw/ccw&inc_dec_kp …encoder map

Plain mod_tap/layer_tap use ZMK's global &mt/&lt; adding a timing or flavor makes the compiler emit a dedicated generated hold-tap node instead. See Firmware targets for full per-target output.

Capability gating

Not every firmware supports every action. When a binding targets a firmware that lacks it (e.g. a per_key lighting action on ZMK, or a Bluetooth output on wired QMK), the compiler emits a warning and drops the binding to a no-op rather than failing. The per-firmware support table is on the Firmware targets page.

Apache-2.0. Originally forked from ZMK Studio; application layer fully rewritten.