Skip to content

Keymap format

This page walks the keymap config top to bottom. Field names and meanings are taken directly from the config type definitions. Required fields are marked · required; everything else is optional.

A complete, real example is the demo Corne shipped with Remappr (a 36-key split with home-row mods and lower/raise layers). Excerpts below are drawn from it.

Top level

jsonc
{
  "schemaVersion": 1,          // · required — fixed at 1
  "kind": "remappr.keymap",    // · required — document discriminator
  "meta": {  },               // · required
  "defaults": {  },           // optional global timings
  "keyboard": {  },           // · required — geometry + hardware
  "layers": [  ],             // · required — at least one layer
  "combos": [  ],             // optional
  "tapDances": [  ],          // optional
  "macros": [  ],             // optional
  "modMorphs": [  ],          // optional
  "holdTaps": [  ],           // optional custom hold-tap definitions
  "conditionalLayers": [  ]   // optional
}

meta

Identity of the keymap.

FieldTypeNotes
namestring · requiredDisplay name.
target"zmk" | "qmk" | "keychron" | null · requiredPinned single target, or null (builder uses keyboard.firmware[]).
authorstring
versionstring
descriptionstring
vendorIdstringUSB vendor id, hex e.g. "0xFEED".
productIdstringUSB product id, hex e.g. "0x0001".
json
"meta": {
  "name": "Mock Corne",
  "author": "remappr",
  "version": "1.0.0",
  "target": null
}

defaults

Global behavior timings, in milliseconds. Per-action overrides win over these.

FieldType
tappingTermMsnumber
quickTapMsnumber
comboTimeoutMsnumber

keyboard

The physical board: geometry, matrix, controller, hardware and lighting. Most sub-objects are documented in Hardware; the layout fields are here.

FieldTypeNotes
idstring · requiredStable slug; seeds export project/shield names.
namestring · requiredDisplay name.
keysCanonGeometry[] · requiredOne entry per physical key (see below).
encoders{ x, y }[]Standalone encoder slots (parallel-array style).
matrixobjectBoard-level rows/cols/diode/mode — see Hardware.
controllerobjectMCU identity — see Hardware.
vialobjectVial UID + unlock — see Hardware.
hardwareobjectkscan / transform / LED / power nodes — see Hardware.
pins{ rows: string[], cols: string[] }Friendly per-row/col GPIO labels (builder metadata).
firmwarestring[]Builder multi-select targets: "qmk" "via" "vial" "zmk".
lightingobjectBoard-level lighting — see Hardware.
firmwareConfigobjectModeled .conf/config.h toggles — see Hardware.
layouts{ id, name }[]Physical-layout variants; keys tag in via variant.
layoutOptions{ label, choices? }[]VIA/Vial layout options; keys tag in via option.
splitbooleanTwo-piece keyboard flag.

keyboard.keys[] — key geometry

Each key is a CanonGeometry:

FieldTypeNotes
x, ynumber · requiredPosition in key units (U).
w, hnumberWidth / height in U. Default 1.
rnumberRotation, degrees. Default 0.
rx, rynumberRotation origin.
matrix[row, col]Electrical position. Authoritative when present; else derived from geometry.
pinstringPer-key direct GPIO label (direct-pin boards).
element"encoder" | "slider"Non-switch input element; absent = a normal key.
variantstringPhysical-layout variant id this key belongs to.
option[group, choice]VIA/Vial layout-option this key is gated by.
json
"keys": [
  { "x": 0, "y": 0 },
  { "x": 1, "y": 0 },
  { "x": 2, "y": 0 }
]

Minimized configs

Defaulted fields (w/h/r = 1/1/0) are dropped when Remappr serializes, so a saved config is compact — { "x": 0, "y": 0 } is a full 1U key. See Normalization & round-trip for the full default-stripping rules; the JSON Schema treats defaulted fields as optional so minimized files validate clean.

layers

An ordered array; index 0 is the base layer. A transparent binding falls through to the layer below, so order matters.

