Skip to content
SiteEmail

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.
modegamepad | keyboardkeyboardSets the mode for the input. Use gamepad in order to accept and display gamepad buttons.
glyphOverridesPartial<Record<GamepadBindingCode, Component<any> | JSX.Element>>{}Object of custom display glyph overrides. Include only the gamepad values you want to change (e.g., 0, 1, left.joystick). See Glyphs Object

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. If the mode prop is set to gamepad the value must match any of the values or aliases from the Glyph Object
onActionRecord<string, (scope?: string, ...args: any[]) => void>undefinedAllows you to add custom navigation action handlers to the keybind. See Implemented Navigation Actions for details.
anchorstring | HTMLElementundefinedLinks navigation to another element. When the anchor element is focused, the button’s actions will execute. Can be a CSS selector or HTMLElement.

The Keybind component implements the following navigation actions by default:

Action NameBehavior
selectBegin listening for gamepad input

You can extend the Keybind with additional navigation actions or override the default behavior using the onAction prop:

import Keybind from '@components/Basic/Keybinds/Keybind';
<Keybind action={'forward'} value={'xbox.a'} onAction={{'back': () => console.log('custom action')}}/>

For more information about navigation actions, see the Navigation component documentation.

With the introduction of the Navigation component, the Keybinds component now supports gamepad input as well.

To enable gamepad support, set the mode prop to gamepad. When in gamepad mode, the component will listen for gamepad button presses instead of keyboard/mouse input.

Internally, the gamepad buttons are identified and stored by the Gamepad API standard button indices, while the axes are identified by custom aliases (e.g., left.joystick.up, right.joystick.left, etc.). See the Glyphs object table for the full list of supported gamepad button codes and axis aliases.

For easier readability, you can use the same button codes and axis aliases used by the Interaction Manager Gamepad Class. These will resolve to the correct button indices internally.

In order for the Keybind component to listen for a gamepad button press, the Keybind component must be focused via the Navigation component system. The easiest way is to wrap the Keybind components with a Navigation.Area component.

import Keybinds from '@components/Basic/Keybinds/Keybinds';
import Keybind from '@components/Basic/Keybinds/Keybind';
import Navigation from '@components/Utility/Navigation/Navigation';
const App = () => {
return (
<Keybinds mode="gamepad">
<Navigation.Area name="keybinds" focused>
<Keybind action="forward" value="xbox.d-pad-up" />
<Keybind action="backward" value="xbox.d-pad-down" />
<Keybind action="left" value="xbox.d-pad-left" />
<Keybind action="right" value="xbox.d-pad-right" />
<Keybind action="jump" value="xbox.a" />
</Navigation.Area>
</Keybinds>
);
};
export default App;

When displaying the bound keys, the component will render the corresponding glyphs for the gamepad buttons. The default glyphs are based on the standard Xbox controller layout and are displayed by the Icon component.

You can override the default glyphs by providing a custom glyphOverrides prop to the Keybinds component and providing your own components or elements for the desired buttons.

import Keybinds from '@components/Basic/Keybinds/Keybinds';
import Keybind from '@components/Basic/Keybinds/Keybind';
const App = () => {
const GLYPHS: GlyphOverrides = {
'0': Icon.gamepad.ps5.cross,
'1': Icon.gamepad.ps5.circle,
'2': <Icon.gamepad.ps5.square style={{width: '5vmax', height: '5vmax'}} />,
'3': <img src={customGlyphIcon} />,
}
return (
<Keybinds mode="gamepad" glyphOverrides={GLYPHS}>
<Keybind action="forward" value="xbox.d-pad-up" />
<Keybind action="backward" value="xbox.d-pad-down" />
<Keybind action="left" value="xbox.d-pad-left" />
<Keybind action="right" value="xbox.d-pad-right" />
<Keybind action="jump" value="xbox.a" />
</Keybinds>
);
};
export default App;

Now the Keybind component with xbox.a value will display the ps5 cross glyph instead of the default xbox a glyph.

If you wish to provide custom styling to the displayed glyphs such as changing the width/height of the displayed image, you can do so by overriding the glyph via the glyphOverrides prop and providing your own component or element with the desired styles.

For example, to keep the default xbox glyphs but change their size, you can do the following:

index.css
.gamepad-glyph {
width: 5vmax;
height: 5vmax;
}
const GLYPHS: GlyphOverrides = {
'0': <Icon.gamepad.xbox.a class="gamepad-glyph" />,
'1': <Icon.gamepad.xbox.b class="gamepad-glyph" />,
'2': <Icon.gamepad.xbox.x class="gamepad-glyph" />,
'3': <Icon.gamepad.xbox.y class="gamepad-glyph" />,
}

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

The Keybinds component comes with a default glyphs object that maps gamepad button codes to their corresponding glyph components for display purposes.

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

You can override the glyphs you want to display custom Components or HTMLElements by providing a custom glyphOverrides prop to the Keybinds component. Keep in mind that you should use the exact same gamepad button codes and axis aliases as specified below.

CodeStandard XboxStandard PlayStation
0ACross
1BCircle
2XSquare
3YTriangle
4LB (Bumper)L1
5RB (Bumper)R1
6LT (Trigger)L2
7RT (Trigger)R2
8View (Back)Create/Share
9Menu (Start)Options
10Left Stick Press (L3)L3
11Right Stick Press (R3)R3
12D-Pad UpD-Pad Up
13D-Pad DownD-Pad Down
14D-Pad LeftD-Pad Left
15D-Pad RightD-Pad Right
16Guide / SharePS Button
CodeDescription
left.joystickThe entire Left Analog Stick
left.joystick.upLeft Stick Up direction
left.joystick.downLeft Stick Down direction
left.joystick.leftLeft Stick Left direction
left.joystick.rightLeft Stick Right direction
right.joystickThe entire Right Analog Stick
right.joystick.upRight Stick Up direction
right.joystick.downRight Stick Down direction
right.joystick.leftRight Stick Left direction
right.joystick.rightRight Stick Right direction