---
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 1Option 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
OptionsEditDuplicateDelete
```
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