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
}
PropertyDescription
idUnique identifier for the action (e.g., 'duplicate', 'zoom-in')
labelTranslation key for display text. Can be a string or an object mapping contexts to different keys (see Context-sensitive labels)
iconIcon name from tldraw's icon set, or a custom React element
kbdKeyboard shortcut string. Use commas to separate platform alternatives: 'cmd+g,ctrl+g' binds Cmd+G on Mac and Ctrl+G elsewhere
readonlyOkWhen true, the action works in readonly mode. Defaults to false
checkboxWhen true, renders as a toggle with a checkmark indicator in menus
isRequiredA11yActionWhen true, the keyboard shortcut works even when shortcuts are normally disabled (e.g., while editing a shape). Used for accessibility actions
onSelectHandler 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:

HelperDescription
addToastShow a toast notification
removeToastRemove a specific toast
clearToastsRemove all toasts
addDialogOpen a dialog
removeDialogClose a specific dialog
clearDialogsClose all dialogs
msgGet a translated string by key
isMobileBoolean indicating mobile breakpoint
insertMediaOpen file picker and insert media
replaceImageReplace selected image with new file
replaceVideoReplace selected video with new file
printSelectionOrPagesPrint selection or all pages
cutCut selected shapes to clipboard
copyCopy selected shapes to clipboard
pastePaste from clipboard
copyAsCopy shapes as SVG or PNG
exportAsExport shapes as SVG, PNG, or JSON
getEmbedDefinitionGet 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 elsewhere

Available 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.
	},
}
  • 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.
Prev
Accessibility
Next
Animation