Back to Tldraw

Actions

apps/docs/content/sdk-features/actions.mdx

5.4.013.0 KB
Original Source

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 (
		<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.

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:

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 bind several combinations, e.g. 'cmd+g,ctrl+g' (see Keyboard shortcuts)
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 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 (
		<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. Some common ones, by category:

CategoryAction ids
Editingundo, redo, duplicate, delete, copy, cut, paste
Groupinggroup, ungroup
Arrangementbring-to-front, bring-forward, send-backward, send-to-back, align-left, align-center-horizontal, align-right, distribute-horizontal, distribute-vertical
Exportexport-as-svg, export-as-png, copy-as-svg, copy-as-png
Zoomzoom-in, zoom-out, zoom-to-100, zoom-to-fit, zoom-to-selection, select-zoom-tool
Preferencestoggle-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.

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 <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.

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 and the custom menus example.

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:

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 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).

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 (
		<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.

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.
	},
}
  • 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.