> ## Documentation Index
> Fetch the complete documentation index at: https://docs.overlayed.gg/llms.txt
> Use this file to discover all available pages before exploring further.

# Show/Hide Window Keybinds

> Set up keybinds to toggle showing and hiding your overlay windows.

One of the most common overlay patterns is a window the player summons on demand: press a key to open your HUD, hold a
key to peek at a scoreboard. This guide walks through wiring a keybind to a window's visibility, end to end.

<Info>
  This guide covers the show/hide workflow specifically. For the full keybinds API reference, see [Setup
  Keybinds](/keybinds/introduction).
</Info>

## How Keybinds Work

Before writing any code, it helps to know three things about how Overlayed handles keybinds:

1. **Keybinds are defined at setup time.** You declare every keybind (with a default key combination) in your
   `overlayed()` config. You can change a keybind's `keys` and `mode` at runtime, but you cannot add brand new keybind
   names after setup.
2. **Key presses are captured by Overlayed's native keyboard hook.** Keybinds fire while the game is focused and
   fullscreen - you don't need Electron's `globalShortcut`, and there's nothing to register with the OS.
3. **User changes persist automatically.** The config you pass in is only the *default*. When your code calls
   `updateKeybind` (typically from your keybind settings UI), the new binding is saved to disk and used from then on.

## Wiring a Keybind to a Window

