Actions
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/tldraw package includes about 80 default actions covering editing, arrangement, export, zoom, and preferences. You can override any of these or add your own through the overrides prop.
How actions work
Actions live in a React context provided by ActionsProvider. 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.
Action structure
The TLUiActionItem interface defines what an action contains:
interface TLUiActionItem {
id: string
label?: string | { [key: string]: string }
icon?: string | React.ReactElement
kbd?: string
readonlyOk?: boolean
checkbox?: boolean
isRequiredA11yAction?: boolean
onSelect(source: TLUiEventSource): Promise<void> | void
}| 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 separate platform alternatives: 'cmd+g,ctrl+g' binds Cmd+G on Mac and Ctrl+G elsewhere |
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 source parameter indicating the trigger origin ('kbd', 'menu', 'toolbar', etc.) |
Accessing actions
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.
Default actions
The default actions cover most editing operations you'd expect in a canvas application. Here are some common categories:
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
Each action includes appropriate guards. For example, group checks that multiple shapes are selected before enabling, and arrangement actions verify that shapes can be reordered.
Overriding actions
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} />
}Modifying behavior
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.
Adding custom actions
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, you'll also need to override the menu components.
Using helper utilities
The override function receives a helpers object with useful utilities:
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 specify alternatives for different platforms:
kbd: 'cmd+g,ctrl+g' // Cmd+G on Mac, Ctrl+G elsewhere
kbd: 'shift+1' // Shift+1 on all platforms
kbd: 'cmd+shift+s,ctrl+shift+s' // Cmd+Shift+S on Mac, Ctrl+Shift+S elsewhereAvailable modifiers are cmd, ctrl, shift, and alt. Special keys include del, backspace, enter, escape, and arrow keys.
Shortcuts are 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. Actions marked with isRequiredA11yAction: true bypass this check for accessibility purposes.
Actions in menus
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, shortcut hint, and disabled state. For toggle actions, use TldrawUiMenuActionCheckboxItem which displays a checkmark when active.
Context-sensitive labels
Some actions show different labels depending on where they appear. The label property can be an object mapping context names 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.
Tracking action usage
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.
},
}Related examples
- Action overrides — Add custom actions and modify existing action shortcuts using the overrides prop.
- Keyboard shortcuts — Change keyboard shortcuts for tools and actions.
- Custom menus — Build custom menus that use actions with proper labels and shortcuts.