apps/docs/content/sdk-features/actions.mdx
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.
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 (
<div style={{ position: 'fixed', inset: 0 }}>
<Tldraw overrides={overrides} />
</div>
)
}
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.
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:
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.
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) |
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) |
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.) |
Use the useActions hook to get all registered actions:
import { useActions } from 'tldraw'
function MyComponent() {
const actions = useActions()
return (
<button onClick={() => actions['undo'].onSelect('toolbar')}>
Undo
</button>
)
}
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.
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. 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.
Pass an overrides prop to customize actions. The override function receives the editor, the default actions, and helper utilities:
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 <Tldraw overrides={overrides} />
}
The copy, cut, and paste shortcuts are handled by native clipboard events rather than the kbd system, so changing their kbd has no effect.
To change what an action does, replace its onSelect handler:
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.
Add new actions by inserting them into the actions record:
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 and the custom menus example.
The override function receives a helpers object (TLUiOverrideHelpers, the return value of useDefaultHelpers):
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 |
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:
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).
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.
The default UI uses TldrawUiMenuActionItem to render actions in menus:
import { TldrawUiMenuActionItem, TldrawUiMenuGroup } from 'tldraw'
function CustomMenu() {
return (
<TldrawUiMenuGroup id="edit">
<TldrawUiMenuActionItem actionId="undo" />
<TldrawUiMenuActionItem actionId="redo" />
<TldrawUiMenuActionItem actionId="duplicate" />
</TldrawUiMenuGroup>
)
}
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.
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:
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.
The source parameter tells you where the action was triggered. Use this for analytics:
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.
},
}