Skip to content

Keybinds

The Keybinds component is an easy way to set up keyboard + mouse binding logic in your UI. It works together with the Keybind component, which represents a single action slot. Keybinds provides context, conflict handling, and programmatic control.

Wrap all your Keybind items in a Keybinds component. Each Keybind needs an action and can optionally get an initial value.

import Keybinds from '@components/Basic/Keybinds/Keybinds';
import Keybind from '@components/Basic/Keybinds/Keybind';
const App = () => {
return (
<Keybinds>
<Keybind action="forward" value="W" />
<Keybind action="backward" value="S" />
<Keybind action="left" value="A" />
<Keybind action="right" value="D" />
<Keybind action="jump" />
</Keybinds>
);
};
export default App;

Another way to do it is to map over an array of objects and create a Keybind for each one:

const mappings = [
{ action: "forward", value: "W" },
{ action: "backward", value: "S" },
{ action: "left", value: "A" },
{ action: "right", value: "D" },
{ action: "jump", value: null }
];
<Keybinds>
<For each={mappings}>
{(mapping) => <Keybind action={mapping.action} value={mapping.value} />}
</For>
</Keybinds>

You can also provide an initial bindings map to the Keybinds component via the defaults prop. This is useful if you want to load presets.

const mappings = {
forward: "W",
backward: "S",
left: "A",
right: "D",
jump: null
};
<Keybinds defaults={mappings}>
<For each={Object.keys(mappings)}>
{(action) => <Keybind action={action} />}
</For>
</Keybinds>
Prop nameTypeDefaultDescription
defaultsRecord<string, string | null>undefinedInitial bindings map: { action: label }. If omitted, children can seed themselves via their value.
placeholderstringundefinedText shown in each Keybind when no key is bound.
listeningTextstringPress any key...Text to show while waiting for input.
overridesPartial<Record<BindingCode, string>>{}Object of custom key overrides. Include only the codes you want to change (e.g., KeyW, ArrowLeft, Digit1). See Mappings
conflictPolicy'block' | 'replace-existing' | 'swap' | 'allow-duplicates''allow-duplicates'How to resolve when a new binding collides with an existing one (see below).
ref(ref: KeybindsRef) => voidundefinedExposes programmatic methods and the current bindings.
onConflict(action: string, key: string | null, conflictAction: string) => voidundefinedCalled when a conflict happens under any policy.
onChange(prev: string | null, next: string | null, action: string) => voidundefinedCalled when a bind or unbind operation succeeds.

To interact with the Keybinds programmatically, you can use the KeybindsRef interface. This interface provides properties and methods to access and manipulate the component’s state.

PropertyTypeDescription
bindingsRecord<string, string | null>The current state of the bindings map. Use to retrieve the up-to-date bindings object.
MethodParametersReturn ValueDescription
bind(action: string, newKey: string | null)voidProgrammatically bind an action. Applies the current conflictPolicy.
unbindKey(key: string | null)voidUnbinds all actions that currently use this key (no-op if null).
mapBindings(bindings: Record<string, string | null>)voidBulk-apply bindings (clears existing, then binds each). Conflict policy still applies per entry.
clearAll()voidClears all keys by setting every action’s value to null.
reset()voidRestores to the original baseline captured on mount: defaults if provided, otherwise whatever the children had seeded initially.

When a user tries to bind a key that is already in use, one of the following policies will be applied:

  • block: The new binding is rejected, and the existing one remains unchanged. It displays a console warn.
  • replace-existing: The new binding replaces the existing one, unbinding the previous action.
  • swap: The new binding takes the place of the existing one, and the existing action is assigned to the key that was previously bound to the new action.
  • allow-duplicates: Both actions are allowed to share the same key.

All policies will trigger the onConflict callback if provided to the component.

The Keybind component represents a single action slot. It must be a child of the Keybinds component. When clicked, it starts to listen for the user to press a key or mouse button to bind the associated action.

Prop nameTypeDefaultDescription
actionstringundefinedThe action that will have a key bound to it.
valuestring | undefinedundefinedThe value associated with the keybind by default. If omitted, it will be null. The string provided must match the label from the mappings object, unless overridden.

When users change keybinds, you will likely want to save the new bindings map somewhere. You can do this by accessing the bindings property. When accessed it will return the up to date object with the currently set bindings.

To achieve this, you need to use a ref:

  1. Create a ref variable with the type KeybindsRef.
  2. Pass the ref variable to the Keybinds component via the ref prop.
  3. Access the bindings property on the ref variable whenever you need to retrieve the current bindings object.
  4. Optionally, you can use the onChange callback to be notified whenever a user successfully changes a binding.