<Steps>
  <Step title="Define the keybind">
    Add a keybind to your `overlayed()` setup. The name (`toggleHud` here) is yours to choose - you'll use it to
    listen for the keybind later.

    ```typescript main.ts theme={null}
    export const overlay = overlayed({
    	keybinds: {
    		toggleHud: {
    			keys: ["AltLeft", "KeyO"],
    			mode: "toggle",
    		},
    	},
    });
    ```

    * `keys` is an array of [KeyboardEvent#code](https://developer.mozilla.org/en-US/docs/Web/API/KeyboardEvent/code)
      values - `"KeyO"`, not `"o"`. Every entry must be held at once, so `["AltLeft", "KeyO"]` means
      <kbd>Alt</kbd> + <kbd>O</kbd>. Use [keycode.info](https://www.toptal.com/developers/keycode) to look up codes.
    * `mode` is `"toggle"` (press once to fire) or `"hold"` (fires on press *and* release).

    <Warning>
      Key codes are case-sensitive, and left/right modifiers are distinct codes. There is no generic `"Ctrl"` -
      use `"ControlLeft"` or `"ControlRight"`. Binding to `"AltLeft"` will not trigger when the user presses the
      right Alt key.
    </Warning>
  </Step>

  <Step title="Create the window">
    Create the window you want to control. For a window rendered inside the game, use `createInGameWindow`:

    ```typescript theme={null}
    const hudWindow = overlay.windows.createInGameWindow({
    	width: 400,
    	height: 300,
    	show: false, // start hidden, let the keybind reveal it
    });
    ```

    See [Creating Windows](/windows/introduction) for the difference between in-game and out-of-game windows.
  </Step>

  <Step title="Listen and toggle visibility">
    Attach a listener to the keybind and flip the window's visibility:

    ```typescript theme={null}
    overlay.keybinds.toggleHud.on("toggle", () => {
    	hudWindow.toggleVisibility();
    });
    ```

    That's it - pressing <kbd>Alt</kbd> + <kbd>O</kbd> in-game now shows the window, and pressing it again hides it.
  </Step>
</Steps>

## Toggle vs. Hold

Which mode you pick changes which events you should listen to. A toggled HUD listens to `toggle`; a hold-to-peek
scoreboard listens to `down` and `up`.

<Tabs>
  <Tab title="Toggle mode">
    The `toggle` event fires once per full key press. The user must release the keys before the keybind can fire
    again - holding the combination down does not re-trigger it.

    ```typescript theme={null}
    export const overlay = overlayed({
    	keybinds: {
    		toggleHud: {
    			keys: ["AltLeft", "KeyO"],
    			mode: "toggle",
    		},
    	},
    });

    overlay.keybinds.toggleHud.on("toggle", () => {
    	hudWindow.toggleVisibility();
    });
    ```
  </Tab>

  <Tab title="Hold mode">
    The `down` event fires when the full combination is pressed, and `up` fires as soon as any key in it is
    released - perfect for "peek" windows like scoreboards.

    ```typescript theme={null}
    export const overlay = overlayed({
    	keybinds: {
    		showScoreboard: {
    			keys: ["Backquote"],
    			mode: "hold",
    		},
    	},
    });

    overlay.keybinds.showScoreboard.on("down", () => {
    	scoreboardWindow.show();
    });

    overlay.keybinds.showScoreboard.on("up", () => {
    	scoreboardWindow.hide();
    });
    ```
  </Tab>
</Tabs>

<Tip>
  If you let users change a keybind's `mode` in your settings UI, register listeners for all three events (`toggle`,
  `down`, and `up`) so the keybind keeps working in either mode.
</Tip>

## Conditionally Blocking the Keybind

Sometimes showing the window doesn't make sense - for example, a scoreboard when the user isn't in a match. Return a
string (the rejection reason) from a `toggle` or `down` listener to block it:

```typescript theme={null}
overlay.keybinds.showScoreboard.on("down", () => {
	if (!siegeState.match) {
		return "Not in a match";
	}

	scoreboardWindow.show();
});
```

Returning a string marks the press as rejected - Overlayed logs the reason, and for `hold` keybinds the matching `up`
event won't fire.

## Letting Users Rebind Keys

The keys you configure are just defaults. To support user customization, build a settings UI on top of these methods:

```typescript theme={null}
// Read the current (possibly user-modified) bindings to display in your UI
const config = overlay.keybinds.getConfig();

// Persist a new binding - saved to disk automatically
overlay.keybinds.updateKeybind("toggleHud", {
	keys: ["ControlLeft", "KeyH"],
	mode: "toggle",
});
```

<Warning>
  While the user is recording a new key combination, call `overlay.keybinds.pauseKeybindListening()` first and
  `overlay.keybinds.resumeKeybindListening()` after. Otherwise, pressing keys during recording can trigger existing
  keybinds - like hiding the very settings window they're typing into.
</Warning>

```typescript theme={null}
overlay.keybinds.pauseKeybindListening();
const newKeys = await recordKeysFromUser();
overlay.keybinds.resumeKeybindListening();

overlay.keybinds.updateKeybind("toggleHud", { keys: newKeys, mode: "toggle" });
```

## FAQ

<AccordionGroup>
  <Accordion title="Do keybinds work while the game is fullscreen and focused?">
    Yes. Keybinds are captured through Overlayed's native keyboard hook in the game, so they fire while the game has
    focus - including exclusive fullscreen. The overlay window itself doesn't need focus, and it doesn't matter
    whether the window is currently hidden.
  </Accordion>

  <Accordion title="What if my keybind conflicts with a game keybind?">
    Overlayed observes key presses; it doesn't stop the game from also acting on them. Pick defaults that rarely
    clash with common game binds (e.g. <kbd>Alt</kbd>-modified combinations rather than bare letters), and give
    users a way to rebind. When users update keybinds, validate that two of your own keybinds don't end up with the
    same combination.
  </Accordion>

  <Accordion title="What happens when extra keys are held down?">
    Matching runs on every key press against the full set of currently held keys. A keybind whose keys exactly
    match that set wins; if nothing matches exactly, any keybind whose keys are all held fires - so
    `["AltLeft", "KeyX"]` triggers even if an unrelated key is also down. Be careful defining one keybind as a
    subset of another (like `["AltLeft", "KeyX"]` and `["AltLeft", "ShiftLeft", "KeyX"]`): because keys arrive one
    at a time, pressing <kbd>Alt</kbd>, then <kbd>X</kbd>, then <kbd>Shift</kbd> exactly matches the two-key bind
    mid-sequence and fires it before the three-key bind.
  </Accordion>

  <Accordion title="Can one keybind control multiple windows?">
    Yes. `on` supports multiple listeners per keybind, so several windows can react to the same press. The reverse
    also works: nothing stops two keybind names from pointing at the same window.
  </Accordion>

  <Accordion title="Where are user keybind changes stored?">
    In a `keybinds.json` file in your app's data directory. Overlayed manages it entirely - `updateKeybind` and
    `updateKeybinds` write to it, and `getConfig()` reads from it. You never need to touch the file yourself.
  </Accordion>

  <Accordion title="Do I need to unregister keybinds on quit?">
    No. Keybind listening is torn down automatically when the app quits. Since keybinds aren't OS-level global
    shortcuts, there's nothing left registered with the system after a crash either.
  </Accordion>

  <Accordion title="Can I add new keybinds at runtime?">
    No - keybind *names* are fixed at setup. You can update an existing keybind's `keys` and `mode` at runtime, but
    new keybinds require declaring them in the `overlayed()` config. Also note there is currently no migration path
    for renaming a keybind, so choose names you can live with.
  </Accordion>

  <Accordion title="My keybind isn't firing - how do I debug it?">
    Check these in order:

    1. **Key codes**: make sure you're using `KeyboardEvent#code` values (`"KeyX"`, `"Digit1"`, `"Backquote"`), not
       characters. Unknown keys are logged as errors.
    2. **Left/right modifiers**: `"AltLeft"` and `"AltRight"` are different keys.
    3. **Logs**: every trigger is logged as accepted or rejected (with your rejection reason), which tells you
       whether the keybind fired but the listener blocked it. See [Logging](/logging/introduction).
    4. **Paused listening**: verify you didn't call `pauseKeybindListening()` without a matching resume.
  </Accordion>
</AccordionGroup>

## Next Steps

<CardGroup cols={2}>
  <Card title="Keybinds Reference" icon="keyboard" href="/keybinds/introduction">
    The full keybinds API: events, updating, pausing, and rejection.
  </Card>

  <Card title="Creating Windows" icon="window" href="/windows/introduction">
    In-game vs. out-of-game windows and how to create them.
  </Card>

  <Card title="Render Windows" icon="layer-group" href="/windows/render-windows">
    Layers, input, and the extra methods on in-game windows.
  </Card>

  <Card title="Blocking Game Input" icon="hand" href="/guides/blocking-game-input">
    Prevent input from reaching the game while your window is open.
  </Card>
</CardGroup>
