---
title: Actions
created_at: 12/25/2025
updated_at: 12/29/2025
keywords:
- actions
- commands
- keyboard shortcuts
- menus
- overrides
- customization
status: published
date: 12/25/2025
readability: 8
voice: 8
completeness: 9
accuracy: 9
notes: Interface definition shows property order different from source. Helper table has slight differences from implementation details.
---
Actions are named operations that users trigger from menus, keyboard shortcuts, or custom UI. Each action bundles an identifier, display metadata (label, icon, keyboard shortcut), and a handler function. Actions let you define operations like "undo", "group", or "export as PNG" once and invoke them from multiple places with consistent behavior.
```tsx
import { Tldraw, TLUiOverrides } from 'tldraw'
import 'tldraw/tldraw.css'
const overrides: TLUiOverrides = {
actions(editor, actions, helpers) {
// Add a custom action
actions['show-selection-count'] = {
id: 'show-selection-count',
label: 'action.show-selection-count',
kbd: 'shift+c',
onSelect(source) {
const count = editor.getSelectedShapeIds().length
helpers.addToast({ title: `${count} shapes selected` })
},
}
return actions
},
}
export default function App() {
return (
)
}
```
The `tldraw` package includes nearly 100 default actions covering editing, arrangement, export, zoom, and preferences. You can override any of these or add your own through the `overrides` prop, typed as [TLUiOverrides](?).
## How actions work
Actions live in a React context inside the tldraw UI. When the UI mounts, it registers all default actions, applies any overrides you've provided, and makes them available through the [useActions](?) hook. Menus and toolbars look up actions by ID and render them with their labels, icons, and keyboard shortcuts.
Each action has an `onSelect` handler that receives a source parameter indicating where it was triggered:
```tsx
const actions = useActions()
const duplicateAction = actions['duplicate']
// Trigger programmatically
duplicateAction.onSelect('toolbar')
```
Keyboard shortcuts are bound automatically. The [useKeyboardShortcuts](?) hook parses each action's `kbd` property and registers hotkey handlers. When a shortcut fires, it calls the action's `onSelect` with `'kbd'` as the source.
## Action structure
The [TLUiActionItem](?) interface defines what an action contains:
| Property | Description |
| ---------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `id` | Unique identifier for the action (e.g., `'duplicate'`, `'zoom-in'`) |
| `label` | Translation key for display text. Can be a string or an object mapping contexts to different keys (see [Context-sensitive labels](#context-sensitive-labels)) |
| `icon` | Icon name from tldraw's icon set, or a custom React element |
| `kbd` | Keyboard shortcut string. Use commas to bind several combinations, e.g. `'cmd+g,ctrl+g'` (see [Keyboard shortcuts](#keyboard-shortcuts)) |
| `readonlyOk` | When `true`, the action works in readonly mode. Defaults to `false` |
| `checkbox` | When `true`, renders as a toggle with a checkmark indicator in menus |
| `isRequiredA11yAction` | When `true`, the keyboard shortcut works even when shortcuts are normally disabled (e.g., while editing a shape). Used for accessibility actions |
| `onSelect` | Handler called when the action is triggered. Receives a [TLUiEventSource](?) indicating the trigger origin (`'kbd'`, `'menu'`, `'toolbar'`, etc.) |
## Accessing actions
Use the [useActions](?) hook to get all registered actions:
```typescript
import { useActions } from 'tldraw'
function MyComponent() {
const actions = useActions()
return (
)
}
```
The hook returns a record mapping action IDs to action objects. You can iterate over it to build custom menus or filter actions by property.
## Default actions
The default actions cover most editing operations you'd expect in a canvas application. Some common ones, by category:
| Category | Action ids |
| ----------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| Editing | `undo`, `redo`, `duplicate`, `delete`, `copy`, `cut`, `paste` |
| Grouping | `group`, `ungroup` |
| Arrangement | `bring-to-front`, `bring-forward`, `send-backward`, `send-to-back`, `align-left`, `align-center-horizontal`, `align-right`, `distribute-horizontal`, `distribute-vertical` |
| Export | `export-as-svg`, `export-as-png`, `copy-as-svg`, `copy-as-png` |
| Zoom | `zoom-in`, `zoom-out`, `zoom-to-100`, `zoom-to-fit`, `zoom-to-selection`, `select-zoom-tool` |
| Preferences | `toggle-dark-mode`, `toggle-snap-mode`, `toggle-grid`, `toggle-focus-mode` |
The full list is in the [default actions source](https://github.com/tldraw/tldraw/blob/main/packages/tldraw/src/lib/ui/context/actions.tsx). Most actions guard themselves inside their handlers. For example, `group` and the arrangement actions do nothing unless shapes are selected and the select tool is active.
## Overriding actions
Pass an `overrides` prop to customize actions. The override function receives the editor, the default actions, and helper utilities:
```typescript
import { Tldraw, TLUiOverrides } from 'tldraw'
const overrides: TLUiOverrides = {
actions(editor, actions, helpers) {
// Modify existing action
actions['duplicate'].kbd = 'cmd+shift+d,ctrl+shift+d'
// Disable an action by removing it
delete actions['print']
return actions
},
}
function App() {
return
}
```
The `copy`, `cut`, and `paste` shortcuts are handled by native clipboard events rather than the `kbd` system, so changing their `kbd` has no effect.
### Modifying behavior
To change what an action does, replace its `onSelect` handler:
```typescript
const overrides: TLUiOverrides = {
actions(editor, actions, helpers) {
const originalDuplicate = actions['duplicate'].onSelect
actions['duplicate'].onSelect = async (source) => {
console.log('Duplicating shapes...')
await originalDuplicate(source)
console.log('Done!')
}
return actions
},
}
```
You can call the original handler before or after your custom logic, or replace it entirely.
### Adding custom actions
Add new actions by inserting them into the actions record:
```typescript
const overrides: TLUiOverrides = {
actions(editor, actions, helpers) {
actions['my-custom-action'] = {
id: 'my-custom-action',
label: 'action.my-custom-action',
kbd: 'cmd+shift+k,ctrl+shift+k',
icon: 'external-link',
onSelect(source) {
const shapes = editor.getSelectedShapes()
console.log('Custom action on', shapes.length, 'shapes')
},
}
return actions
},
}
```
Custom actions integrate with the keyboard shortcut system automatically. To add them to menus, override the menu components; see [Actions in menus](#actions-in-menus) and the [custom menus example](/examples/ui/custom-menus).
### Using helper utilities
The override function receives a `helpers` object ([TLUiOverrideHelpers](?), the return value of [useDefaultHelpers](?)):
```typescript
const overrides: TLUiOverrides = {
actions(editor, actions, helpers) {
actions['show-toast'] = {
id: 'show-toast',
label: 'action.show-toast',
onSelect(source) {
helpers.addToast({
title: 'Hello!',
description: 'This is a custom action.',
})
},
}
return actions
},
}
```
Available helpers:
| Helper | Description |
| ----------------------- | ------------------------------------ |
| `addToast` | Show a toast notification |
| `removeToast` | Remove a specific toast |
| `clearToasts` | Remove all toasts |
| `addDialog` | Open a dialog |
| `removeDialog` | Close a specific dialog |
| `clearDialogs` | Close all dialogs |
| `msg` | Get a translated string by key |
| `isMobile` | Boolean indicating mobile breakpoint |
| `insertMedia` | Open file picker and insert media |
| `replaceImage` | Replace selected image with new file |
| `replaceVideo` | Replace selected video with new file |
| `printSelectionOrPages` | Print selection or all pages |
| `cut` | Cut selected shapes to clipboard |
| `copy` | Copy selected shapes to clipboard |
| `paste` | Paste from clipboard |
| `copyAs` | Copy shapes as SVG or PNG |
| `exportAs` | Export shapes as SVG, PNG, or JSON |
| `getEmbedDefinition` | Get embed info for a URL |
## Keyboard shortcuts
Shortcuts use a simple string format with modifier keys separated by `+`. Use commas to bind several combinations to the same action. Every combination is active on every platform; the conventional `cmd+…,ctrl+…` pair covers Mac and everything else, and only the shortcut hint shown in menus ([TldrawUiKbd](?)) is platform-specific:
```typescript
kbd: 'cmd+g,ctrl+g' // Cmd+G or Ctrl+G
kbd: 'shift+1' // Shift+1
kbd: 'cmd+shift+s,ctrl+shift+s' // Cmd+Shift+S or Ctrl+Shift+S
```
Modifiers are `cmd` (alias `meta`), `ctrl`, `shift`, and `alt` (alias `option`). Special keys include `del`, `backspace`, `enter`, `escape`, `space`, and the arrow keys (`left`, `right`, `up`, `down`).
To bind the `+` key itself, double the trailing plus: `cmd++`. That matches an unshifted `+` key (German, Nordic and similar layouts, or the numpad). On layouts where `+` is shift plus another key, such as US, write `cmd+shift++`, which is the same binding as `cmd+shift+=`.
Shortcuts match the character the browser reports for the key press, not the physical keycap, with two exceptions: shifted number-row keys match by position, so write them as `shift+` on every layout, and when the reported character is non-ASCII (Cyrillic, Greek, macOS Option dead keys) matching falls back to the physical key's US QWERTY character, so `cmd+z` still works on a Russian layout. With shift held, the shifted and unshifted US spellings are the same binding, so `shift+:` and `shift+;` both fire when the browser reports `:` with shift held. Bind what the keyboard emits, not what the keycap says: on French AZERTY the `:` key emits `/` with shift held, so bind `shift+/`; on Japanese JIS the `^` key emits `~`, so bind `shift+~`. The characters `!`, `?` and `$` are reserved as legacy modifier markers and cannot be used as keys.
Shortcuts only fire while the editor is focused and the key event does not target a text input. They are also disabled when a menu is open, a shape is being edited, the editor has a crashing error, or the user has disabled keyboard shortcuts in preferences. In readonly mode, only actions with `readonlyOk` are bound. Actions marked with `isRequiredA11yAction: true` bypass the disabled check for accessibility purposes.
## Actions in menus
The default UI uses [TldrawUiMenuActionItem](?) to render actions in menus:
```typescript
import { TldrawUiMenuActionItem, TldrawUiMenuGroup } from 'tldraw'
function CustomMenu() {
return (
)
}
```
This component looks up the action by ID and renders it with the correct label, icon, and shortcut hint. Pass `disabled` yourself if the item should be disabled. For toggle actions, use [TldrawUiMenuActionCheckboxItem](?) with a `checked` prop.
## Context-sensitive labels
Some actions show different labels depending on where they appear. The `label` property can be an object mapping menu context types ([TLUiMenuContextType](?), plus `default`) to translation keys:
```typescript
actions['export-as-svg'] = {
id: 'export-as-svg',
label: {
default: 'action.export-as-svg',
menu: 'action.export-as-svg.short',
'context-menu': 'action.export-as-svg.short',
},
// ...
}
```
The menu component uses the appropriate label based on its context. If no specific label exists for a context, it falls back to `default`.
## Tracking action usage
The `source` parameter tells you where the action was triggered. Use this for analytics:
```tsx
actions['custom-action'] = {
id: 'custom-action',
label: 'action.custom',
kbd: 'cmd+k,ctrl+k',
onSelect(source) {
trackEvent('custom-action', { source })
// source: 'kbd', 'menu', 'context-menu', 'toolbar', 'quick-actions', 'zoom-menu', etc.
},
}
```
## Related examples
- [Action overrides](/examples/ui/action-overrides) - Add custom actions and modify existing action shortcuts using the overrides prop.
- [Keyboard shortcuts](/examples/ui/keyboard-shortcuts) - Change keyboard shortcuts for tools and actions.
- [Custom menus](/examples/ui/custom-menus) - Build custom menus that use actions with proper labels and shortcuts.