import Keybinds, { KeybindsRef } from '@components/Basic/Keybinds/Keybinds';
import Keybind from '@components/Basic/Keybinds/Keybind';
const App = () => {
let keybindsRef!: KeybindsRef;
const retrieveBindings = (prev: string | null, next: string | null, action: string) => {
console.log(`Changed "${action}" from "${prev}" to "${next}"`);
console.log(keybindsRef.bindings);
}
return (
<Keybinds ref={keybindsRef} onChange={retrieveBindings} conflictPolicy="block">
<Keybind action={'forward'} value="W" />
<Keybind action={'backward'} value="Down" />
</Keybinds>
);
};
export default App;

You can reset all keybinds to their original state by calling the reset method on the ref. This will restore the internal state to the initial defaults if provided, otherwise whatever the children had seeded initially when first rendered.

To achieve this, you need to use a ref:

  1. Create a ref variable with the type KeybindsRef.
  2. Pass the ref variable to the Keybinds component via the ref prop.
  3. Reset the keybinds by calling the reset method on the ref variable whenever needed.
import Keybinds, { KeybindsRef } from '@components/Basic/Keybinds/Keybinds';
import Keybind from '@components/Basic/Keybinds/Keybind';
const App = () => {
let keybindsRef!: KeybindsRef;
const resetToDefaults = () => keybindsRef.reset();
return (
<Keybinds ref={keybindsRef} >
<Keybind action={'forward'} value="W" />
<Keybind action={'backward'} value="Down" />
</Keybinds>
);
};
export default App;

In this case because no defaults prop was provided, it will reset to the original values seeded by the children: forward: "W" and backward: "Down".

You can programmatically bind or unbind keys by using the bind and unbindKey methods on the ref.

To achieve this, you need to use a ref:

  1. Create a ref variable with the type KeybindsRef.
  2. Pass the ref variable to the Keybinds component via the ref prop.
  3. Rebind a key to an action by calling the bind method on the ref variable, passing the action and the new key as arguments.
  4. Unbind a key from all actions by calling the unbindKey method on the ref variable, passing the key to unbind as an argument.
import Keybinds, { KeybindsRef } from '@components/Basic/Keybinds/Keybinds';
import Keybind from '@components/Basic/Keybinds/Keybind';
const App = () => {
let keybindsRef!: KeybindsRef;
const rebindForward = () => keybindsRef.bind('forward', 'Up');
const unbindBackward = () => keybindsRef.unbindKey('Down');
return (
<>
<button onClick={rebindForward}>Rebind Forward to Up</button>
<button onClick={unbindBackward}>Unbind backward</button>
<Keybinds ref={keybindsRef} conflictPolicy="block" placeholder="Unbound">
<Keybind action={'forward'} value="W" />
<Keybind action={'backward'} value="Down" />
</Keybinds>
</>
);
};
export default App;

If operations are successful, the onChange callback will be triggered if provided.

Triggering a function when conflicts happen

Section titled “Triggering a function when conflicts happen”

You can be notified when a conflict happens by providing the onConflict callback to the Keybinds component. This can be useful if you want to display a message to the user or prompt them for an action. The onConflict callback will return the following parameters:

  • action: The action that the user is trying to bind.
  • key: The key that the user is trying to bind.
  • conflictAction: The action that is currently bound to the key.

To achieve this, you need to provide a function to the onConflict prop of the Keybinds component.

import Keybinds from '@components/Basic/Keybinds/Keybinds';
import Keybind from '@components/Basic/Keybinds/Keybind';
const App = () => {
let keybindsRef!: KeybindsRef;
const handleConflict = (action: string, key: string | null, conflictAction: string) => {
console.log(`Conflict detected! Tried to bind "${key}" to "${action}", but it's already bound to "${conflictAction}".`);
}
return (
<>
<Keybinds conflictPolicy="block" onConflict={handleConflict}>
<Keybind action={'forward'} value="W" />
<Keybind action={'backward'} value="Down" />
</Keybinds>
</>
);
};
export default App;

You can apply a preset bindings map by using the mapBindings method on the ref. This is useful if you want to allow users to switch between different keybind presets. To achieve this, you need to use a ref:

  1. Create a ref variable with the type KeybindsRef.
  2. Pass the ref variable to the Keybinds component via the ref prop.
  3. Apply a new bindings map by calling the mapBindings method on the ref variable, passing the new bindings object as an argument.
import Keybinds, { KeybindsRef } from '@components/Basic/Keybinds/Keybinds';
import Keybind from '@components/Basic/Keybinds/Keybind';
const App = () => {
let keybindsRef!: KeybindsRef;
const preset = {
forward: "Up",
backward: "Down",
left: "Left",
right: "Right",
jump: null
};
const changePreset = () => keybindsRef.mapBindings(preset);
return (
<>
<button onClick={changePreset}>Change preset</button>
<Keybinds ref={keybindsRef} conflictPolicy="block" placeholder="Unbound">
<Keybind action={'forward'} value="W" />
<Keybind action={'backward'} value="S" />
<Keybind action={'left'} value="A" />
<Keybind action={'right'} value="D" />
<Keybind action={'jump'} value="Space" />
</Keybinds>
</>
);
};
export default App;