FieldTypeNotes
namestring · requiredReferenced by name (e.g. layer-tap targets).
bindingsAction[] · requiredOne action per key, in keyboard.keys order.
descriptionstring
encoders{ cw, ccw, press? }[]Slot-indexed encoder bindings (aligned to keyboard.encoders).
encoderBindings{ [keyIndex]: { cw, ccw, press? } }Per-key encoder bindings (key has element: "encoder").
sliderBindings{ [keyIndex]: SliderBinding }Per-key slider value-mappings (key has element: "slider").
json
{
    "name": "base",
    "bindings": [
        "Q",
        "W",
        "E",
        "R",
        "T",
        { "type": "mod_tap", "tap": "A", "mod": "LEFT_GUI" },
        {
            "type": "layer_tap",
            "tap": "SPACE",
            "layer": "raise",
            "resolve": "prefer-hold"
        }
    ],
    "encoders": [{ "cw": "Volume Up", "ccw": "Volume Down" }]
}

See Actions for the full binding catalog and the friendly shorthand (bare keys, "Ctrl+C", presets).

combos

A chord: pressing several key positions together fires one action.

FieldTypeNotes
namestring · required
keysnumber[] · requiredKey positions (indices into keyboard.keys).
actionAction · requiredWhat the chord fires.
timeoutMsnumber
layersstring[]Restrict to these layers.
json
{
    "name": "esc",
    "keys": [0, 1],
    "action": "ESCAPE",
    "timeoutMs": 50,
    "layers": ["base"]
}

tapDances

Multi-tap behaviors referenced by id from a tap_dance action.

FieldTypeNotes
idstring · required
taps{ count, action }[] · requiredAction per tap count.
holdhold targetOptional hold action (modifier or layer).
tappingTermMsnumber
descriptionstring
json
{
    "id": "esc_grave",
    "taps": [
        { "count": 1, "action": "ESCAPE" },
        { "count": 2, "action": "GRAVE" }
    ],
    "hold": { "type": "modifier", "modifier": "LEFT_GUI" }
}

macros

Sequences of steps referenced by id from a macro action.

FieldTypeNotes
idstring · required
stepsMacroStep[] · requiredSee step types below.
params0 | 1 | 2Binding-cells: plain / one-param / two-param.
descriptionstring

Macro step types:

StepFieldsMeaning
tap / press / releasekeyTap, hold, or release a key.
waitmsDelay.
texttextType a literal string.
paramfrom?, to?Forward a macro argument (defaults 1→1).
tap_timemsOverride how long tapped behaviors are held.
pause_for_releaseBlock until the trigger key is released.
json
{
    "id": "lock_screen",
    "steps": [
        { "type": "press", "key": "LEFT_GUI" },
        { "type": "tap", "key": "L" },
        { "type": "release", "key": "LEFT_GUI" }
    ]
}

modMorphs

A behavior that sends one binding normally and a different one while a modifier is held. Referenced by id from a mod_morph action.

FieldTypeNotes
idstring · required
modsModifier[] · requiredModifiers that trigger the morph.
bindings[Action, Action] · required[normal, while-held].
keepModsModifier[]Modifiers passed through.
descriptionstring

holdTaps

Custom hold-tap definitions (the ZMK behavior-hold-tap), referenced by id from a hold_tap action that supplies the two inner params.

FieldTypeNotes
idstring · required
bindings[string, string] · requiredThe two inner behavior tokens (e.g. "&kp", "&mo").
flavorhold-tap flavorhold-preferred | balanced | tap-preferred | tap-unless-interrupted.
tappingTermMs, quickTapMs, requirePriorIdleMsnumberTimings.
holdTriggerKeyPositionsnumber[]Positional hold-trigger.
holdTriggerOnRelease, retroTapboolean
descriptionstring

conditionalLayers

Auto-activate a layer when a set of other layers are all active.

FieldTypeNotes
ifLayersstring[] · requiredLayer names that must all be active.
thenLayerstring · requiredLayer to activate.
json
{ "ifLayers": ["lower", "raise"], "thenLayer": "adjust" }

Next

Actions → · the full binding catalog

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