--- title: UI primitives created_at: 12/25/2025 updated_at: 12/25/2025 keywords: - ui - primitives - button - icon - menu - dialog - components status: published date: 12/25/2025 readability: 9 voice: 8 completeness: 8 accuracy: 10 notes: '' --- The `tldraw` package exports a set of UI primitive components that you can use when building custom interfaces. These components match the look and feel of tldraw's default UI and integrate with the editor's theming, translations, and accessibility features. Use these primitives when you want your custom UI to feel like a natural part of tldraw rather than something bolted on. See the [UI primitives example](/examples/ui/ui-primitives) for all of them in one place. ## Buttons The button system consists of [TldrawUiButton](?) and its companion components for icons, labels, and check indicators. ### TldrawUiButton The base button component with several visual variants: ```tsx import { TldrawUiButton, TldrawUiButtonIcon, TldrawUiButtonLabel } from 'tldraw' function MyButtons() { return ( <> console.log('clicked')}> Normal console.log('clicked')}> Primary console.log('clicked')}> Danger console.log('clicked')}> ) } ``` The `type` prop controls the button's appearance: | Type | Description | | --------- | ---------------------------------------- | | `normal` | Standard button for general actions | | `primary` | Emphasized button for primary actions | | `danger` | Red button for destructive actions | | `low` | Subtle button with minimal visual weight | | `icon` | Square button sized for a single icon | | `tool` | Tool button style used in the toolbar | | `menu` | Button style used inside menus | | `help` | Style used for help/info buttons | Use `isActive` to indicate a selected or active state, and `tooltip` to show a tooltip on hover: ```tsx ``` ### Button sub-components Build up button contents using these components: ```tsx import { TldrawUiButton, TldrawUiButtonIcon, TldrawUiButtonLabel, TldrawUiButtonCheck } from 'tldraw' // Icon button // Button with icon and label Add item // Button with checkmark (for toggles in menus) Show grid ``` ## Icons [TldrawUiIcon](?) renders icons from tldraw's icon set. Icons are SVG masks filled with the current text color. ```tsx import { TldrawUiIcon } from 'tldraw' ``` The `label` prop is required for accessibility—it becomes the icon's `aria-label`. Use `small` for a smaller icon size. You can also pass a custom React element instead of an icon name: ```tsx ★} label="Favorite" /> ``` ## Menu primitives When adding items to tldraw's menus, use these components to match the default menu styling and behavior. Menu primitives read a menu context to decide how to render, so they must be placed inside one of tldraw's menus (for example a custom `MainMenu` or `ContextMenu` component) or wrapped in a [TldrawUiMenuContextProvider](?). See the [custom menus example](/examples/ui/custom-menus). ### TldrawUiMenuItem [TldrawUiMenuItem](?) is the main component for menu items. It adapts its rendering to the menu it's in (dropdown, context menu, toolbar, and so on): ```tsx import { TldrawUiMenuGroup, TldrawUiMenuItem } from 'tldraw' function MyMenuGroup() { return ( { console.log('action triggered') }} /> ) } ``` The props are: | Prop | Description | | ------------ | ------------------------------------------------------------------------------- | | `id` | Unique identifier for the menu item | | `label` | Display text (supports translation keys) | | `icon` | Icon for icon-style menus and toolbars (not shown in dropdown or context menus) | | `iconLeft` | Icon shown on the left in dropdown and context menus | | `kbd` | Keyboard shortcut to display | | `onSelect` | Called when the item is clicked | | `disabled` | Whether the item is disabled | | `readonlyOk` | If true, item is shown even in readonly mode | | `isSelected` | Whether the item shows as selected (for toolbar items) | | `spinner` | Show a loading spinner | | `noClose` | Prevent the menu from closing when clicked | ### TldrawUiMenuGroup [TldrawUiMenuGroup](?) groups related menu items together. In dropdown menus, groups are separated by dividers: ```tsx ``` ### TldrawUiMenuSubmenu [TldrawUiMenuSubmenu](?) creates a nested submenu: ```tsx ``` ### TldrawUiMenuCheckboxItem [TldrawUiMenuCheckboxItem](?) is a menu item with a checkbox: ```tsx { setSnapEnabled(!snapEnabled) }} /> ``` The `onSelect` callback receives a `source` parameter, a [TLUiEventSource](?) such as `'main-menu'` or `'context-menu'`, taken from the surrounding menu context. Ignore it if you don't need to tell sources apart. To put an existing action or tool in a menu, use [TldrawUiMenuActionItem](?) or [TldrawUiMenuToolItem](?) with the action or tool id; they fill in the label, icon, and shortcut for you. ## Dialogs Build modal dialogs using tldraw's dialog primitives ([TldrawUiDialogHeader](?), [TldrawUiDialogTitle](?), [TldrawUiDialogCloseButton](?), [TldrawUiDialogBody](?), and [TldrawUiDialogFooter](?)) and open them with [useDialogs](?). These components handle accessibility, focus management, and styling. ```tsx import { TldrawUiDialogHeader, TldrawUiDialogTitle, TldrawUiDialogCloseButton, TldrawUiDialogBody, TldrawUiDialogFooter, TldrawUiButton, TldrawUiButtonLabel, useDialogs, } from 'tldraw' function MyDialog({ onClose }: { onClose(): void }) { return ( <> Confirm deletion Are you sure you want to delete this item? This action cannot be undone. Cancel { deleteItem() onClose() }} > Delete ) } // Show the dialog using the useDialogs hook function MyComponent() { const { addDialog } = useDialogs() return } ``` The `onClose` function is passed to your dialog component automatically. Call it to dismiss the dialog. ## Input [TldrawUiInput](?) is a styled text input with built-in handling for Enter (confirm) and Escape (cancel): ```tsx { // Called when user presses Enter saveName(value) }} onCancel={(value) => { // Called when user presses Escape // Value is reset to initial value }} onValueChange={(value) => { // Called on every keystroke }} autoSelect // Select all text on focus autoFocus /> ``` Add icons to the input using `iconLeft` (left side) or `icon` (right side): ```tsx ``` ## Layout [TldrawUiRow](?), [TldrawUiColumn](?), and [TldrawUiGrid](?) organize UI controls with proper spacing and orientation-aware tooltips. ```tsx import { TldrawUiRow, TldrawUiColumn, TldrawUiGrid } from 'tldraw' // Horizontal row of buttons // Vertical column Option 1 Option 2 // 4-column grid (useful for color pickers, shape selectors, etc.) {colors.map(color => ( setColor(color)}>
))} ``` These components set up an orientation context. By default, tooltips appear below items in rows and to the right of items in columns; nested layouts inherit the side until the orientation changes. Pass `tooltipSide` to override it. ## Other primitives ### TldrawUiKbd [TldrawUiKbd](?) displays a keyboard shortcut using platform-specific symbols. It is hidden on small mobile breakpoints unless you pass `visibleOnMobileLayout`: ```tsx cmd+shift+d ``` ### TldrawUiSlider [TldrawUiSlider](?) is a slider control with discrete integer steps rather than a continuous range: ```tsx console.log(value)} /> ``` The props are: | Prop | Description | | --------------- | -------------------------------------------------------------------- | | `title` | Title text, combined with the label for the tooltip and `aria-label` | | `label` | Translation key for the label | | `value` | Current value (`min` to `steps`), or `null` | | `steps` | Maximum value | | `min` | Optional minimum value (defaults to 0) | | `onValueChange` | Called with the new value when it changes | | `onHistoryMark` | Called on pointer down so you can mark a history stopping point | ### TldrawUiPopover [TldrawUiPopover](?) shows content next to a trigger element: ```tsx
Popover content here
``` The `id` prop is required and used to track the popover's open state. The `side` prop on `TldrawUiPopoverContent` controls which side of the trigger the popover appears on. ### TldrawUiDropdownMenu [TldrawUiDropdownMenuRoot](?) and its companions build a dropdown menu on Radix UI: ```tsx Options Edit Duplicate Delete ``` The `id` prop is required on `TldrawUiDropdownMenuRoot`. Each `TldrawUiDropdownMenuItem` wraps a child element (typically a button) and handles the dropdown behavior. Other exported primitives include [TldrawUiToolbar](?) and [TldrawUiToolbarButton](?), [TldrawUiTooltip](?), [TldrawUiSelect](?), [TldrawUiContextualToolbar](?), and the dropdown submenu and checkbox item components. ## Related examples - [UI primitives](/examples/ui/ui-primitives) - A showcase of the UI primitive components - [Custom menus](/examples/ui/custom-menus) - Add items to tldraw's menus using menu primitives - [Toasts and dialogs](/examples/ui/toasts-and-dialogs) - Show toasts and custom dialogs - [Custom UI](/examples/ui/custom-ui) - Build a completely custom interface