You can see an extended example of presets in the Menu UI view located at src/views/menu/Menu.tsx.

In order to provide custom values for some keys you need to customize how keys are displayed. To achieve that, do the following.

  1. Create an object where the keys are the codes you want to override and the values are the custom labels you want to display.
  2. Pass the object to the overrides prop of the Keybinds component.
  3. Now the Keybinds component will consider you custom labels as valid display values.
import Keybinds, { KeybindsRef } from '@components/Basic/Keybinds/Keybinds';
import Keybind from '@components/Basic/Keybinds/Keybind';
const App = () => {
const overrides = {
"ArrowUp": "Up",
"ArrowDown": "Down",
"Backquote": "`",
"BracketLeft": "[",
"BracketRight": "]",
"0": "Mouse Left",
"2": "Mouse Right",
"WheelUp": "Wheel Up",
};
return (
<>
<Keybinds overrides={overrides} conflictPolicy="block" placeholder="Unbound">
<Keybind action={'forward'} value="Up" />
<Keybind action={'backward'} value="Down" />
<Keybind action={'special-action-one'} value="`" />
<Keybind action={'special-action-two'} value="[" />
<Keybind action={'special-action-three'} value="]" />
<Keybind action={'select-item'} value="Mouse Left" />
<Keybind action={'interact'} value="Mouse Right" />
<Keybind action={'view-up'} value="Wheel up" />
</Keybinds>
</>
);
};
export default App;

The Keybinds component comes with a default mapping object that translates common KeyboardEvent.code and mouse button codes to user-friendly labels.

The mappings object uses KeyboardEvent.code for keyboard mappings and MouseEvent.button for mouse button mappings.

The object is located at src/components/Basic/Keybinds/mappings.ts.

CodeLabel
KeyAA
KeyBB
KeyCC
KeyDD
KeyEE
KeyFF
KeyGG
KeyHH
KeyII
KeyJJ
KeyKK
KeyLL
KeyMM
KeyNN
KeyOO
KeyPP
KeyQQ
KeyRR
KeySS
KeyTT
KeyUU
KeyVV
KeyWW
KeyXX
KeyYY
KeyZZ
Digit00
Digit11
Digit22
Digit33
Digit44
Digit55
Digit66
Digit77
Digit88
Digit99
F1F1
F2F2
F3F3
F4F4
F5F5
F6F6
F7F7
F8F8
F9F9
F10F10
F11F11
F12F12
F13F13
F14F14
F15F15
F16F16
F17F17
F18F18
F19F19
F20F20
F21F21
F22F22
F23F23
F24F24
Numpad0Num 0
Numpad1Num 1
Numpad2Num 2
Numpad3Num 3
Numpad4Num 4
Numpad5Num 5
Numpad6Num 6
Numpad7Num 7
Numpad8Num 8
Numpad9Num 9
NumpadAdd+
NumpadSubtract-
NumpadMultiply*
NumpadDivide/
NumpadDecimal.
NumpadComma,
NumpadEnterNum Enter
NumpadEqualNum =
Backquote`
Minus-
Equal=
BracketLeft[
BracketRight]
Backslash\
Semicolon;
Quote
Comma,
Period.
Slash/
ShiftLeftL Shift
ShiftRightR Shift
ControlLeftL Ctrl
ControlRightR Ctrl
AltLeftL Alt
AltRightR Alt
MetaLeftL Meta
MetaRightR Meta
EscapeEsc
TabTab
CapsLockCaps
SpaceSpace
EnterEnter
BackspaceBack
DeleteDel
InsertIns
HomeHome
EndEnd
PageUpPgUp
PageDownPgDn
ArrowUpUp
ArrowDownDown
ArrowLeftLeft
ArrowRightRight
PrintScreenPrtSc
ScrollLockScroll
PausePause
ContextMenuMenu
NumLockNumLock
AudioVolumeMuteMute
AudioVolumeDownVol-
AudioVolumeUpVol+
MediaTrackNextNext
MediaTrackPreviousPrev
MediaStopStop
MediaPlayPausePlay/Pause
BrowserBackBrowserBack
BrowserForwardBrowserForward
BrowserRefreshBrowserRefresh
BrowserStopBrowserStop
BrowserSearchBrowserSearch
BrowserFavoritesBrowserFav
BrowserHomeBrowserHome
CodeLabel
0LMB
1MMB
2RMB
3BMB
4FMB
CodeLabel
WheelUpWheel Up
WheelDownWheel Down