The designer ships with a set of built-in keybindings for common actions like undo, redo, duplicate, and zoom. You can listen to those shortcuts, override them, and add your own — all with full type safety.

## Default Keybindings

The `DEFAULT_KEYBINDINGS` constant exposes the built-in shortcuts. Every keybinding follows the same shape:

| Property | Type | Description |
| --- | --- | --- |
| `key` | `string` | The key combination, written in [react-hotkeys-hook](https://react-hotkeys-hook.vercel.app/) syntax (e.g. `"mod+z"`). |
| `label` | `string` | The label shown for non-Mac platforms (e.g. `"Ctrl Z"`). |
| `labelMac` | `string` | The label shown on Mac (e.g. `"⌘ Z"`). |
| `description` | `string` | Human-readable description shown in the help dialog. |
| `group` | `string` | Group name used when listing shortcuts (e.g. `"History"`). |

Built-in groups include `History`, `Layer`, and `Zoom`. Each registered layer type also adds an `ADD_LAYER_<NAME>` keybinding when its `keybinding` field is set.

## Listening to a Shortcut

Use `useShortcut` to run a callback when a keybinding fires. The hook accepts a `KeybindingName` — a typed union of all built-in names plus any custom or `ADD_LAYER_*` names you've registered:

```tsx
import { useShortcut } from "@shadcn/designer";

function ShortcutHandler() {
  useShortcut("DUPLICATE_LAYER", () => {
    console.log("Duplicate pressed");
  });

  useShortcut("DELETE_LAYER", () => {
    console.log("Delete pressed");
  });

  return null;
}
```

If you mistype the name, TypeScript reports it instead of silently failing at runtime.

## Adding Custom Keybindings

You can extend or override the defaults in three steps:

### 1. Define your keybindings

Define your app's keybindings in a single file. Use `satisfies Record<string, Keybinding>` to keep the literal keys narrow:

```ts
import { DEFAULT_KEYBINDINGS, type Keybinding } from "@shadcn/designer";

export const keybindings = {
  ...DEFAULT_KEYBINDINGS,
  TOGGLE_PANEL_LEFT: {
    key: "mod+b",
    label: "Ctrl B",
    labelMac: "⌘ B",
    description: "Toggle left panel",
    group: "Panels",
  },
  TOGGLE_PANEL_RIGHT: {
    key: "mod+/",
    label: "Ctrl /",
    labelMac: "⌘ /",
    description: "Toggle right panel",
    group: "Panels",
  },
} satisfies Record<string, Keybinding>;
```

### 2. Pass them to the Designer

Pass your keybindings to `<Designer />` so the runtime store knows about them:

```tsx
import { Designer } from "@shadcn/designer";
import { keybindings } from "./keybindings";

function MyDesigner() {
  return (
    <Designer keybindings={keybindings}>
      {/* ... */}
    </Designer>
  );
}
```

### 3. Register them with TypeScript

Augment the `DesignerKeybindings` interface with `InferKeybindings` so `useShortcut` and `KeybindingName` know about your custom names:

```tsx
import type { InferKeybindings } from "@shadcn/designer";
import type { keybindings } from "./keybindings";

declare module "@shadcn/designer" {
  // eslint-disable-next-line @typescript-eslint/no-empty-object-type
  interface DesignerKeybindings extends InferKeybindings<typeof keybindings> {}
}
```

See [Type Safety](/docs/concepts/type-safety) for more on the registry pattern.

After registration, your custom names autocomplete and type-check:

```tsx
useShortcut("TOGGLE_PANEL_LEFT", () => {
  setLeftPanelOpen((open) => !open);
});
```

## Listing All Keybindings

Use `useKeybindings` to get the merged map of built-in, custom, and `ADD_LAYER_*` keybindings. This is useful for building a help dialog:

```tsx
import { type Keybinding, useKeybindings } from "@shadcn/designer";
import { useIsMac } from "@shadcn/designer/hooks";
import { useMemo } from "react";

export function KeyboardShortcuts() {
  const keybindings = useKeybindings();
  const isMac = useIsMac();

  const groups = useMemo(() => {
    return Object.values(keybindings).reduce(
      (acc, keybinding) => {
        acc[keybinding.group] = [...(acc[keybinding.group] ?? []), keybinding];
        return acc;
      },
      {} as Record<string, Keybinding[]>
    );
  }, [keybindings]);

  return (
    <div className="grid gap-6">
      {Object.entries(groups).map(([group, items]) => (
        <div key={group}>
          <h3 className="text-muted-foreground">{group}</h3>
          <ul className="grid gap-2">
            {items.map((keybinding) => (
              <li
                key={keybinding.key}
                className="flex items-center justify-between"
              >
                <span>{keybinding.description}</span>
                <span>{isMac ? keybinding.labelMac : keybinding.label}</span>
              </li>
            ))}
          </ul>
        </div>
      ))}
    </div>
  );
}
```

Group rows by `keybinding.group` to render sections, then pick the platform-appropriate label using `useIsMac`.

## Layer Keybindings

Layer types can declare their own keybinding alongside the layer definition. The designer registers it as `ADD_LAYER_<TYPE>` and wires it up to add a new layer when pressed:

```tsx
createLayerType({
  type: "shape",
  name: "Shape",
  keybinding: {
    key: "s",
    label: "S",
    labelMac: "S",
    description: "Add shape",
    group: "New Layer",
  },
  // ...
});
```

After registering the layer type, `useShortcut("ADD_LAYER_SHAPE", ...)` becomes a valid call and the entry appears in the merged map returned by `useKeybindings`.

## Next Steps

- See [Type Safety](/docs/concepts/type-safety) for more on the module augmentation pattern.
- Browse the [hooks reference](/docs/reference/hooks) for `useShortcut` and `useKeybindings`.