# Releases

Version: `5.2.5`

This file is generated during package publishing from the tldraw release notes content for this exact package version.

## v5.2.5

This release adds snapping and symmetric resizing while cropping images, a reactive `Editor.getIsMounted()` with a new `unmount` event, and faster viewport culling on large pages. It also fixes a batch of crashes and interaction bugs across arrows, groups, and text.

### What's new

#### Snapping and symmetric resize while cropping ([#9424](https://github.com/tldraw/tldraw/pull/9424))

When cropping an image, crop edges now snap to other shapes and the grid, with the usual snapping guidelines. Holding option resizes the crop region symmetrically from its center.

### API changes

- Add an `unmount` event to `TLEventMap` and make `Editor.getIsMounted()` a reactive value that tracks whether the editor's component is currently mounted. ([#9472](https://github.com/tldraw/tldraw/pull/9472), [#9483](https://github.com/tldraw/tldraw/pull/9483))
- Make the shape hit-test margin pointer-aware and add a `coarseHitTestMargin` option: fine pointers (mouse and trackpad) use a tighter 3px default, coarse pointers (touch and pen) use 4px. ([#9492](https://github.com/tldraw/tldraw/pull/9492))

### Improvements

- Improve viewport culling performance on large pages: the spatial index now rechecks only the shapes affected by a change instead of every shape on the page. Custom shapes whose geometry depends on unrelated shapes (outside parent/child and binding relationships) are no longer automatically rechecked for culling when those shapes change; built-in shapes and standard patterns are unaffected. ([#9093](https://github.com/tldraw/tldraw/pull/9093))
- Improve performance when deleting many shapes at once by batching page state cleanup. ([#9456](https://github.com/tldraw/tldraw/pull/9456))
- Make long toast and dialog content wrap instead of overflowing, so long strings like URLs stay within the toast or dialog. ([#9546](https://github.com/tldraw/tldraw/pull/9546))
- Delay the appearance of the "back to content" button by about a second so it no longer flickers during normal panning and zooming. Hiding stays immediate. ([#9448](https://github.com/tldraw/tldraw/pull/9448))
- Remove the unnecessary ellipsis from the "Keyboard shortcuts" menu item. ([#9570](https://github.com/tldraw/tldraw/pull/9570))

### Bug fixes

- Fix rich text headings (H1–H6) rendering with their line-height too tight, causing overlapping lines. ([#9543](https://github.com/tldraw/tldraw/pull/9543))
- Fix the style panel not highlighting the active fill correctly when the current fill lives in the overflow dropdown. ([#9509](https://github.com/tldraw/tldraw/pull/9509)) (contributed by [@meg-an31](https://github.com/meg-an31))
- Fix the style panel Shape, Line, and spline dropdown popovers so they open with the same gap as the other dropdowns. ([#9491](https://github.com/tldraw/tldraw/pull/9491))
- Fix double-clicking a shape inside an unfocused group immediately editing it; a double-click now drills one level into the group. ([#9488](https://github.com/tldraw/tldraw/pull/9488))
- Fix selected clipped arrows so they can be dragged when their geometry extends outside a clipping parent. ([#9479](https://github.com/tldraw/tldraw/pull/9479))
- Fix arrows bound to frames so they duplicate and delete with the frame without being clipped. ([#9470](https://github.com/tldraw/tldraw/pull/9470))
- Fix a shape dragged from the toolbar to a frame beneath the toolbar not being parented to the frame. ([#9458](https://github.com/tldraw/tldraw/pull/9458))
- Fix a missing `onChildrenChange` call when creating a shape directly within a parent shape. ([#9455](https://github.com/tldraw/tldraw/pull/9455))
- Fix the reactive Meta (Cmd) key state not clearing on keyup until a pointer event on macOS, which affected `getMetaKey()` and `getAccelKey()`. ([#9404](https://github.com/tldraw/tldraw/pull/9404))
- Fix a crash when dragging a handle on a line whose point keys differed from their indices. ([#9299](https://github.com/tldraw/tldraw/pull/9299))
- Fix arrow bindings floating outside a geo shape after changing its geo type; precise bindings now re-snap to the nearest point on the new shape. ([#9281](https://github.com/tldraw/tldraw/pull/9281))
- Fix an occasional crash in text measurement by iterating over grapheme clusters instead of individual characters, keeping emoji, flags, and accented characters intact. ([#9266](https://github.com/tldraw/tldraw/pull/9266))
- Fix a crash ("Shape not found") when dragging a tool from the toolbar in a read-only room. ([#9225](https://github.com/tldraw/tldraw/pull/9225))
- Fix sync clients not recovering from a malformed WebSocket message; they now restart the connection so a corrupt frame can no longer silently leave the client out of sync. ([#9380](https://github.com/tldraw/tldraw/pull/9380))
- Fix uploaded asset handling for invalid R2 object names. ([#9495](https://github.com/tldraw/tldraw/pull/9495))

## v5.2.0

[View on GitHub](https://github.com/tldraw/tldraw/releases/tag/v5.2.0)

This release brings in performance improvements across tldraw. The freehand ink algorithm now computes strokes 2–3x faster, shape resizing is quicker, and editor load time is reduced. Multi-click handling was simplified to double-click only to support improved mobile gestures. Plus it's easier to add shapes to frames, and dozens of bugs were fixed.

### What's new

#### Faster freehand ink ([#9154](https://github.com/tldraw/tldraw/pull/9154))

We rewrote the freehand ink algorithm. Draw and highlighter strokes now compute about 2x faster (up to 3x for long strokes) and produce 57% smaller SVG path data, with no visible change to how strokes look. The rewrite removes two rarely-used stroke APIs; see the breaking changes in [API changes](#api-changes).

#### Frame selection ([#8151](https://github.com/tldraw/tldraw/pull/8151))

A new "Frame selection" action wraps the selected shapes in a frame with `cmd+shift+f`. When a frame is selected, the same shortcut removes it.

### API changes

- 💥 Simplify multi-click handling to double-click only. `ClickManager` no longer tracks triple or quadruple clicks: the `triple_click` and `quadruple_click` events and the `onTripleClick` / `onQuadrupleClick` handlers have been removed, and `TLClickEventName` is now just `'double_click'`. ([#8897](https://github.com/tldraw/tldraw/pull/8897))
- 💥 Move `ShapeIndicatorOverlayUtil` and `TLShapeIndicatorOverlay` from `@tldraw/editor` to `tldraw`. Both are still exported from `tldraw`; update any imports of these symbols that came directly from `@tldraw/editor`. ([#9018](https://github.com/tldraw/tldraw/pull/9018))
- 💥 Drop support for the EOL Node 20. The minimum supported Node is now `>=22.12.0`, the first version with native `require()` of ES modules, so tldraw can depend on ESM-only packages. ([#9098](https://github.com/tldraw/tldraw/pull/9098))
- 💥 Type the collaborator user-id APIs as `TLUserId` instead of `string`. `TLInstancePresence.userId` / `followingUserId`, `TLInstance.followingUserId` / `highlightedUserIds`, `Editor.startFollowingUser()`, `Editor.zoomToUser()`, `usePresence()`, `usePeerIds()`, and the people-menu prop types now use `TLUserId`. Runtime behavior is unchanged: `TLUserId` is a branded string, so cast plain strings at the call site, for example `editor.startFollowingUser(userId as TLUserId)`. ([#9002](https://github.com/tldraw/tldraw/pull/9002))
- 💥 Remove `StrokePoint.vector` and `setStrokePointRadii` from the freehand stroke pipeline. The vector is derivable from consecutive points and radius/taper computation now happens inside `getStroke` and `svgInk`; nothing in the SDK consumed these outside the pipeline itself. ([#9154](https://github.com/tldraw/tldraw/pull/9154))
- 💥 Remove the obsolete selection-background slot from `@tldraw/editor`: `DefaultSelectionBackground`, `TLSelectionBackgroundProps`, `TLEditorComponents.SelectionBackground`, and the `useTransform` helper. These were unused and have no replacement; remove any references to them. ([#9362](https://github.com/tldraw/tldraw/pull/9362))
- Add an `embedConfig` option to `EmbedShapeUtil` for passing per-embed configuration such as API keys. The default Google Maps embed no longer reads `process.env.NEXT_PUBLIC_GC_API_KEY` — pass the key explicitly instead. ([#9068](https://github.com/tldraw/tldraw/pull/9068))

  ```ts
  EmbedShapeUtil.configure({
  	embedConfig: { google_maps: { apiKey: '...' } },
  })
  ```

- Add an `allowVideoAutoplay` option to `TldrawOptions`. When set to `false`, no video shape autoplays regardless of its per-shape `autoplay` prop — including pasted and restored shapes. Defaults to `true`. ([#8943](https://github.com/tldraw/tldraw/pull/8943))
- Add `ShapeUtil.getContentElement` and `ShapeUtil.onReleaseContentElement`, an app-owned content element lifecycle that lets stateful shape content (such as cross-origin iframes) survive unmounting the editor. ([#9167](https://github.com/tldraw/tldraw/pull/9167))

### Improvements

- Open the context menu with a right-click from any tool, not just the select tool, and with a touch long-press while using the select tool. Long-pressing on a shape-creation tool no longer leaves a stray shape behind. ([#8948](https://github.com/tldraw/tldraw/pull/8948))
- Add a `shift+q` shortcut that copies the styles of the hovered shape and applies them to the next shapes you create. ([#9028](https://github.com/tldraw/tldraw/pull/9028))
- Add a double-tap-and-drag zoom gesture for touch devices. ([#7858](https://github.com/tldraw/tldraw/pull/7858))
- As you drag out a frame, the frame tool now highlights the shapes it will enclose. ([#9102](https://github.com/tldraw/tldraw/pull/9102))
- Pasted bookmarks now show an immediate loading placeholder with a spinner at the paste location, instead of waiting for metadata before appearing. ([#8663](https://github.com/tldraw/tldraw/pull/8663))
- Add the missing keyboard shortcuts (copy as PNG, print, insert embed, highlight tool, frame selection, flatten, toggle locked, distribute horizontally/vertically) to the keyboard shortcuts dialog. ([#9285](https://github.com/tldraw/tldraw/pull/9285))
- Make the selection corner rotate handles easier to grab. ([#9374](https://github.com/tldraw/tldraw/pull/9374))
- Reduce the default indentation on rich-text lists so nested lists take up less horizontal space. ([#9287](https://github.com/tldraw/tldraw/pull/9287))
- Hide the "back to content" button during the animation that pans back to the canvas when focusing it. ([#9278](https://github.com/tldraw/tldraw/pull/9278))
- Remove the extraneous `title` tooltips on menu items. ([#9315](https://github.com/tldraw/tldraw/pull/9315))
- `@tldraw/mermaid` now loads its ESM-only `mermaid` dependency lazily, so the package is requirable from CommonJS and mermaid only loads when a diagram is created. ([#9100](https://github.com/tldraw/tldraw/pull/9100))
- Keep sync at full speed when the same multiplayer room is open in multiple tabs, windows, or devices — previously a room with no other users throttled network sync to once per second. ([#8988](https://github.com/tldraw/tldraw/pull/8988))
- Include `DOCS.md` documentation in published npm packages, and a generated docs and release notes rollup in the `tldraw` package, so documentation is available when browsing package artifacts. ([#8503](https://github.com/tldraw/tldraw/pull/8503))
- Shrink stored draw shape data for mouse, trackpad, and finger input by ~33% by omitting the redundant constant-pressure value from the encoded path. ([#8963](https://github.com/tldraw/tldraw/pull/8963))
- Improve performance when resizing many shapes at once. ([#9153](https://github.com/tldraw/tldraw/pull/9153))
- Improve change tracking and sync performance when many shapes change at once. ([#9151](https://github.com/tldraw/tldraw/pull/9151), [#9152](https://github.com/tldraw/tldraw/pull/9152))
- Improve validation performance during high-frequency updates. ([#9150](https://github.com/tldraw/tldraw/pull/9150))
- Speed up editor load by caching generated pattern fill defs and deduplicating icon preloads, so neither is rebuilt on every editor mount. ([#9345](https://github.com/tldraw/tldraw/pull/9345), [#9351](https://github.com/tldraw/tldraw/pull/9351))
- Remove the `jittered-fractional-indexing` dependency and speed up index key generation and validation; newly generated index keys are slightly shorter. ([#9352](https://github.com/tldraw/tldraw/pull/9352))
- Improve minimap rendering performance on large documents. ([#9355](https://github.com/tldraw/tldraw/pull/9355))

### Bug fixes

- Fix the fill style dropdown trigger showing a misleading tooltip for the currently selected fill. ([#9023](https://github.com/tldraw/tldraw/pull/9023))
- Fix a two-finger pinch unintentionally changing the selection on touch devices. ([#9006](https://github.com/tldraw/tldraw/pull/9006))
- Pasting content while dragging, translating, resizing, or rotating no longer steals selection from the shape being manipulated. ([#8976](https://github.com/tldraw/tldraw/pull/8976))
- Fix a bug where pressing `Tab` while editing a list inside a note shape created a new note instead of indenting the list item. ([#8958](https://github.com/tldraw/tldraw/pull/8958))
- Fix the viewport shifting slightly when zooming past the minimum or maximum zoom level. ([#8957](https://github.com/tldraw/tldraw/pull/8957))
- Improve the contrast of disabled buttons and menu items in dark mode. ([#8931](https://github.com/tldraw/tldraw/pull/8931))
- Fix `debounce(...).cancel()` leaving an awaited call hanging forever; it now rejects the pending promise. ([#8683](https://github.com/tldraw/tldraw/pull/8683))
- Fix arrows jumping to incorrect positions after undoing or redoing shape deletions involving bindings. ([#8283](https://github.com/tldraw/tldraw/pull/8283))
- Fix touch long-press on shape-creation tools so the context menu opens without leaving a stray shape behind. ([#8946](https://github.com/tldraw/tldraw/pull/8946))
- Fix text labels drifting vertically between browsers (e.g. Chrome vs Safari) in multiplayer by snapping line-height to whole pixels. ([#8981](https://github.com/tldraw/tldraw/pull/8981))
- Fix collaborators not appearing until they interact with the board, idle following peers not being hidden, the "following you" indicator not showing in the people menu, and a phantom cursor for your own other browser sessions. ([#9088](https://github.com/tldraw/tldraw/pull/9088), [#9002](https://github.com/tldraw/tldraw/pull/9002))
- Fix releasing a modifier while still holding a shortcut key triggering the adjacent non-modifier shortcut. ([#9099](https://github.com/tldraw/tldraw/pull/9099))
- Fix redo (`cmd+shift+z`) not firing after an undo (`cmd+z`) on macOS when cmd is kept held. ([#9139](https://github.com/tldraw/tldraw/pull/9139))
- Fix releasing a fast right-click pan zooming the camera to the maximum zoom level. ([#9140](https://github.com/tldraw/tldraw/pull/9140))
- Fix selection handles disappearing when pressing (but not yet dragging) a resize edge, resize corner, or rotate handle, and keep selection chrome consistent while pointing. ([#9179](https://github.com/tldraw/tldraw/pull/9179), [#9184](https://github.com/tldraw/tldraw/pull/9184))
- Fix double-clicking an arrow handle no longer toggling the arrowhead on that end. ([#9180](https://github.com/tldraw/tldraw/pull/9180))
- Fix the camera jumping when a one-finger hand-tool pan transitions into a two-finger pinch zoom. ([#9182](https://github.com/tldraw/tldraw/pull/9182))
- Fix arrows becoming partially un-hoverable and un-clickable when bindings were created or updated without a shape change (e.g. `createBindings` after `createShapes`). ([#9183](https://github.com/tldraw/tldraw/pull/9183))
- Fix camera state getting stuck at `moving` when the editor is disposed during camera movement (e.g. React strict mode with a shared store). ([#9201](https://github.com/tldraw/tldraw/pull/9201))
- Fix the following indicator border being hidden beneath UI panels. ([#9210](https://github.com/tldraw/tldraw/pull/9210))
- Fix the sync version-mismatch handshake reporting `CLIENT_TOO_OLD` when the server is the one running an older version; it now correctly reports `SERVER_TOO_OLD`. ([#9212](https://github.com/tldraw/tldraw/pull/9212))
- Fix ctrl+click on macOS not selecting locked shapes or opening the context menu; it now behaves like a right-click. ([#9234](https://github.com/tldraw/tldraw/pull/9234))
- Fix a crash that could occur when undo or redo restored an editing state for a shape that no longer exists. ([#9260](https://github.com/tldraw/tldraw/pull/9260))
- Fix the line tool's minimum bend distance so it's measured in screen pixels. Shift-click now behaves the same at every zoom level. ([#9275](https://github.com/tldraw/tldraw/pull/9275))
- Fix style scrubbing selecting the wrong option when you release the pointer over a different style section. ([#9280](https://github.com/tldraw/tldraw/pull/9280))
- Fix empty rich text (an empty array) not being treated as an empty label. ([#9295](https://github.com/tldraw/tldraw/pull/9295))
- Fix desktop graphics tablets (e.g. Wacom) incorrectly switching the editor into pen mode. Pen mode now auto-enables only for direct-display stylus input such as Apple Pencil on iPad or Surface Pen on a touchscreen. ([#9316](https://github.com/tldraw/tldraw/pull/9316))
- Fix inconsistent easing when clicking the minimap to move the camera. ([#9355](https://github.com/tldraw/tldraw/pull/9355))
- Fix sync clients crashing when a malformed WebSocket message is received. ([#9356](https://github.com/tldraw/tldraw/pull/9356))
- Fix a crash when measuring viewport bounds after the editor document body is unavailable. ([#9357](https://github.com/tldraw/tldraw/pull/9357))
- Fix highlighted rich text showing an outline in dark mode. ([#9358](https://github.com/tldraw/tldraw/pull/9358))
- Fix text in exported images sometimes being sized with a fallback font when the export ran before the document's fonts had finished loading. ([#9372](https://github.com/tldraw/tldraw/pull/9372))
- Fix a crash when a shape is deleted mid-click (e.g. by a remote user or an undo) while clicking on it. ([#9376](https://github.com/tldraw/tldraw/pull/9376))
- Fix the license manager treating loopback hosts other than `localhost` (such as `127.0.0.1` and `::1`) as production environments. ([#9384](https://github.com/tldraw/tldraw/pull/9384))
- Fix the crop of a grouped, cropped image shifting when the group is flipped by dragging a resize handle. ([#9395](https://github.com/tldraw/tldraw/pull/9395))

## v5.1.0

[View on GitHub](https://github.com/tldraw/tldraw/releases/tag/v5.1.0)

This release redesigns the page menu around inline interaction, adds a keyboard shortcut to copy styles from a hovered shape, and adds a `selectLockedShapes` option for inspecting locked shapes, along with new public translation APIs, canvas performance improvements, and various rendering and UI bug fixes.

### What's new

#### Page menu redesign ([#8836](https://github.com/tldraw/tldraw/pull/8836))

The page menu no longer has an explicit edit mode. Reorder pages by dragging a row directly, rename inline by double-clicking the label or pressing Enter, and drag the new resize handle at the bottom of the popover to adjust the list height — the height is persisted across sessions and a double-click on the handle resets it to the default. The current page is now indicated by a subtle background pill, the row submenu trigger reveals on hover, and the "Create new page" button is pinned to the footer of the popover.

### API changes

- Add a `selectLockedShapes` option to `TldrawOptions`. When enabled, locked shapes can be selected by left click or by brush/scribble selection while remaining protected from edits, moves, and deletes. ([#8860](https://github.com/tldraw/tldraw/pull/8860))
- Export `TldrawUiTranslationProvider`, `AssetUrlsProvider`, and `useAssetUrls` as public API so components like `TldrawSelectionForeground` can be rendered without the full `TldrawUiContextProvider`. ([#8909](https://github.com/tldraw/tldraw/pull/8909))
- Add `FontManager.dispose()`, `OverlayManager.dispose()`, and `OverlayUtil.dispose()` for cleaning up manager state across editor lifecycles. ([#8896](https://github.com/tldraw/tldraw/pull/8896))

### Improvements

- Improve performance on busy canvases — `getRenderingShapes()` now skips its sort step when only shape props, not the set of shape ids, have changed. ([#8784](https://github.com/tldraw/tldraw/pull/8784))
- Improve drawing performance on pages with many shapes by skipping spatial index and culling recomputation when only shape props change. ([#8799](https://github.com/tldraw/tldraw/pull/8799), [#8804](https://github.com/tldraw/tldraw/pull/8804))

### Bug fixes

- Fix a misleading "license expired" console warning for perpetual licenses on covered versions. ([#8791](https://github.com/tldraw/tldraw/pull/8791))
- Fix inconsistent tooltip behavior on the video toolbar by using `TldrawUiToolbarButton` for the replace and download buttons. ([#8794](https://github.com/tldraw/tldraw/pull/8794))
- Fix the missing open-state hint on the page menu and zoom menu triggers when rendered outside the main toolbar. ([#8813](https://github.com/tldraw/tldraw/pull/8813))
- Mark the tldraw UI layer with `role="document"` so toolbars, menus, and dialogs stay reachable to mobile screen readers like VoiceOver and TalkBack, which do not announce the outer canvas `role="application"`. ([#8901](https://github.com/tldraw/tldraw/pull/8901))
- Fix selection edge resize handles overlapping corner handles, which made corners hard to grab on small shapes. ([#8926](https://github.com/tldraw/tldraw/pull/8926))
- Fix a bug where deleting a shape inside a group could move the group to a different z-index. ([#8925](https://github.com/tldraw/tldraw/pull/8925))
- Avoid console errors from calling `preventDefault` on non-cancelable events. ([#8910](https://github.com/tldraw/tldraw/pull/8910))
- Only log the missing-translation warning once per session instead of once per `useTranslation` consumer. ([#8909](https://github.com/tldraw/tldraw/pull/8909))
- Catch `image.decode()` rejections from the icon preload effect so they no longer surface as uncaught promise errors in the console. ([#8824](https://github.com/tldraw/tldraw/pull/8824))

## v5.0.0

This major release introduces a first-class theme system with display values, a new `OverlayUtil` system for canvas overlays, and extensibility APIs for custom record types, asset types, geo shapes, and frame-like shapes. Two new packages ship alongside it: `@tldraw/driver` for imperative editor control and `@tldraw/mermaid` for converting Mermaid diagrams into native shapes. It also adds a shape attribution system with the `TLUserStore` provider, clipboard and performance-measurement hooks, WebSocket hibernation in tlsync, RTL support, cross-window embedding, and various other improvements and bug fixes.

### What's new

#### 💥 Custom themes with display values ([#8410](https://github.com/tldraw/tldraw/pull/8410))

A new first-class theme system replaces the previous approach where colors were hardcoded and resolved inline. Themes are named, registered objects that shape utils consume via a structured display values pipeline.

Register custom themes via `TLThemes` module augmentation for type-safe IDs, add or remove palette colors via `TLThemeDefaultColors` and `TLRemovedDefaultThemeColors`, and pass themes to the editor via the `themes` and `initialTheme` props:

```tsx
<Tldraw themes={{ corporate: myCorporateTheme }} initialTheme="corporate" />
```

Each default shape util defines `getDefaultDisplayValues` to resolve visual properties (colors, stroke widths, font sizes) from the current theme and color mode. Override display values for a default shape with `getCustomDisplayValues`:

```tsx
const MyDrawShapeUtil = DrawShapeUtil.configure({
	getCustomDisplayValues(_editor, _shape, _theme, _colorMode) {
		return { strokeWidth: 10 }
	},
})
```

<details>
<summary>Migration guide</summary>

The `inferDarkMode` prop has been renamed to `colorScheme` and changed from `boolean` to `'light' | 'dark' | 'system'`:

```tsx
// Before
<Tldraw inferDarkMode />

// After
<Tldraw colorScheme="system" />
```

`useIsDarkMode()` has been renamed to `useColorMode()` and returns `'dark' | 'light'` instead of `boolean`.

`getDefaultColorTheme()` and `DefaultColorThemePalette` have been removed. Use `editor.getCurrentTheme().colors[colorMode]` instead:

```tsx
// Before
const theme = getDefaultColorTheme({ isDarkMode })

// After
const theme = editor.getCurrentTheme()
const colors = theme.colors[editor.getColorMode()]
```

`useDefaultColorTheme()` has been removed. Use `editor.getCurrentTheme()` and `useColorMode()` instead.

`FONT_FAMILIES`, `FONT_SIZES`, `LABEL_FONT_SIZES`, `STROKE_SIZES`, `TEXT_PROPS`, and `ARROW_LABEL_FONT_SIZES` have been removed — these are now resolved via display values.

`SvgExportContext.themeId` has been renamed to `SvgExportContext.colorMode` and changed from `string` to `'light' | 'dark'`.

`getColorValue()` now takes `TLThemeColors` as its first argument instead of `TLDefaultColorTheme`.

</details>

#### 💥 Custom overlays ([#8469](https://github.com/tldraw/tldraw/pull/8469))

A new `OverlayUtil` system unifies canvas overlays. Built-in brushes, handles, scribbles, snap indicators, selection foreground, collaborator cursors, and arrow hints are now implemented as overlay utils and can be customized or extended via the `overlayUtils` prop.

Overlays now render in front of indicators in a unified `CanvasOverlays` layer, and the legacy default component implementations and their CSS have been removed.

<details>
<summary>Migration guide</summary>

The legacy overlay components have been removed from `TLEditorComponents`. Canvas overlays are now rendered directly to a 2D canvas context by `OverlayUtil` classes, and customization happens by subclassing the default util rather than swapping a React component.

**Customizing a built-in overlay:** extend the default util and give it the same `static type`; it replaces the built-in when passed via `overlayUtils`:

```tsx
// Before
const components: TLEditorComponents = {
	Brush: MyBrushComponent,
}

<Tldraw components={components} />

// After
import { Tldraw, BrushOverlayUtil, type TLBrushOverlay } from 'tldraw'

class MyBrushOverlayUtil extends BrushOverlayUtil {
	override render(ctx: CanvasRenderingContext2D, overlays: TLBrushOverlay[]) {
		const overlay = overlays[0]
		if (!overlay) return
		const { x, y, w, h } = overlay.props
		const zoom = this.editor.getEfficientZoomLevel()
		ctx.fillStyle = 'rgba(0, 0, 255, 0.1)'
		ctx.strokeStyle = 'blue'
		ctx.lineWidth = 1 / zoom
		ctx.beginPath()
		ctx.rect(x, y, w, h)
		ctx.fill()
		ctx.stroke()
	}
}

<Tldraw overlayUtils={[MyBrushOverlayUtil]} />
```

The following slots have been removed from `TLEditorComponents`. Subclass the matching default overlay util instead:

- `Brush`, `ZoomBrush` → `BrushOverlayUtil`, `ZoomBrushOverlayUtil`
- `Scribble` → `ScribbleOverlayUtil`
- `SnapIndicator` → `SnapIndicatorOverlayUtil`
- `Handle`, `Handles` → `ShapeHandleOverlayUtil`
- `SelectionForeground`, `SelectionBackground` → `SelectionForegroundOverlayUtil`
- `CollaboratorHint` → `CollaboratorHintOverlayUtil` (collaborator cursors/brushes/scribbles have their own utils too)

The corresponding `Default*` exports and `LiveCollaborators` have also been removed.

**`ShapeUtil.indicator()` → `ShapeUtil.getIndicatorPath()`.** The shape util method that draws a shape's selection indicator no longer returns JSX. It now returns a `Path2D` (or a `TLIndicatorPath`) and is composited onto the canvas overlay layer. Every custom `ShapeUtil` that overrides `indicator` needs to migrate:

```tsx
// Before
override indicator(shape: MyShape) {
	return <rect width={shape.props.w} height={shape.props.h} />
}

// After
override getIndicatorPath(shape: MyShape): Path2D {
	const path = new Path2D()
	path.rect(0, 0, shape.props.w, shape.props.h)
	return path
}
```

For shapes whose indicator should match the shape's geometry, return `path` built from the same primitives. For placeholder shapes (e.g. an `ErrorShape`), return an empty `new Path2D()` and declare the return type explicitly so TypeScript doesn't infer `never`.

The React component slots `ShapeIndicator`, `ShapeIndicators`, and `ShapeIndicatorErrorFallback` are also removed from `TLEditorComponents` — there is no replacement React slot, since indicators now render through `getIndicatorPath()`.

Overlays cannot render React; they draw into a 2D canvas context. For React-based overlays, render an HTML layer above the canvas via a regular `TLEditorComponents` slot like `InFrontOfTheCanvas`.

Overlay colors come from `TLTheme`, not CSS variables. Read them inside `render()` via `this.editor.getCurrentTheme().colors[this.editor.getColorMode()]` so overlays follow light/dark mode automatically.

These CSS variables have been removed: `--tl-color-snap`, `--tl-color-brush-fill`, `--tl-color-brush-stroke`, `--tl-color-laser`, `--tl-layer-overlays-custom`.

Overriding them (or the removed selectors `.tl-brush`, `.tl-scribble`, `.tl-snap-indicator`, `.tl-handle*`, `.tl-selection__fg__outline`, `.tl-corner-handle`, `.tl-text-handle`, `.tl-corner-crop-handle`, `.tl-mobile-rotate__*`) no longer has any effect. Port these to `TLTheme` entries or to the overlay util's `render()`.

</details>

#### 💥 Tldraw component options

Several `<Tldraw>` props that previously lived at the top level have been consolidated under the `options` prop, and a few extension points moved off the component entirely.

<details>
<summary>Migration guide</summary>

**`cameraOptions`, `textOptions`, and `deepLinks` props moved into `options`:**

```tsx
// Before
<Tldraw cameraOptions={camOpts} textOptions={txtOpts} deepLinks />

// After
<Tldraw options={{ camera: camOpts, text: txtOpts, deepLinks: true }} />
```

**`embeds` prop removed; configure embed definitions on `EmbedShapeUtil`:**

```tsx
// Before
<Tldraw embeds={[...DEFAULT_EMBED_DEFINITIONS, customEmbed]} />

// After
import { EmbedShapeUtil } from 'tldraw'
const ConfiguredEmbedShapeUtil = EmbedShapeUtil.configure({
	embedDefinitions: [...DEFAULT_EMBED_DEFINITIONS, customEmbed],
})
<Tldraw shapeUtils={[ConfiguredEmbedShapeUtil]} />
```

This silently compiles in some setups (the prop is unknown but JSX won't always reject it), so this is the kind of change that the typecheck won't always catch — search the source for `embeds=` and migrate any matches.

**`setDefaultEditorAssetUrls()` and `setDefaultUiAssetUrls()` are no longer part of the public API.** They still exist at runtime for now, but they're marked `@internal` and should not be relied on. Pass `assetUrls` to each `<Tldraw>` instead:

```tsx
// Before
import { setDefaultEditorAssetUrls, setDefaultUiAssetUrls } from 'tldraw'
setDefaultEditorAssetUrls(assetUrls)
setDefaultUiAssetUrls(assetUrls)

<Tldraw />

// After
<Tldraw assetUrls={assetUrls} />
```

Do not reach for module augmentation to re-expose the old globals — find each `<Tldraw>` mount point and pass `assetUrls` directly.

</details>

#### Custom record types ([#8213](https://github.com/tldraw/tldraw/pull/8213))

You can now register custom record types in the tldraw store for persisting and synchronizing domain-specific data that doesn't fit into shapes, bindings, or assets. Custom records support scoping (document/session/presence), validation, migrations, and default properties.

```tsx
import { createTLSchema, createCustomRecordId } from 'tldraw'

const schema = createTLSchema({
	records: [
		{
			typeName: 'marker',
			scope: 'document',
			validator: markerValidator,
		},
	],
})
```

TypeScript module augmentation via `TLGlobalRecordPropsMap` lets custom record types participate in the `TLRecord` union.

#### Extensible geo shapes ([#8543](https://github.com/tldraw/tldraw/pull/8543))

`GeoShapeUtil` now supports custom geo types via a `customGeoTypes` option on `configure()`. Previously, adding a new geo type required forking or monkey-patching `GeoShapeUtil`; now custom types plug into the existing system and inherit labels, resizing, fill/dash/color styling, SVG export, and hyperlink support — while supplying their own path geometry, snap behavior, creation size, and style panel icon.

```tsx
import { GeoShapeUtil, PathBuilder } from 'tldraw'

const MyGeoShapeUtil = GeoShapeUtil.configure({
	customGeoTypes: {
		'rounded-rect': {
			getPath: (w, h, shape) => {
				const r = Math.min(w, h) * 0.2
				return new PathBuilder()
					.moveTo(r, 0, { geometry: { isFilled: shape.props.fill !== 'none' } })
					.lineTo(w - r, 0)
					.circularArcTo(r, false, true, w, r)
					.lineTo(w, h - r)
					.circularArcTo(r, false, true, w - r, h)
					.lineTo(r, h)
					.circularArcTo(r, false, true, 0, h - r)
					.lineTo(0, r)
					.circularArcTo(r, false, true, r, 0)
					.close()
			},
			snapType: 'polygon',
			icon: 'geo-rectangle',
			defaultSize: { w: 200, h: 150 },
		},
	},
})

<Tldraw shapeUtils={[MyGeoShapeUtil]} />
```

Each definition supplies `getPath`, `snapType` (`'polygon'` for vertex + center snaps, `'blobby'` for center-only), and an `icon` for the style panel, plus optional `defaultSize` and `onDoubleClick` handler. Custom types appear alongside the built-ins in the geo picker.

#### 💥 Extensible asset types ([#8031](https://github.com/tldraw/tldraw/pull/8031))

A new `AssetUtil` base class follows the `ShapeUtil` / `BindingUtil` pattern, making the asset system extensible. Previously, assets were a hardcoded union of image, video, and bookmark types. Now you can register custom asset types with their own MIME type handling, file-to-asset conversion, and shape creation logic.

```tsx
import { AssetUtil } from '@tldraw/editor'

class AudioAssetUtil extends AssetUtil<TLAudioAsset> {
	static override type = 'audio' as const
	override getSupportedMimeTypes() {
		return ['audio/mpeg', 'audio/wav']
	}
	override async getAssetFromFile(editor, file) {
		/* ... */
	}
}

;<Tldraw assetUtils={[AudioAssetUtil]} />
```

`TLAssetStore` keeps `upload`/`resolve`/`remove` as cross-cutting concerns, while `AssetUtil` handles type-specific behavior: MIME types, file-to-asset metadata, and asset-to-shape creation.

<details>
<summary>Migration guide</summary>

`assetValidator` has been removed. Use `imageAssetValidator`, `videoAssetValidator`, or `bookmarkAssetValidator` instead.

`getMediaAssetInfoPartial` has been removed. Use `AssetUtil.getAssetFromFile` instead.

`notifyIfFileNotAllowed` signature changed from `(file, options)` to `(editor, file, options)`.

`getAssetInfo` signature changed from `(file, options, assetId?)` to `(editor, file, assetId?)` and now returns `TLAsset | null` instead of throwing.

</details>

#### Frame-like custom shapes ([#8331](https://github.com/tldraw/tldraw/pull/8331))

Custom shapes can now opt into frame-like behavior: clipping children, acting as a parent on paste and drag-in, blocking erasure from inside, and supporting full-brush selection. Previously, frame behavior was hardcoded to the built-in `frame` type; the editor and tools now route frame checks through `editor.getShapeUtil(shape).isFrameLike(shape)`.

The easiest way to build one is to extend the new `BaseFrameLikeShapeUtil` abstract class, which provides sensible defaults for `isFrameLike`, `providesBackgroundForChildren`, `canReceiveNewChildrenOfType`, `canRemoveChildrenOfType`, `getClipPath`, `onDragShapesIn`, and `onDragShapesOut`:

```tsx
import { BaseFrameLikeShapeUtil, SVGContainer } from '@tldraw/editor'

class MyContainerUtil extends BaseFrameLikeShapeUtil<MyContainerShape> {
	static override type = 'my-container' as const
	static override props = myContainerShapeProps

	override getDefaultProps() {
		return { w: 300, h: 200 }
	}

	override component(shape: MyContainerShape) {
		return <SVGContainer>...</SVGContainer>
	}

	override indicator(shape: MyContainerShape) {
		return <rect width={shape.props.w} height={shape.props.h} />
	}
}
```

Shapes that need frame behavior without inheriting from `BaseFrameLikeShapeUtil` can override `isFrameLike()` directly on any `ShapeUtil`. `FrameShapeUtil` now extends `BaseFrameLikeShapeUtil`, so existing frames are unaffected.

#### Shape attribution and TLUserStore ([#8147](https://github.com/tldraw/tldraw/pull/8147))

A new shape attribution system tracks who created and last edited shapes. The `TLUserStore` provider interface connects tldraw to your auth system with reactive `Signal`-based methods: `getCurrentUser()` returns the active user for presence and attribution, while `resolve(userId)` resolves any user ID to display info.

```tsx
<Tldraw
	users={{
		getCurrentUser: () => currentUserSignal,
		resolve: (userId) => resolvedUserSignal(userId),
	}}
/>
```

User records are now document-scoped via the unified `TLUser` record type. SDK users can extend user records with custom validated metadata through `createTLSchema`:

```tsx
const schema = createTLSchema({
	user: {
		meta: {
			isAdmin: T.boolean,
			department: T.string,
		},
	},
})
```

Note shapes now track and display a "first edited by" attribution label in the bottom-right corner, showing who first added text to the note.

<details>
<summary>Migration guide</summary>

**`useTldrawUser` has been removed.** Previously it bundled user *preferences* (color, color scheme) and user *identity* (id, name) into a single object. These are now separate concerns:

- **Preferences** (color, color scheme, snap mode, etc.) are managed via `TLUserPreferences`. Set them with the editor's user-preferences API directly.
- **Identity** for attribution comes from a `TLUserStore` provider passed to the new `users` prop on `<Tldraw>`.

```tsx
// Before
const user = useTldrawUser({ userPreferences, setUserPreferences })
<Tldraw user={user} />

// After
<Tldraw
	users={{
		getCurrentUser: () => currentUserSignal,
		resolve: (userId) => resolvedUserSignal(userId),
	}}
	colorScheme={userPreferences.colorScheme}
/>
```

If you only need preferences (no custom identity), pass `colorScheme` directly and seed preferences imperatively after mount; if you only need identity, pass `users`. Most apps want both.

</details>

#### @tldraw/driver ([#7952](https://github.com/tldraw/tldraw/pull/7952))

A new `@tldraw/driver` package provides an imperative API for driving the tldraw editor programmatically. `Driver` wraps an `Editor` instance and exposes event dispatch, selection transforms, clipboard operations, and shape queries with fluent chaining.

```tsx
import { Driver } from '@tldraw/driver'

const driver = new Driver(editor)
driver.pointerMove(100, 100).pointerDown().pointerMove(200, 200).pointerUp()
```

This is useful for scripting, automation, agent workflows, and REPL-style interaction. The release includes a Scripter example that demonstrates the package.

#### @tldraw/mermaid ([#8194](https://github.com/tldraw/tldraw/pull/8194), [#8285](https://github.com/tldraw/tldraw/pull/8285), [#8322](https://github.com/tldraw/tldraw/pull/8322))

A new `@tldraw/mermaid` package converts Mermaid diagram syntax into native tldraw shapes. Paste Mermaid text to create flowcharts, sequence diagrams, state diagrams, and mind maps as editable shapes on the canvas.

```tsx
import { createMermaidDiagram } from '@tldraw/mermaid'

await createMermaidDiagram(
	editor,
	`
  graph TD
    A[Start] --> B{Decision}
    B -->|Yes| C[Action]
    B -->|No| D[End]
`
)
```

The package parses Mermaid syntax, extracts layout from the rendered SVG, and produces a diagram-agnostic blueprint that gets rendered into geo shapes, arrows, and groups.

Node creation is extensible: pass `mapNodeToRenderSpec` per diagram type to map diagram nodes to different shapes, or use `createShape` in `BlueprintRenderingOptions` to take full control of how nodes are created on the canvas.

#### Arbitrary iframe embeds ([#8306](https://github.com/tldraw/tldraw/pull/8306))

Paste any `<iframe>` embed code onto the canvas to create an embed shape. Previously only URLs matching known providers (YouTube, Google Maps, etc.) worked. Now any valid iframe with an HTTP(S) source creates an embed directly. It works with services like OpenStreetMap, SoundCloud, Loom, and more.

#### Clipboard hooks ([#8290](https://github.com/tldraw/tldraw/pull/8290))

New `TldrawOptions` hooks let you intercept and customize clipboard copy, cut, and paste. `onBeforeCopyToClipboard` filters or transforms serialized content before it hits the clipboard, `onBeforePasteFromClipboard` filters parsed paste payloads before shapes are created, and `onClipboardPasteRaw` handles raw clipboard data before tldraw's default paste pipeline.

```tsx
const options: Partial<TldrawOptions> = {
	onBeforeCopyToClipboard(info, content) {
		// filter shapes or transform content before copy/cut
		return content
	},
	onBeforePasteFromClipboard(info, content) {
		// filter or transform parsed paste content
		return content
	},
	onClipboardPasteRaw(info) {
		// handle raw clipboard data yourself; return false to cancel tldraw handling
		return false
	},
}
```

#### Cross-window embedding support ([#8196](https://github.com/tldraw/tldraw/pull/8196))

Tldraw now works correctly when embedded in iframes, Electron pop-out windows, and Obsidian plugins where the global `document` and `window` differ from the ones tldraw is mounted in. All bare `document` and `window` references have been replaced with container-aware alternatives.

New helpers `getOwnerDocument()` and `getOwnerWindow()` are exported from `@tldraw/editor`, along with `Editor.getContainerDocument()` and `Editor.getContainerWindow()` convenience methods.

#### Performance measurement hooks ([#8421](https://github.com/tldraw/tldraw/pull/8421))

A new `editor.performance` API lets SDK users subscribe to editor performance events: interaction frame times (translating, resizing, drawing), camera pan/zoom sessions, shape create/update/delete operations, and undo/redo timings. Events aggregate frame-time percentiles (avg / median / p95 / p99) and, on Chromium, attach Long Animation Frame data with per-script attribution for diagnosing jank.

```tsx
const unsubscribe = editor.performance.on('interaction-end', (event) => {
	// forward to analytics
	const { frameTimes, longAnimationFrames, ...metrics } = event
	track('editor.interaction', metrics)
})
```

The manager is zero-overhead when no listeners are attached. Custom tools opt in to interaction tracking via `static trackPerformance = true` on the `StateNode`. A `PerformanceApiAdapter` is also available for piping events into the browser Performance API for DevTools timelines.

#### RTL support ([#8033](https://github.com/tldraw/tldraw/pull/8033))

The tldraw UI now supports right-to-left languages like Arabic. A new `useDirection()` hook returns `'ltr'` or `'rtl'` from the current translation context, and all Radix UI components and CSS have been updated to respect text direction. The `dir` attribute is set on `.tl-container`, and CSS uses logical properties (`margin-inline-start`, `inset-inline-end`, etc.) instead of physical ones.

#### WebSocket hibernation in tlsync ([#8070](https://github.com/tldraw/tldraw/pull/8070))

`TLSocketRoom` now supports session resume and snapshot APIs for WebSocket hibernation environments like Cloudflare Durable Objects. Sessions can be suspended and restored without losing state, and the `sync-cloudflare` template has been updated to use the WebSocket Hibernation API.

New APIs include `handleSocketResume()` for restoring sessions from snapshots, `getSessionSnapshot()` for capturing session state, and an `onSessionSnapshot` callback for persisting snapshots to WebSocket attachments.

### API changes

- 💥 Remove `defaultColorNames`, `DefaultColorThemePalette`, `DefaultLabelColorStyle`, `TLDefaultColorTheme` type, and `getDefaultColorTheme()` from `@tldraw/tlschema`. Use `editor.getCurrentTheme().colors` instead. ([#8410](https://github.com/tldraw/tldraw/pull/8410))
- 💥 Remove `ARROW_LABEL_FONT_SIZES`, `FONT_FAMILIES`, `FONT_SIZES`, `LABEL_FONT_SIZES`, `STROKE_SIZES`, `TEXT_PROPS`, and `useDefaultColorTheme()` from `@tldraw/tldraw`. These are now resolved via display values. ([#8410](https://github.com/tldraw/tldraw/pull/8410))
- 💥 Rename `inferDarkMode` prop to `colorScheme` (`boolean` → `'light' | 'dark' | 'system'`). Rename `useIsDarkMode()` to `useColorMode()` (returns `'dark' | 'light'` instead of `boolean`). ([#8410](https://github.com/tldraw/tldraw/pull/8410))
- 💥 Rename `SvgExportContext.themeId` to `SvgExportContext.colorMode` (`string` → `'light' | 'dark'`). ([#8410](https://github.com/tldraw/tldraw/pull/8410))
- 💥 Change `getColorValue()` first argument from `TLDefaultColorTheme` to `TLThemeColors`. ([#8410](https://github.com/tldraw/tldraw/pull/8410))
- 💥 Change `PlainTextLabelProps` and `RichTextLabelProps`: `font` → `fontFamily`, `align` → `textAlign`, `fill` removed. ([#8410](https://github.com/tldraw/tldraw/pull/8410))
- 💥 Remove `Brush`, `ZoomBrush`, `Scribble`, `SnapIndicator`, `Handle`, `Handles`, `SelectionForeground`, `SelectionBackground`, `CollaboratorHint`, `ShapeIndicator`, `ShapeIndicators`, and `ShapeIndicatorErrorFallback` from `TLEditorComponents`, along with the corresponding `Default*` component exports and `LiveCollaborators`. Migrate custom overlay components to `OverlayUtil`; indicator customization moves to `ShapeUtil.getIndicatorPath()`. ([#8469](https://github.com/tldraw/tldraw/pull/8469))
- 💥 Remove the CSS variables `--tl-color-snap`, `--tl-color-brush-fill`, `--tl-color-brush-stroke`, `--tl-color-laser`, and `--tl-layer-overlays-custom`, along with the overlay class selectors (`.tl-brush`, `.tl-scribble`, `.tl-snap-indicator`, `.tl-handle*`, `.tl-selection__fg__outline`, `.tl-corner-handle`, `.tl-text-handle`, `.tl-corner-crop-handle`, `.tl-mobile-rotate__*`). Overlay colors now come from `TLTheme`. ([#8469](https://github.com/tldraw/tldraw/pull/8469))
- 💥 Remove `assetValidator`. Use `imageAssetValidator`, `videoAssetValidator`, or `bookmarkAssetValidator` instead. ([#8031](https://github.com/tldraw/tldraw/pull/8031))
- 💥 Remove `getMediaAssetInfoPartial`. Use `AssetUtil.getAssetFromFile` instead. ([#8031](https://github.com/tldraw/tldraw/pull/8031))
- 💥 Change `notifyIfFileNotAllowed` signature from `(file, options)` to `(editor, file, options)`. ([#8031](https://github.com/tldraw/tldraw/pull/8031))
- 💥 Change `getAssetInfo` signature from `(file, options, assetId?)` to `(editor, file, assetId?)` and return `TLAsset | null` instead of throwing. ([#8031](https://github.com/tldraw/tldraw/pull/8031))
- 💥 Move the `Cmd+Shift+C` / `Ctrl+Shift+C` shortcut from "Copy as SVG" to "Copy as PNG". ([#8532](https://github.com/tldraw/tldraw/pull/8532))
- 💥 Replace `ShapeUtil.indicator()` (returned JSX) with `ShapeUtil.getIndicatorPath()` (returns `Path2D | TLIndicatorPath | undefined`). Indicators now render to the canvas overlay layer instead of as React elements. ([#8469](https://github.com/tldraw/tldraw/pull/8469))
- 💥 Move `cameraOptions`, `textOptions`, and `deepLinks` from top-level `<Tldraw>` props into the `options` prop (e.g. `options={{ camera, text, deepLinks }}`).
- 💥 Remove the `embeds` prop from `<Tldraw>`. Configure embed definitions via `EmbedShapeUtil.configure({ embedDefinitions })` and pass the configured util through `shapeUtils`.
- 💥 Demote `setDefaultEditorAssetUrls()` and `setDefaultUiAssetUrls()` to `@internal`. Pass `assetUrls` to each `<Tldraw>` instead.
- 💥 Remove `useTldrawUser`. Use the new `users` prop (`TLUserStore`) for identity and `TLUserPreferences` for preferences.
- 💥 Replace `TLDrawShapeSegment.points` with the helper `getPointsFromDrawSegment(segment, scaleX, scaleY)` so segment points respect the shape's current scale.
- 💥 Change `BindingUtil` hook params: `fromShapeType`/`toShapeType` are removed in favor of full `fromShape`/`toShape` records (read `fromShape.type` / `toShape.type` directly).
- 💥 Add `'middle-legacy'` (and other legacy values) to the `align` union resolved by `PlainTextLabel`/`RichTextLabel`. If your code maps `align` into `PlainTextLabel.textAlign`, narrow legacy values to one of `'start' | 'center' | 'end'` before passing them through.
- Add `TLTheme`, `TLThemeId`, `TLThemes`, `TLThemeDefaultColors`, `TLThemeColors`, `TLRemovedDefaultThemeColors`, `ThemeManager`, `getDisplayValues()`, `getColorValue()`, and `DEFAULT_THEME` for the new theme system. ([#8410](https://github.com/tldraw/tldraw/pull/8410))
- Add `themes` and `initialTheme` props to `<Tldraw>` and `<TldrawEditor>`. ([#8410](https://github.com/tldraw/tldraw/pull/8410))
- Add `getCurrentTheme()`, `setCurrentTheme()`, `getThemes()`, `getTheme()`, `updateTheme()`, `updateThemes()`, and `getColorMode()` to the editor. ([#8410](https://github.com/tldraw/tldraw/pull/8410))
- Add `getDefaultDisplayValues` and `getCustomDisplayValues` to shape util options for theme-aware visual properties. ([#8410](https://github.com/tldraw/tldraw/pull/8410))
- Add `OverlayUtil`, `TLOverlay`, `OverlayManager`, `Editor.overlays`, `defaultOverlayUtils`, and the `overlayUtils` prop on `<Tldraw>` / `<TldrawEditor>` for registering and managing canvas overlays. ([#8469](https://github.com/tldraw/tldraw/pull/8469))
- Add per-overlay utils: `BrushOverlayUtil`, `ZoomBrushOverlayUtil`, `ScribbleOverlayUtil`, `SnapIndicatorOverlayUtil`, `ShapeHandleOverlayUtil`, `SelectionForegroundOverlayUtil`, `ArrowHintOverlayUtil`, `CollaboratorBrushOverlayUtil`, `CollaboratorScribbleOverlayUtil`, `CollaboratorHintOverlayUtil`, and `CollaboratorCursorOverlayUtil`. ([#8469](https://github.com/tldraw/tldraw/pull/8469))
- Add overlay-related colors to `TLTheme`. ([#8469](https://github.com/tldraw/tldraw/pull/8469))
- Add `BaseFrameLikeShapeUtil` abstract class and `ShapeUtil.isFrameLike()` so custom shapes can opt into frame-like behaviors (paste parenting, full-brush selection, blocking erasure from inside, clipping children). `FrameShapeUtil` now extends `BaseFrameLikeShapeUtil`. ([#8331](https://github.com/tldraw/tldraw/pull/8331))
- Add extensibility API for custom geo shape types via `GeoShapeUtil.configure({ customGeoTypes })`. Add `GeoTypeDefinition` interface for defining path geometry, snap behavior, creation size, style panel icon, and double-click handler while inheriting standard geo shape behavior. ([#8543](https://github.com/tldraw/tldraw/pull/8543))
- Add `AssetUtil` base class with `configure()`, `getDefaultProps()`, `getSupportedMimeTypes()`, `getAssetFromFile()`, and `createShape()`. Add `assetUtils` prop to `<Tldraw>`. Add `Editor.getAssetUtil()`, `Editor.hasAssetUtil()`, and `Editor.getAssetUtilForMimeType()`. Add `TLGlobalAssetPropsMap` for type-safe custom asset registration. Add `createAssetRecordType()`, `defaultAssetSchemas`, and `assets` parameter to `createTLSchema()`. ([#8031](https://github.com/tldraw/tldraw/pull/8031))
- Add `CustomRecordInfo` interface, `createCustomRecordId()`, `createCustomRecordMigrationIds()`, `createCustomRecordMigrationSequence()`, `isCustomRecord()`, `isCustomRecordId()` for custom record types. `createTLSchema()` and `createTLStore()` now accept a `records` option. ([#8213](https://github.com/tldraw/tldraw/pull/8213))
- Add `TLUserStore` interface with `getCurrentUser()` and `resolve()` for connecting tldraw to auth systems. Add unified `TLUser` record type, `UserRecordType`, `createUserId`, `isUserId`, `userIdValidator`, and `createUserRecordType()` for extensible user schemas. Add `user` parameter to `createTLSchema()`. Add `Editor.getAttributionUser()`, `Editor.getAttributionUserId()`, and `Editor.getAttributionDisplayName()`. Add `textFirstEditedBy` prop to `TLNoteShapeProps`. ([#8147](https://github.com/tldraw/tldraw/pull/8147))
- Add `@tldraw/driver` package with `Driver` class for imperative editor control. ([#7952](https://github.com/tldraw/tldraw/pull/7952))
- Add `@tldraw/mermaid` package with `createMermaidDiagram()`, `renderBlueprint()`, and `MermaidDiagramError` for converting Mermaid syntax to tldraw shapes. ([#8194](https://github.com/tldraw/tldraw/pull/8194))
- Add `mapNodeToRenderSpec` per-diagram-type option and `createShape` override to `@tldraw/mermaid` for customizing how diagram nodes are rendered as shapes. ([#8322](https://github.com/tldraw/tldraw/pull/8322))
- Add `onBeforeCopyToClipboard`, `onBeforePasteFromClipboard`, and `onClipboardPasteRaw` hooks to `TldrawOptions` for intercepting clipboard operations. Add `TLClipboardWriteInfo` and `TLClipboardPasteRawInfo` types. Export `handleNativeOrMenuCopy` from `@tldraw/tldraw`. ([#8290](https://github.com/tldraw/tldraw/pull/8290))
- Add `Cmd+Shift+V` / `Ctrl+Shift+V` shortcut to paste clipboard content as plain text. `Cmd+Shift+V` no longer toggles paste-at-cursor positioning. ([#8347](https://github.com/tldraw/tldraw/pull/8347))
- Add support for pasting arbitrary `<iframe>` embed codes to create embed shapes from any service. ([#8306](https://github.com/tldraw/tldraw/pull/8306))
- Add Canva to `DEFAULT_EMBED_DEFINITIONS` so Canva design URLs paste as iframe embeds. ([#8459](https://github.com/tldraw/tldraw/pull/8459))
- Add `TldrawOptions.rightClickPanning` (default `true`) to gate the new right-click drag-to-pan behavior. Add `InputsManager.getIsRightPointing()` / `setIsRightPointing()`. ([#8501](https://github.com/tldraw/tldraw/pull/8501))
- Add `Editor.performance` (`PerformanceManager`) with `on()`, `once()`, and `dispose()` for subscribing to interaction, camera, shape-operation, frame, and undo/redo performance events. Add `PerformanceApiAdapter` for piping events into the browser Performance API. Add `StateNode.trackPerformance` static field so custom tools can opt into interaction tracking. Add `TLPerfFrameTimeStats`, `TLPerfEventMap`, `TLInteractionStartPerfEvent`, `TLInteractionEndPerfEvent`, `TLCameraStartPerfEvent`, `TLCameraEndPerfEvent`, `TLShapeOperationPerfEvent`, `TLFramePerfEvent`, `TLUndoRedoPerfEvent`, `TLPerfLongAnimationFrame`, and `TLPerfLongAnimationFrameScript`. ([#8421](https://github.com/tldraw/tldraw/pull/8421))
- Add `Editor.getContainerDocument()` and `Editor.getContainerWindow()` methods, and `getOwnerDocument()` / `getOwnerWindow()` helpers for cross-window embedding. ([#8196](https://github.com/tldraw/tldraw/pull/8196))
- Add `handleSocketResume()`, `getSessionSnapshot()`, and `onSessionSnapshot` to `TLSocketRoom` for WebSocket hibernation support. Add `clientTimeout` option to `TLSyncRoom`. ([#8070](https://github.com/tldraw/tldraw/pull/8070))
- Add `useDirection()` hook for RTL support. ([#8033](https://github.com/tldraw/tldraw/pull/8033))
- Expose `PeopleMenu`, `PeopleMenuItem`, `PeopleMenuFacePile`, and `UserPresenceEditor` through `TldrawUiComponents` so the share panel sub-components can be overridden via the `<Tldraw />` `components` prop. ([#8346](https://github.com/tldraw/tldraw/pull/8346))
- Add `'none'` to `TLDefaultDashStyle` for hiding shape borders while preserving fills. The option is not exposed in the style panel — it can only be set programmatically. Add `NonePathBuilderOpts`; `PathBuilder.toSvg()` now returns `null` for the `'none'` style. ([#8453](https://github.com/tldraw/tldraw/pull/8453))
- Export `defaultHandleExternalFileReplaceContent`, which was previously referenced in docs but missing from the package's public exports. ([#8579](https://github.com/tldraw/tldraw/pull/8579))
- Change `TLSvgExportOptions.padding` to accept `number | 'auto'`. The `'auto'` mode (now default) renders with padding then trims to visual content bounds. ([#8202](https://github.com/tldraw/tldraw/pull/8202))
- Add `TextManager.measureHtmlBatch()` for batched DOM text measurement. ([#7949](https://github.com/tldraw/tldraw/pull/7949))
- Add `'json'` to `TLCopyType` for copying shapes as JSON in debug mode. ([#8206](https://github.com/tldraw/tldraw/pull/8206))
- Add `Vec.IsFinite()` for checking whether a vector has finite coordinates. ([#8176](https://github.com/tldraw/tldraw/pull/8176))
- Change `Vec.PointsBetween()` to accept an optional `ease` parameter. ([#7977](https://github.com/tldraw/tldraw/pull/7977))

### Improvements

- Optimize geometry hot paths for hit testing: reduce allocations and function call overhead in `Vec`, `Edge2d`, `Circle2d`, `Arc2d`, `Polyline2d`, and intersection routines. Circle hit testing is up to 19x faster, polyline nearest-point is 6.8x faster. ([#8210](https://github.com/tldraw/tldraw/pull/8210))
- Improve resize performance for multiple geo shapes with text labels by batching DOM measurements into a single pass per frame. ([#7949](https://github.com/tldraw/tldraw/pull/7949))
- Exports now automatically trim to visual content bounds, capturing overflow like thick strokes and arrowheads without extra whitespace. ([#8202](https://github.com/tldraw/tldraw/pull/8202))
- Add `Cmd+Shift+V` / `Ctrl+Shift+V` shortcut to paste clipboard content as plain text, stripping HTML and rich formatting. `Cmd+Shift+V` no longer toggles paste-at-cursor positioning — the "Paste at cursor" preference covers that use case. ([#8347](https://github.com/tldraw/tldraw/pull/8347))
- Change `Cmd+Shift+C` / `Ctrl+Shift+C` to copy selected shapes as PNG instead of SVG, matching the default in most apps. The SVG copy path is still available from the Copy-as menu. ([#8532](https://github.com/tldraw/tldraw/pull/8532))
- Right-click and drag now pans the camera, matching tools like Figma. A static right-click still opens the context menu on release. Gate via `TldrawOptions.rightClickPanning` (defaults to `true`). ([#8501](https://github.com/tldraw/tldraw/pull/8501))
- Allow Cmd+click / Ctrl+click on style panel items to apply style changes only to selected shapes without updating defaults for future shapes. ([#8452](https://github.com/tldraw/tldraw/pull/8452))
- Hide selection overlay when nudging shapes with arrow keys. ([#8447](https://github.com/tldraw/tldraw/pull/8447))
- Move the debug mode toggle into the preferences submenu. ([#8259](https://github.com/tldraw/tldraw/pull/8259))
- Tighten iframe referrer policy for embeds to send only the origin instead of the full URL to third-party embed providers. ([#8412](https://github.com/tldraw/tldraw/pull/8412))
- Replace the `@use-gesture/react` dependency with custom gesture handling. Bundle size is smaller and a stale dependency is gone. ([#8392](https://github.com/tldraw/tldraw/pull/8392))

### Bug fixes

- Fix `bailToMark` silently discarding pending history changes when the target mark doesn't exist. ([#8260](https://github.com/tldraw/tldraw/pull/8260))
- Fix `FocusManager.dispose()` not actually removing document event listeners due to `.bind()` creating new function references. ([#8232](https://github.com/tldraw/tldraw/pull/8232))
- Fix camera state getting stuck at `'moving'` when the editor is disposed mid-transition (e.g. deep links under React strict mode), which blocked all pointer interactions on the canvas. ([#8396](https://github.com/tldraw/tldraw/pull/8396))
- Fix leaked camera animations, following subscriptions, and stale menu state when the editor is disposed during active operations. ([#8422](https://github.com/tldraw/tldraw/pull/8422))
- Fix a memory leak where disposed `Editor` instances were retained through a shared throttled `updateHoveredShapeId` closure. ([#8439](https://github.com/tldraw/tldraw/pull/8439))
- Fix a memory leak where disposed `Editor` instances were retained through the `window.__tldraw__hardReset` global. ([#8476](https://github.com/tldraw/tldraw/pull/8476))
- Fix crash when isolating curved arrows with degenerate binding geometry. ([#8176](https://github.com/tldraw/tldraw/pull/8176))
- Fix all shapes disappearing when a labeled arrow has zero length (both endpoints overlapping), which previously propagated NaN values through the spatial index. ([#8329](https://github.com/tldraw/tldraw/pull/8329))
- Fix crash from duplicate fractional index keys in `kickoutOccludedShapes` during multiplayer sync. ([#8448](https://github.com/tldraw/tldraw/pull/8448))
- Fix crash in browsers without `OffscreenCanvas` support (e.g. older Safari) when preloading image alpha data. ([#8582](https://github.com/tldraw/tldraw/pull/8582))
- Fix over-softened corners and end artifacts when shift-clicking to draw straight line segments. ([#7977](https://github.com/tldraw/tldraw/pull/7977))
- Fix eraser not erasing shapes when starting a drag from inside a group's bounds. ([#8054](https://github.com/tldraw/tldraw/pull/8054))
- Fix draw-shape loop-closing sensitivity so closing works more consistently across zoom levels. ([#8293](https://github.com/tldraw/tldraw/pull/8293))
- Fix shadow artifact and oversized caret on notes and geo shapes at high zoom in dynamic size mode. ([#8378](https://github.com/tldraw/tldraw/pull/8378))
- Fix pattern fill not scaling correctly with dynamic size mode. ([#8441](https://github.com/tldraw/tldraw/pull/8441))
- Fix arrow labels rendered at incorrect size when editing at high zoom in dynamic size mode. ([#8451](https://github.com/tldraw/tldraw/pull/8451))
- Fix rotation performance regression where CSS `transform: scale()` was applied to shape labels even when dynamic size was inactive, forcing an unnecessary compositing layer on every shape. ([#8570](https://github.com/tldraw/tldraw/pull/8570))
- Fix spiky artifacts on draw-style geometric shapes by using a circular random offset distribution instead of a square one. ([#8466](https://github.com/tldraw/tldraw/pull/8466))
- Fix pasting into editable text shapes when the clipboard contains tldraw data. ([#8192](https://github.com/tldraw/tldraw/pull/8192))
- Fix slight positioning drift when pasting text onto the canvas. ([#8345](https://github.com/tldraw/tldraw/pull/8345))
- Fix right-clicking inside a multi-selection over a filled background shape changing the selection. ([#8434](https://github.com/tldraw/tldraw/pull/8434))
- Fix context menu briefly flashing when right-clicking to dismiss an already-open context menu. ([#8498](https://github.com/tldraw/tldraw/pull/8498))
- Fix context menu items accidentally being selected when right-clicking and quickly moving the mouse. ([#8499](https://github.com/tldraw/tldraw/pull/8499))
- Fix opacity slider drag not working on Safari due to `stopPropagation` blocking pointer events. ([#8519](https://github.com/tldraw/tldraw/pull/8519))
- Fix quick zoom mode (Z + Shift) cursor anchor and brush misalignment when the tldraw container is offset from the top-left of the window. ([#8520](https://github.com/tldraw/tldraw/pull/8520))
- Fix `Cmd+Shift+V` doing nothing when the clipboard contains an image; the shortcut now falls back to the regular paste flow when there is no plain-text payload. ([#8490](https://github.com/tldraw/tldraw/pull/8490))
- Fix keyboard shortcuts that change styles not marking the change as a style update, causing subsequent shapes to use the wrong defaults. ([#8599](https://github.com/tldraw/tldraw/pull/8599)) (contributed by [@danieljamesross](https://github.com/danieljamesross))
- Fix "back to content" button flickering when both it and the "move focus to canvas" button are visible. ([#8334](https://github.com/tldraw/tldraw/pull/8334))
- Fix intermittent `ResizeObserver loop` browser warnings when hovering over toolbar buttons. ([#8574](https://github.com/tldraw/tldraw/pull/8574))
- Fix missing sandbox attribute on GitHub Gist embeds. ([#8403](https://github.com/tldraw/tldraw/pull/8403))
- Restrict sandbox permissions for unknown/arbitrary embeds to mitigate security risks from untrusted content. ([#8404](https://github.com/tldraw/tldraw/pull/8404))
- Fix duplicate presence cursors appearing after login or logout in multiplayer sessions by filtering a session's own stale record from the connect handshake response. ([#8542](https://github.com/tldraw/tldraw/pull/8542))
- Fix sticky notes preserving stale attribution when cloned via a nib. ([#8614](https://github.com/tldraw/tldraw/pull/8614))
- Fix `Cmd+V` paste of tldraw-copied PNGs on Chrome 147 stable, where the custom-format clipboard entry returned a 0-byte blob. ([#8615](https://github.com/tldraw/tldraw/pull/8615))
- Fix note author attribution overflowing the sticky note width in SVG and PNG exports. ([#8664](https://github.com/tldraw/tldraw/pull/8664))
- Fix keyboard shortcuts not matching on alternative Latin keyboard layouts (Dvorak, Colemak, AZERTY) by replacing the `hotkeys-js` library with a small layout-aware native event matcher. ([#8669](https://github.com/tldraw/tldraw/pull/8669))
- Fix TypeScript signature mismatch on `ShapeUtil` base-class methods so overrides that need a `shape` parameter can declare it without errors. ([#8521](https://github.com/tldraw/tldraw/pull/8521))

## v4.5.0

[View on GitHub](https://github.com/tldraw/tldraw/releases/tag/v4.5.0)

This release adds click-through on transparent image pixels, SVG sanitization for pasted and dropped content, configurable embed definitions via `EmbedShapeUtil.configure()`, and correct high-DPI image sizing across platforms. It also includes a new `Editor.resizeToBounds()` method, TypeScript enum-to-const refactoring for Node strip-types compatibility, and various other improvements and bug fixes.

### What's new

#### Click-through on transparent image pixels ([#7942](https://github.com/tldraw/tldraw/pull/7942))

Clicking on transparent areas of PNG, WebP, GIF, and AVIF images now selects shapes behind the image instead of the image itself. This works with cropped, flipped, and circle-cropped images.

This is powered by a new `Geometry2d.ignoreHit(point)` method that allows geometries to reject successful hit tests.

#### 🔜 Configurable embed definitions ([#8034](https://github.com/tldraw/tldraw/pull/8034))

Embed definitions are now configured through `EmbedShapeUtil.configure()` instead of the static `setEmbedDefinitions()` method, which has been deprecated. The `embeds` prop on `Tldraw` is deprecated in favor of this approach.

```tsx
import { EmbedShapeUtil, DEFAULT_EMBED_DEFINITIONS } from 'tldraw'

const shapeUtils = [
	EmbedShapeUtil.configure({
		embedDefinitions: [...DEFAULT_EMBED_DEFINITIONS, myCustomEmbed],
	}),
]
```

<details>
<summary>Migration guide</summary>

Before:

```ts
EmbedShapeUtil.setEmbedDefinitions([...DEFAULT_EMBED_DEFINITIONS, myEmbed])
```

After:

```ts
const shapeUtils = [
	EmbedShapeUtil.configure({
		embedDefinitions: [...DEFAULT_EMBED_DEFINITIONS, myEmbed],
	}),
]
```

The `embeds` prop on `Tldraw` still works but is deprecated. Use `EmbedShapeUtil.configure()` instead.

</details>

#### SVG sanitization ([#7896](https://github.com/tldraw/tldraw/pull/7896), [#8087](https://github.com/tldraw/tldraw/pull/8087))

Pasted and dropped SVG content is now sanitized against XSS and data exfiltration. The sanitizer uses an allowlist-based approach that blocks script injection, event handlers, dangerous URI schemes, and external resource loading while preserving tldraw's own SVG output (including `<foreignObject>` text rendering and CSS font references).

The sanitizer is also available as a public export for custom content handling:

```ts
import { sanitizeSvg } from 'tldraw'

const cleanSvg = sanitizeSvg(untrustedSvgText)
```

### API changes

- 🔜 **`EmbedShapeUtil.setEmbedDefinitions()`** deprecated. Use `EmbedShapeUtil.configure({ embedDefinitions: [...] })` instead. ([#8034](https://github.com/tldraw/tldraw/pull/8034))
- 🔜 **`Tldraw` `embeds` prop** is deprecated. Configure embed definitions via `EmbedShapeUtil.configure()`. ([#8034](https://github.com/tldraw/tldraw/pull/8034))
- Add `Geometry2d.ignoreHit(point)` for rejecting hit tests on transparent pixels. ([#7942](https://github.com/tldraw/tldraw/pull/7942))
- Add `Editor.resizeToBounds(shapes, bounds)` for resizing shapes to fit target bounds. ([#8120](https://github.com/tldraw/tldraw/pull/8120))
- Add `Editor.getResizeScaleFactor()` for computing the dynamic size scale factor at the current zoom level. ([#8042](https://github.com/tldraw/tldraw/pull/8042))
- Add `sanitizeSvg(svgText: string)` export for sanitizing SVG content against XSS and data exfiltration. ([#7896](https://github.com/tldraw/tldraw/pull/7896))
- Add experimental `experimental__onDropOnCanvas` option to intercept canvas drop events. Return `true` from the callback to prevent the editor's default drop behavior. ([#7911](https://github.com/tldraw/tldraw/pull/7911))

  ```tsx
  <Tldraw
  	options={{
  		experimental__onDropOnCanvas: (point, event) => {
  			// Handle drop at page-space point
  			return true // prevent default
  		},
  	}}
  />
  ```

- Add optional `pixelRatio` property to `TLImageAsset` for correct high-DPI image sizing. Detected automatically from PNG metadata. ([#8163](https://github.com/tldraw/tldraw/pull/8163))
- Replace TypeScript enums (`MigrationFailureReason`, `PORTRAIT_BREAKPOINT`) with `const` object + type alias pattern for compatibility with Node's built-in TypeScript support (strip-types). Values are unchanged. ([#8084](https://github.com/tldraw/tldraw/pull/8084))

### Improvements

- Improve arrow component rendering performance with finer-grained reactivity. ([#8167](https://github.com/tldraw/tldraw/pull/8167))
- Simplify paste-parent selection to use `canReceiveNewChildrenOfType` instead of frame-specific checks. Prevent edge-only overlap from auto-reparenting pasted shapes into adjacent frames. ([#8057](https://github.com/tldraw/tldraw/pull/8057))
- Save link and alt-text values when clicking outside the editor instead of discarding changes. ([#8037](https://github.com/tldraw/tldraw/pull/8037))
- Add SVG sanitization on paste and file drop to prevent XSS and data exfiltration. ([#7896](https://github.com/tldraw/tldraw/pull/7896))
- Fix circular dependencies across `@tldraw/state`, `@tldraw/editor`, and `@tldraw/tldraw` packages to improve compatibility with Jest mocking and tree-shaking. ([#7935](https://github.com/tldraw/tldraw/pull/7935))
- Fix high-DPI image sizing to work correctly across macOS and Windows by detecting the source DPI baseline from PNG metadata. ([#8163](https://github.com/tldraw/tldraw/pull/8163))

### Bug fixes

- Fix shapes pasted with Ctrl+V not being parented to a frame when they land inside one. ([#7938](https://github.com/tldraw/tldraw/pull/7938))
- Fix a crash when cropping custom shapes that don't include `isCircle` in their crop schema. ([#7931](https://github.com/tldraw/tldraw/pull/7931))
- Fix a crash when loading session state without a `currentPageId` (e.g. when using deep links). ([#7994](https://github.com/tldraw/tldraw/pull/7994))
- Fix U+2028/U+2029 line separators breaking chunked sync messages. ([#7918](https://github.com/tldraw/tldraw/pull/7918))
- Fix "Download original" not triggering a download for cross-origin assets. ([#8090](https://github.com/tldraw/tldraw/pull/8090))
- Fix arrow endpoints terminating at invisible clipped shape boundaries instead of at the frame edge. ([#7932](https://github.com/tldraw/tldraw/pull/7932))
- Fix sticky notes having a hard shadow instead of a soft drop shadow when exported as SVG. ([#7934](https://github.com/tldraw/tldraw/pull/7934))
- Fix rich text toolbar staying open when the editing shape is deleted by another user. ([#8050](https://github.com/tldraw/tldraw/pull/8050))
- Fix SVG sanitizer stripping embedded SVG data URIs on `<image>` elements. Nested SVGs are now recursively sanitized instead of blocked. ([#8087](https://github.com/tldraw/tldraw/pull/8087))
- Fix `TldrawSelectionForeground` crashing when used without `TldrawUiContextProvider`. ([#8011](https://github.com/tldraw/tldraw/pull/8011))
- Fix dynamic-size shapes losing shadows and dashes too early when zoomed out. ([#8040](https://github.com/tldraw/tldraw/pull/8040))
- Fix a crash when resizing draw or highlight shapes to zero width or height. ([#8067](https://github.com/tldraw/tldraw/pull/8067))
- Fix crash when enabling Debug SVG with shapes on the canvas. ([#8101](https://github.com/tldraw/tldraw/pull/8101))
- Fix `localOffset` mutation bug in `stretchShapes` when shapes have parent transforms. ([#8120](https://github.com/tldraw/tldraw/pull/8120))
- Fix arrow SVG export producing invalid negative `<foreignObject>` dimensions when arrows have no text label. ([#8137](https://github.com/tldraw/tldraw/pull/8137))
- Fix arrow endpoint flickering when anchors are at exact shape boundaries. ([#8130](https://github.com/tldraw/tldraw/pull/8130))
- Fix shapes dragged from the toolbar not respecting dynamic size mode. ([#8042](https://github.com/tldraw/tldraw/pull/8042))
- Fix dragging unselected shapes that have an `onClick` handler. ([#7936](https://github.com/tldraw/tldraw/pull/7936))
- Fix false positive "multiple instances" warning in Next.js dev mode. ([#7933](https://github.com/tldraw/tldraw/pull/7933))
- Fix missing alt text on rendered image shapes in some cases. ([#8158](https://github.com/tldraw/tldraw/pull/8158))
- Fix drawing on tablets that report zero pen pressure. ([#5693](https://github.com/tldraw/tldraw/pull/5693))
- Fix `create-tldraw` CLI to always create a subdirectory from the project name. ([#8161](https://github.com/tldraw/tldraw/pull/8161))

---

### Patch releases

#### v4.5.3

- Fix dynamic import file extension handling. ([#8278](https://github.com/tldraw/tldraw/pull/8278))

[View release on GitHub](https://github.com/tldraw/tldraw/releases/tag/v4.5.3)

#### v4.5.4

- Fix all shapes disappearing when a labeled arrow has zero length due to NaN propagation through the spatial index. ([#8351](https://github.com/tldraw/tldraw/pull/8351))

[View release on GitHub](https://github.com/tldraw/tldraw/releases/tag/v4.5.4)

#### v4.5.5

- Fix camera state getting permanently stuck at 'moving' when the editor is disposed mid-camera-transition. ([#8396](https://github.com/tldraw/tldraw/pull/8396))

[View release on GitHub](https://github.com/tldraw/tldraw/releases/tag/v4.5.5)

#### v4.5.6

- Fix crash when isolating curved arrows with degenerate binding geometry. ([#8390](https://github.com/tldraw/tldraw/pull/8390))

[View release on GitHub](https://github.com/tldraw/tldraw/releases/tag/v4.5.6)

## v4.4.0

[View on GitHub](https://github.com/tldraw/tldraw/releases/tag/v4.4.0)

This release adds a consolidated `options` prop, quick zoom navigation, a fill styles dropdown, a new `TldrawUiSelect` component, and shape-aware binding checks. It also includes 2D canvas rendering for shape indicators, R-tree spatial indexing, telestrator-style laser behavior, significant performance improvements for large canvases, and various bug fixes.

### What's new

#### 2D canvas rendering for shape indicators ([#7708](https://github.com/tldraw/tldraw/pull/7708))

Shape indicators (selection outlines, hover states) now render using a 2D canvas instead of SVG elements. This significantly improves performance when selecting or hovering over many shapes, with up to 25x faster rendering in some scenarios.

Custom shapes can opt into canvas indicators by implementing the new `getIndicatorPath()` method on their ShapeUtil, and marking `useLegactIndicator` to return false:

```tsx
class MyShapeUtil extends ShapeUtil<MyShape> {
	getIndicatorPath(shape: MyShape): TLIndicatorPath | undefined {
		return {
			path: new Path2D(),
			// optional clip path for complex shapes like arrows with labels
		}
	}

	// Return false to use the new canvas indicators (default is true for backwards compatibility)
	useLegacyIndicator(): boolean {
		return false
	}
}
```

#### Quick zoom navigation ([#7801](https://github.com/tldraw/tldraw/pull/7801), [#7836](https://github.com/tldraw/tldraw/pull/7836))

Press `z` then hold Shift to zoom out and see the whole canvas ("eagle eye" view). A viewport brush appears showing where you'll zoom to. Move the cursor to pick a location and release Shift to zoom there. Press Escape to cancel and return to the original view.

#### Fill styles dropdown ([#7885](https://github.com/tldraw/tldraw/pull/7885))

The style panel now exposes additional fill styles (pattern, fill, lined-fill) through a dropdown picker after the solid button. The first three fill options (none, semi, solid) remain as inline buttons.

#### User preference: Invert mouse wheel zoom direction ([#7732](https://github.com/tldraw/tldraw/pull/7732))

Added a new user preference to invert mouse wheel zoom direction. Some users prefer "natural" scrolling behavior where scrolling down zooms out, which this option now enables.

Access it via Menu → Preferences → Input device → Invert mouse zoom.

#### Performance improvements ([#7676](https://github.com/tldraw/tldraw/pull/7676), [#7826](https://github.com/tldraw/tldraw/pull/7826), [#7840](https://github.com/tldraw/tldraw/pull/7840), [#7657](https://github.com/tldraw/tldraw/pull/7657))

This release includes several performance improvements for large canvases:

- **R-tree spatial indexing**: Shape queries now use an R-tree (RBush) for O(log n) lookups instead of O(n) iteration, significantly improving brushing, scribble selection, and erasing with many shapes on the canvas. The spatial index is maintained internally and accessed through existing methods like `editor.getShapesAtPoint()` and `editor.getShapeAtPoint()`.
- **Faster panning**: Hover hit-testing is now skipped during camera movement, reducing work while panning through large documents.
- **Reduced allocations**: Optimized Set comparisons, reduced memory allocations, and added string hash caching for better performance in large canvases and multiplayer rooms.
- **Smarter network scheduling**: Solo-mode network traffic is reduced by throttling to 1 FPS when no collaborators are present.

#### Re-designed laser pointer ([#7681](https://github.com/tldraw/tldraw/pull/7681))

The laser pointer now behaves like a telestrator: all strokes remain visible while you're drawing and fade together when you stop. Previously, each stroke segment would fade independently, creating a trailing effect.

This is powered by a new generic session system on `ScribbleManager`. Sessions group multiple scribbles together and control how they fade:

```tsx
// Start a grouped session (used internally by the laser tool)
const sessionId = editor.scribbles.startSession({
	fadeMode: 'grouped',
	idleTimeoutMs: 1200,
	fadeDurationMs: 500,
})

// Add scribbles and points to a session
editor.scribbles.addScribbleToSession(sessionId, { color: 'laser' })
editor.scribbles.addPointToSession(sessionId, scribbleId, x, y)

// Stop or clear a session
editor.scribbles.stopSession(sessionId)
editor.scribbles.clearSession(sessionId)
```

New option in `TldrawOptions`:

- `laserFadeoutMs` (default: 500ms) - How long to fade all laser scribbles after the session ends

#### Image pipeline starter template ([#7863](https://github.com/tldraw/tldraw/pull/7863))

A new "Image pipeline" starter template is available via `npx create-tldraw`. It provides a visual node-based canvas for building AI image generation workflows, with custom node shapes, typed port connections, pipeline regions with play/stop controls, and a DAG-based execution engine backed by a Cloudflare Worker API.

#### Agent starter template improvements ([#7640](https://github.com/tldraw/tldraw/pull/7640))

The agent starter template has been restructured around a manager-based architecture for better modularity and extensibility. It now includes a mode system for controlling agent capabilities per mode, action schema registries, prompt part definitions, canvas linting, and user action tracking. The template also renames "SimpleShape" to "FocusedShape" for clarity.

#### TldrawUiSelect component ([#7566](https://github.com/tldraw/tldraw/pull/7566))

New select dropdown primitive wrapping Radix UI's Select, following existing tldraw UI patterns.

#### 🔜 Consolidated options prop ([#7888](https://github.com/tldraw/tldraw/pull/7888))

The `cameraOptions`, `textOptions`, and `deepLinks` props on `Tldraw`, `TldrawEditor`, and `TldrawImage` are now consolidated into the `options` prop. The standalone props are deprecated but still work for backward compatibility.

```tsx
// Before
<Tldraw cameraOptions={{ isLocked: true }} deepLinks textOptions={{ ... }} />

// After
<Tldraw options={{ camera: { isLocked: true }, deepLinks: true, text: { ... } }} />
```

<details>
<summary>Migration guide</summary>

Replace standalone props with equivalent `options` fields:

- `cameraOptions={...}` → `options={{ camera: { ... } }}`
- `textOptions={...}` → `options={{ text: { ... } }}`
- `deepLinks` or `deepLinks={...}` → `options={{ deepLinks: true }}` or `options={{ deepLinks: { ... } }}`

The deprecated props still work. When both the deprecated prop and the `options` field are provided, the `options` value takes precedence.

</details>

### API changes

- 🔜 **`TldrawEditorBaseProps.cameraOptions`**, **`TldrawEditorBaseProps.textOptions`**, **`TldrawEditorBaseProps.deepLinks`** deprecated in favor of `options.camera`, `options.text`, `options.deepLinks`. ([#7888](https://github.com/tldraw/tldraw/pull/7888))
- 🔜 **`TLShapeUtilCanBindOpts.fromShapeType`** and **`toShapeType`** replaced with `fromShape` and `toShape` accepting `TLShape | { type }`. ([#7821](https://github.com/tldraw/tldraw/pull/7821))
- Remove `editor.spatialIndex` from public API. The spatial index is now internal; use `editor.getShapesAtPoint()` and `editor.getShapeAtPoint()` for shape queries. ([#7699](https://github.com/tldraw/tldraw/pull/7699))
- Add `TldrawOptions.camera`, `TldrawOptions.text`, and `TldrawOptions.deepLinks` fields to the `options` prop. ([#7888](https://github.com/tldraw/tldraw/pull/7888))
- Add `TldrawUiSelect`, `TldrawUiSelectTrigger`, `TldrawUiSelectValue`, `TldrawUiSelectContent`, `TldrawUiSelectItem` components and associated prop types. Add `iconTypes` export for enumerating available icons. ([#7566](https://github.com/tldraw/tldraw/pull/7566))
- Add `useCanApplySelectionAction()` hook for checking if selection actions should be enabled. ([#7811](https://github.com/tldraw/tldraw/pull/7811))
- Add `TLInstance.cameraState: 'idle' | 'moving'` for tracking camera movement state. ([#7826](https://github.com/tldraw/tldraw/pull/7826))
- Add `quickZoomPreservesScreenBounds` to `TldrawOptions`. ([#7836](https://github.com/tldraw/tldraw/pull/7836))
- Add `fillExtra` to `STYLES` object for additional fill style options. ([#7885](https://github.com/tldraw/tldraw/pull/7885))
- Make `Editor.getShapeIdsInsideBounds()` public. ([#7863](https://github.com/tldraw/tldraw/pull/7863))
- Add `ShapeUtil.getIndicatorPath()` method and `TLIndicatorPath` type for canvas-based indicator rendering. Add `ShapeUtil.useLegacyIndicator()` to control whether shapes use SVG or canvas indicators. ([#7708](https://github.com/tldraw/tldraw/pull/7708))
- Add `isZoomDirectionInverted` to `TLUserPreferences` interface and `UserPreferencesManager.getIsZoomDirectionInverted()` method. Add `ToggleInvertZoomItem` component export and `toggle-invert-zoom` action. ([#7732](https://github.com/tldraw/tldraw/pull/7732))
- Add `complete` to `TL_SCRIBBLE_STATES` enum and `ScribbleManager.complete(id)` method for marking scribbles as complete before fading. ([#7760](https://github.com/tldraw/tldraw/pull/7760))
- Add scribble session system: `ScribbleManager.startSession()`, `stopSession()`, `clearSession()`, `extendSession()`, `isSessionActive()`, `addScribbleToSession()`, `addPointToSession()`, and `ScribbleSessionOptions` type. Add `LaserTool.getSessionId()`. Add `laserFadeoutMs` to `TldrawOptions`. ([#7681](https://github.com/tldraw/tldraw/pull/7681))
- Add `FpsScheduler` class to create FPS-throttled function queues with configurable target rates. ([#7418](https://github.com/tldraw/tldraw/pull/7418))

### Improvements

- Improve performance in large canvases and multiplayer rooms by optimizing Set comparisons, reducing memory allocations, and caching string hashes. ([#7840](https://github.com/tldraw/tldraw/pull/7840))
- Improve panning performance in large documents by skipping hover hit-testing during camera movement. ([#7826](https://github.com/tldraw/tldraw/pull/7826))
- Improve performance when translating arrows together with bound shapes by skipping unnecessary reparenting. ([#7733](https://github.com/tldraw/tldraw/pull/7733))
- Add R-tree spatial indexing for O(log n) shape queries, improving performance of brushing, selection, and erasing with many shapes. ([#7676](https://github.com/tldraw/tldraw/pull/7676))
- Improve laser pointer to keep all strokes visible during a session (telestrator pattern), with strokes fading together when drawing stops. ([#7681](https://github.com/tldraw/tldraw/pull/7681))
- Improve laser pointer strokes with proper taper when lifting the pointer by adding a 'complete' state to the scribble lifecycle. ([#7760](https://github.com/tldraw/tldraw/pull/7760))
- Improve quick zoom to use zoom-to-fit instead of fixed 5%, add "quick peek" behavior, and add edge scrolling. ([#7836](https://github.com/tldraw/tldraw/pull/7836))
- Remove `core-js` polyfill dependency from `@tldraw/editor`. ([#7769](https://github.com/tldraw/tldraw/pull/7769))
- Reduce network traffic by squashing pending push requests before sending, so rapid edits result in fewer network calls. ([#7724](https://github.com/tldraw/tldraw/pull/7724))
- Reduce solo-mode network traffic by using a dedicated FPS-based scheduler that throttles to 1 FPS when no collaborators are present. ([#7657](https://github.com/tldraw/tldraw/pull/7657))
- Improve performance by separating UI and network scheduling into independent queues with configurable target FPS. ([#7418](https://github.com/tldraw/tldraw/pull/7418))
- Improve frame label sizing on smaller viewports and when zoomed out. ([#7746](https://github.com/tldraw/tldraw/pull/7746))
- Add image pipeline starter template with visual node-based AI image generation workflows. ([#7863](https://github.com/tldraw/tldraw/pull/7863))
- Restructure agent starter template with manager-based architecture and mode system. ([#7640](https://github.com/tldraw/tldraw/pull/7640))

### Bug fixes

- Fix geo shapes with text not being resizable to a smaller size. ([#7878](https://github.com/tldraw/tldraw/pull/7878))
- Fix shapes exploding when resizing unaligned arrows. ([#7855](https://github.com/tldraw/tldraw/pull/7855))
- Fix Safari pinch zoom resetting selection to previous shapes. ([#7777](https://github.com/tldraw/tldraw/pull/7777))
- Fix toggle-lock action firing when no shapes are selected. ([#7815](https://github.com/tldraw/tldraw/pull/7815))
- Fix menu items (zoom to selection, cut, copy, delete) being enabled when not in select tool. ([#7811](https://github.com/tldraw/tldraw/pull/7811))
- Fix excessive tab indentation in text shapes (now 2 spaces instead of 8). ([#7796](https://github.com/tldraw/tldraw/pull/7796))
- Fix canvas-in-front elements (cursors, following indicators) appearing in front of UI panels instead of behind them. ([#7865](https://github.com/tldraw/tldraw/pull/7865))
- Fix license console message colors for warnings and errors. ([#7850](https://github.com/tldraw/tldraw/pull/7850))
- Fix asset resolution being delayed by 500ms when updating an image shape's asset. ([#7612](https://github.com/tldraw/tldraw/pull/7612))
- Fix Durable Object SQLite migration in the multiplayer Cloudflare template. ([#7829](https://github.com/tldraw/tldraw/pull/7829), [#7832](https://github.com/tldraw/tldraw/pull/7832), [#7834](https://github.com/tldraw/tldraw/pull/7834), [#7835](https://github.com/tldraw/tldraw/pull/7835))
- Fix `zoomToFit` and `getCurrentPageBounds` to ignore hidden shapes when computing bounds. ([#7770](https://github.com/tldraw/tldraw/pull/7770))
- Fix collaborator shape indicators not rendering after the canvas indicator change. ([#7759](https://github.com/tldraw/tldraw/pull/7759))
- Fix rich text content not updating correctly with message squashing due to reference comparison. ([#7758](https://github.com/tldraw/tldraw/pull/7758))
- Fix keyboard shortcut menu item labels to use consistent ellipsis formatting. ([#7757](https://github.com/tldraw/tldraw/pull/7757))
- Fix spatial index not removing shapes when moved to a different page. ([#7700](https://github.com/tldraw/tldraw/pull/7700))
- Fix tldraw failing to load in CJS environments (tsx, ts-node, Jest) due to ESM-only rbush dependency. ([#7905](https://github.com/tldraw/tldraw/pull/7905))

---

### Patch releases

#### v4.4.1

- Fix tooltip positioning when tldraw is rendered inside a container with CSS transforms. ([#8171](https://github.com/tldraw/tldraw/pull/8171))

[View release on GitHub](https://github.com/tldraw/tldraw/releases/tag/v4.4.1)

## v4.3.0

[View on GitHub](https://github.com/tldraw/tldraw/releases/tag/v4.3.0)

This release introduces several significant changes: a new pattern for defining custom shape/binding typings, pluggable storage for `TLSocketRoom` with a new SQLite option, reactive `editor.inputs`, and optimized draw shape encoding. It also adds various other API improvements, performance optimizations, and bug fixes, including better support for React 19.

---

#### 💥 New pattern for defining custom shape/binding types ([#7091](https://github.com/tldraw/tldraw/pull/7091))

We've improved the developer experience of working with custom shape and binding types. There's now less boilerplate and fewer gotchas when using tldraw APIs in a type-safe manner.

This is a minor breaking change at the type level—your code will still run, but you'll get TypeScript errors until you migrate.

<details>
<summary>Migration guide</summary>

When _declaring_ types for custom shapes, you can now use TypeScript's module augmentation feature to provide more specific types for the custom shape.

Before:

```ts
import { TLBaseShape } from 'tldraw'

// Shapes were defined by using the helper TLBaseShape type
type MyShape = TLBaseShape`<'my-shape', { w: number; h: number; text: string }>`
```

After:

```ts
import { TLShape } from 'tldraw'

const MY_SHAPE = 'my-shape'

// We now use TypeScript's module augmentation feature to allow
// extending the builtin TLShape type.
declare module 'tldraw' {
	export interface TLGlobalShapePropsMap {
		[MY_SHAPE]: { w: number; h: number; text: string }
	}
}

type MyShape = TLShape`<typeof MY_SHAPE>`
```

The benefit of this new system is that Editor APIs such as `createShape` now know about your custom shapes automatically:

```ts
// Just works - TypeScript validates props and provides autocomplete
editor.createShape({ type: 'my-shape', props: { w: 100, h: 100, text: 'Hello' } })

// Will cause a TypeScript error for `text`
editor.createShape({ type: 'my-shape', props: { w: 100, h: 100, text: 123 } })
```

The same pattern applies to custom bindings — augment `TLGlobalBindingPropsMap` and use `TLBinding<'binding-name'>`:

```ts
declare module 'tldraw' {
	export interface TLGlobalBindingPropsMap {
		'my-binding': { offset: number }
	}
}

type MyBinding = TLBinding`<'my-binding'>`
```

See the [Custom Shapes Guide](https://tldraw.dev/docs/shapes#custom-shapes) and the [Pin Bindings example](https://github.com/tldraw/tldraw/tree/main/apps/examples/src/examples/shapes/tools/pin-bindings) for details.

**After registering, remove the `TLBaseShape` import** — it's no longer used and will trip lint rules that flag unused imports.

**Shape type names are global.** `TLGlobalShapePropsMap` is a single shared registry, so two custom shapes that previously used the same type name in different files now collide. Pick a unique name for each, and remember the rename ripples beyond the type alias:

- `static override type = '...'` on the `ShapeUtil` subclass
- Every `editor.createShape({ type: '...' })` and `editor.updateShape({ type: '...' })` call site
- The `TLGlobalShapePropsMap` entry itself
- Any persisted snapshot or migration JSON that references the old name

**Use `as const` on the static `type` field.** TypeScript may widen `static override type = 'my-shape'` to `string`, which fails the new constraint. Add `as const` to keep it a string literal:

```ts
class MyShapeUtil extends ShapeUtil`<MyShape>` {
	static override type = 'my-shape' as const
	// ...
}
```

The same fix applies to `BaseBoxShapeTool` (and other tool) subclasses, which expose `static override shapeType = '...'`. If you see TS2416 on a tool's `shapeType`, add `as const` there too. The two fields are easy to confuse — check both.

**Heterogeneous `createShapes`/`updateShapes` arrays may need a cast.** Because `TLShape` is now a discriminated union over the global props map, a mapped array of mixed shape types no longer narrows automatically. For homogeneous arrays where every item shares one shape type, `as const` on the `type` field in the mapped object literal is enough. For genuinely heterogeneous arrays, go straight to `as TLShapePartial[]` (or `as TLCreateShapePartial[]` for `createShapes`):

```ts
// Heterogeneous update — the union doesn't narrow
editor.updateShapes(
	shapes.map((s) => ({ id: s.id, type: s.type, x: s.x + 10 })) as TLShapePartial[]
)
```

Don't reach for `satisfies TLShapePartial` here — `TLShapePartial` is distributive over the `TLShape` union, so `satisfies` enforces a literal-per-variant constraint that mapped-over heterogeneous shapes can't meet. The cast is the right tool for this specific shape of code.

</details>

(contributed by [@Andarist](https://github.com/Andarist))

---

#### Pluggable storage for TLSocketRoom + SQLite support ([#7320](https://github.com/tldraw/tldraw/pull/7320), [#7123](https://github.com/tldraw/tldraw/pull/7123))

We've refactored the `TLSocketRoom` API to support a pluggable storage layer. We're providing two implementations:

- **`SQLiteSyncStorage`** – Automatically persists room state to SQLite. Recommended for production.
- **`InMemorySyncStorage`** – Keeps state in memory with manual persistence via callbacks (previous built-in behavior).

**We recommend switching to `SQLiteSyncStorage` if your environment supports SQLite** (Cloudflare Durable Objects, Node.js, Bun, Deno). It provides automatic persistence, lower memory usage, and faster startup times.

<details>
<summary>Why SQLite?</summary>

- **Automatic persistence**: Data survives process restarts without manual snapshot handling
- **Lower memory usage**: No need to keep entire documents in memory
- **Faster startup**: No need to load the document into memory before accepting socket connections
- **Simpler code**: No more `onChange` callbacks and manual persistence logic

</details>

<details>
<summary>Platform support</summary>

| Platform                   | Wrapper                          | SQLite Library                    |
| -------------------------- | -------------------------------- | --------------------------------- |
| Cloudflare Durable Objects | `DurableObjectSqliteSyncWrapper` | Built-in `ctx.storage`            |
| Node.js/Deno               | `NodeSqliteWrapper`              | `better-sqlite3` or `node:sqlite` |

See the [Cloudflare template](https://github.com/tldraw/tldraw-sync-cloudflare) and the [Node server example](https://github.com/tldraw/tldraw/tree/main/templates/simple-server-example) respectively. Bun support should be straightforward to add.

</details>

<details>
<summary>Migration guide</summary>

Existing code continues to work, however we have deprecated the following `TLSocketRoom` options:

- `initialSnapshot`
- `onDataChange`

These are replaced by the new `storage` option. We've also deprecated the `TLSocketRoom.updateStore` method, which has been supplanted by `storage.transaction`.

Before:

```ts
const existingSnapshot = loadExistingSnapshot()

const room = new TLSocketRoom({
	initialSnapshot: existingSnapshot,
	onDataChange: () => {
		persistSnapshot(room.getCurrentSnapshot())
	},
})
```

If you want to keep the same behavior with in-memory document storage and manual persistence:

```ts
import { InMemorySyncStorage, TLSocketRoom } from '@tldraw/sync-core'

const room = new TLSocketRoom({
	storage: new InMemorySyncStorage({
		snapshot: existingSnapshot,
		onChange() {
			saveToDatabase(storage.getSnapshot())
		},
	}),
})
```

However, we recommend switching to SQLite. Users of our Cloudflare template should follow the migration guide on the [sync docs page](https://tldraw.dev/docs/sync).

If you're using `TLSocketRoom` on Node, creating the room should end up looking something like this:

```ts
import Database from 'better-sqlite3'
import { SQLiteSyncStorage, NodeSqliteWrapper, TLSocketRoom, RoomSnapshot } from '@tldraw/sync-core'

async function createRoom(roomId: string) {
	const db = new Database(`path/to/${roomId}.db`)
	const sql = new NodeSqliteWrapper(db)
	let snapshot: RoomSnapshot | undefined = undefined
	if (!SQLiteSyncStorage.hasBeenInitialized(sql)) {
		// This db hasn't been used before, so if it's a pre-existing
		// document, load the legacy room snapshot
		snapshot = await loadExistingSnapshot()
	}

	const storage = new SQLiteSyncStorage({ sql, snapshot })
	return new TLSocketRoom({
		storage,
		onSessionRemoved(room, args) {
			if (args.numSessionsRemaining === 0) {
				room.close()
				db.close()
			}
		},
	})
}
```

</details>

---

#### Optimized draw shape encoding ([#7364](https://github.com/tldraw/tldraw/pull/7364), [#7710](https://github.com/tldraw/tldraw/pull/7710))

Draw and highlight shape point data is now stored using a compact delta-encoded binary format instead of JSON arrays. This reduces storage size by approximately 80% while preserving stroke fidelity.

<details>
<summary>Breaking change details</summary>

If you were reading or writing draw shape data programatically you might need to update your code to use the new format.

- `TLDrawShapeSegment.points` renamed to `.path` and changed from `VecModel[]` to `string` (base64-encoded)
- Added `scaleX` and `scaleY` properties to draw and highlight shapes
- New exports: `b64Vecs` encoding utilities, e.g. `getPointsFromDrawSegment` helper. Use this if you need to manually read/write point data.

Existing documents are automatically migrated.

</details>

---

#### Reactive inputs ([#7312](https://github.com/tldraw/tldraw/pull/7312))

Refactored `editor.inputs` to use reactive atoms via the new `InputsManager` class. All input state is now accessed via getter methods (e.g., `editor.inputs.getCurrentPagePoint()`, `editor.inputs.getShiftKey()`). Direct property access is deprecated but still supported for backwards compatibility.

### API changes

- 💥 **`DefaultTopPanel`** export removed from `tldraw`. The top panel component for displaying the offline indicator is now handled internally by `PeopleMenu`. ([#7568](https://github.com/tldraw/tldraw/pull/7568))
- 💥 **`TextDirection`** export removed from `tldraw`. Use TipTap's native `TextDirection` extension instead. The `richTextValidator` now includes an optional `attrs` property - a migration may be necessary for older clients/custom shapes. ([#7304](https://github.com/tldraw/tldraw/pull/7304))
- Add `tlenvReactive` atom to `@tldraw/editor` for reactive environment state tracking, including coarse pointer detection that updates when users switch between mouse and touch input. ([#7296](https://github.com/tldraw/tldraw/pull/7296))
- Add `hideAllTooltips()` helper function for programmatically dismissing tooltips. ([#7288](https://github.com/tldraw/tldraw/pull/7288))
- Add `zoomToFitPadding` option to `TldrawOptions` to customize the default padding used by zoom-to-fit operations. ([#7602](https://github.com/tldraw/tldraw/pull/7602))
- Add `snapThreshold` option to `TldrawOptions` for configuring the snap distance, defaulting to 8 screen pixels. ([#7543](https://github.com/tldraw/tldraw/pull/7543))
- Add `resizeChildren` configuration option to `FrameShapeUtil` to allow frame children to be resized proportionally when the frame is resized. ([#7526](https://github.com/tldraw/tldraw/pull/7526))
- Add `Editor.canEditShape()` and `Editor.canCropShape()` methods to centralize shape permission checks. Add `ShapeUtil.canEditWhileLocked()` for shapes that remain editable when locked. ([#7361](https://github.com/tldraw/tldraw/pull/7361))
- Add `editor.getDebouncedZoomLevel()` and `editor.getEfficientZoomLevel()` methods for improved zoom performance on dense canvases. Add `debouncedZoom` and `debouncedZoomThreshold` options. ([#7235](https://github.com/tldraw/tldraw/pull/7235))
- Add configurable `showTextOutline` option to `TextShapeUtil`, `ArrowShapeUtil`, and `GeoShapeUtil` via `.configure()` pattern. ([#7314](https://github.com/tldraw/tldraw/pull/7314))
- Export freehand stroke utilities: `getStroke`, `getStrokeOutlinePoints`, and `setStrokePointRadii`. ([#7400](https://github.com/tldraw/tldraw/pull/7400)) (contributed by [@VimHax](https://github.com/VimHax))
- Add `Box.isValid()` method to check for finite coordinates. ([#7532](https://github.com/tldraw/tldraw/pull/7532))
- Add `spacebarPanning` option to control whether spacebar activates pan mode. ([#7312](https://github.com/tldraw/tldraw/pull/7312))
- Introduce pluggable `TLSyncStorage` API for `TLSocketRoom`. The `initialSnapshot` and `onDataChange` options are now deprecated in favor of the new `storage` option. ([#7123](https://github.com/tldraw/tldraw/pull/7123))
- Export `DefaultLabelColorStyle` which is necessary for rich text in custom shapes. ([#7114](https://github.com/tldraw/tldraw/pull/7114))

#### Improvements

- Improve coarse pointer detection by replacing CSS media queries with a reactive `data-coarse` attribute that updates when users switch between mouse and touch input. ([#7404](https://github.com/tldraw/tldraw/pull/7404))
- Add CSS containment to main toolbar and text measurement element for improved rendering performance. ([#7406](https://github.com/tldraw/tldraw/pull/7406)) ([#7407](https://github.com/tldraw/tldraw/pull/7407))
- Improve cross-realm support by scoping canvas event listeners to the editor container's ownerDocument. ([#7113](https://github.com/tldraw/tldraw/pull/7113))
- Simplify ImmutableMap implementation for better code clarity. ([#7431](https://github.com/tldraw/tldraw/pull/7431))
- Improve code readability in number validator by using `Number.isFinite()` instead of arithmetic trick. ([#7374](https://github.com/tldraw/tldraw/pull/7374))
- Upgrade to React 19 with all necessary type and configuration changes for compatibility. ([#7317](https://github.com/tldraw/tldraw/pull/7317))
- Improve signal graph traversal performance by eliminating per-recursion closure allocations. ([#7430](https://github.com/tldraw/tldraw/pull/7430))
- Optimize object utility functions. ([#7432](https://github.com/tldraw/tldraw/pull/7432))
- Use in-place sorting in parentsToChildren derivation. ([#7433](https://github.com/tldraw/tldraw/pull/7433))
- Optimize notVisibleShapes derivation with inlined bounds checks. ([#7429](https://github.com/tldraw/tldraw/pull/7429))
- Cache label size measurements with WeakCache for improved geo shape performance. ([#7412](https://github.com/tldraw/tldraw/pull/7412))
- Optimize validators with fast paths and production inlining. ([#7373](https://github.com/tldraw/tldraw/pull/7373))

#### Bug fixes

- Fix migrations for draw and highlight shapes to be idempotent, preventing errors when migrations run multiple times. ([#7389](https://github.com/tldraw/tldraw/pull/7389))
- Fix dot detection in draw and highlight shapes after the point compression change. ([#7365](https://github.com/tldraw/tldraw/pull/7365))
- Fix clicking a shape's text label while editing to re-focus the input and select all text. ([#7342](https://github.com/tldraw/tldraw/pull/7342))
- Fix pasting at cursor to correctly account for frames and parent containers. ([#7277](https://github.com/tldraw/tldraw/pull/7277))
- Fix editing mode to exit when dragging causes the text input to blur. ([#7291](https://github.com/tldraw/tldraw/pull/7291))
- Fix CommonJS build issues with TipTap imports in rich text module. ([#7282](https://github.com/tldraw/tldraw/pull/7282))
- Fix iOS automatically zooming in on input fields by ensuring 16px minimum font size. ([#7118](https://github.com/tldraw/tldraw/pull/7118))
- Fix `distanceToLineSegment` returning squared distance instead of actual distance, causing hit testing (eraser, scribble select) to be too strict. ([#7610](https://github.com/tldraw/tldraw/pull/7610)) (contributed by [@arpit-goblins](https://github.com/arpit-goblins))
- Fix `zoomToSelection` to toggle between 100% zoom and zoom-to-fit behavior. ([#7536](https://github.com/tldraw/tldraw/pull/7536))
- Fix export of SVG markers by handling fragment-only URLs correctly. ([#7506](https://github.com/tldraw/tldraw/pull/7506)) (contributed by [@PidgeyBE](https://github.com/PidgeyBE))
- Restore wheel and pinch canvas event emission. ([#6834](https://github.com/tldraw/tldraw/pull/6834)) (contributed by [@swdev33](https://github.com/swdev33))
- Fix context menu submenu flickering when hovered. ([#6837](https://github.com/tldraw/tldraw/pull/6837)) (contributed by [@swdev33](https://github.com/swdev33))
- Fix rotated shape positions not being restored after flipping twice. ([#7359](https://github.com/tldraw/tldraw/pull/7359))
- Fix keyboard shortcuts and clipboard events not working when `hideUi` is true. ([#7367](https://github.com/tldraw/tldraw/pull/7367))
- Fix elbow arrows routing incorrectly when using dynamic sizing at high zoom levels. ([#7424](https://github.com/tldraw/tldraw/pull/7424))
- Allow fullscreen for embed shapes. ([#7417](https://github.com/tldraw/tldraw/pull/7417))
- Move mobile rotate handle to bottom for image and video shapes to accommodate the contextual toolbar. ([#6727](https://github.com/tldraw/tldraw/pull/6727))
- Fix dropdown menu items incorrectly displaying an icon on the right side. ([#7533](https://github.com/tldraw/tldraw/pull/7533))
- Fix zoom menu showing debounced zoom level for canvases with many shapes. ([#7626](https://github.com/tldraw/tldraw/pull/7626))
- Fix extra line appearing after bullet and ordered lists in text shapes. ([#7643](https://github.com/tldraw/tldraw/pull/7643)) (contributed by [@sahiee-dev](https://github.com/sahiee-dev))
- Fix "Back to content" button not appearing when selected shapes are off-screen. ([#7649](https://github.com/tldraw/tldraw/pull/7649))
- Fix dotted freehand lines becoming invisible at minimum zoom on Chrome. ([#7650](https://github.com/tldraw/tldraw/pull/7650))
- Fix menu bar stretching to full width on mobile viewports instead of fitting content. ([#7568](https://github.com/tldraw/tldraw/pull/7568))

[View release on GitHub](https://github.com/tldraw/tldraw/releases/tag/v4.3.0)

---

### Patch releases

#### v4.3.1

- Fix draw shape precision issues at high canvas coordinates by switching to delta encoding with a Float32 anchor point. ([#7710](https://github.com/tldraw/tldraw/pull/7710))

[View release on GitHub](https://github.com/tldraw/tldraw/releases/tag/v4.3.1)

#### v4.3.2

- Fix crash when cropping custom shapes that don't include `isCircle` in their crop schema. ([#7931](https://github.com/tldraw/tldraw/pull/7931))

[View release on GitHub](https://github.com/tldraw/tldraw/releases/tag/v4.3.2)

## v4.2.0

This month's release includes many bug fixes and small API additions, along with a major version bump of our TipTap rich text editor.

### What's new

#### 💥 TipTap v3 ([#5717](https://github.com/tldraw/tldraw/pull/5717))

We've upgraded TipTap from v2 to v3. If you've done any customization to our standard TipTap kit, please refer to TipTap's guide [How to upgrade Tiptap v2 to v3](https://tiptap.dev/docs/guides/upgrade-tiptap-v2) for breaking changes you might experience.

<details>
<summary>Migration guide</summary>

Starting in v4.2, tldraw bundles TipTap v3 as a transitive dependency. If your project pins `@tiptap/*` v2 packages directly, upgrade them to v3 *before* trying to fix any code-level errors — leaving v2 in place produces a dual install that surfaces as confusing type errors.

**Dual-install symptom.** Deeply-nested errors like *"Property 'getCharacterCount' is missing in type 'Editor' but required in type 'Editor'"*, with the two `Editor` types resolving to different paths (one under `node_modules/@tiptap/core`, the other under `node_modules/@tldraw/editor/node_modules/@tiptap/core`), are not API changes — they are the v2/v3 dual install. The `Editor` classes are structurally similar but nominally different. Finishing the v3 upgrade collapses the duplicate and these errors disappear without code changes.

**Code-level changes that affect tldraw users:**

- **Default → named exports.** Several extensions changed from default to named exports:

  ```ts
  // Before
  import StarterKit from '@tiptap/starter-kit'

  // After
  import { StarterKit } from '@tiptap/starter-kit'
  ```

- **`TextStyle` → `TextStyleKit`, and `FontFamily` moved.** The `FontFamily` extension is no longer in `@tiptap/extension-font-family`; it's part of `TextStyleKit` in `@tiptap/extension-text-style`. The `setFontFamily` and `setFontSize` chain commands only exist on `TextStyleKit`, not on bare `TextStyle`.

- **Transaction handler types.** Inline parameter type annotations on TipTap event handlers no longer work. Import the proper event-map type:

  ```ts
  import { EditorEvents } from '@tiptap/core'
  // ...
  onTransaction: (props: EditorEvents['transaction']) => { /* ... */ }
  ```

- **Custom chained commands** still register via `declare module '@tiptap/core'` augmentation in v3 — the augmentation target is the same.

</details>

### API changes

- Add `Editor.setTool`/`Editor.removeTool` for dynamically altering the editor's tool state chart. ([#6909](https://github.com/tldraw/tldraw/pull/6909)) ([#7134](https://github.com/tldraw/tldraw/pull/7134))
- Allow using custom socket implementations with `useSync` via a new `connect` option. ([#6859](https://github.com/tldraw/tldraw/pull/6859)) (contributed by [@Digital39999](https://github.com/Digital39999))
- Add support for nested property queries in the store. ([#6981](https://github.com/tldraw/tldraw/pull/6981))
- Add `createBookmarkFromUrl` helper for easier bookmark shape creation. ([#6894](https://github.com/tldraw/tldraw/pull/6894))
- Add custom reference points for handle angle snapping. ([#6987](https://github.com/tldraw/tldraw/pull/6987)) (contributed by [@naaa760](https://github.com/naaa760))
- Add new "lined-fill" style, accessible via Option+Shift+F. ([#7034](https://github.com/tldraw/tldraw/pull/7034))
- Add `onInteractionEnd` callback option. ([#6919](https://github.com/tldraw/tldraw/pull/6919))
- Add `allowReferrer` option for window opening functions. ([#7004](https://github.com/tldraw/tldraw/pull/7004))
- Remove defunct Excalidraw embed definition. ([#6897](https://github.com/tldraw/tldraw/pull/6897))

### Improvements

- Use window.prompt for renaming frames on mobile. ([#6838](https://github.com/tldraw/tldraw/pull/6838))
- Optimize string append operations in sync protocol. ([#7007](https://github.com/tldraw/tldraw/pull/7007)) (contributed by [@quasor](https://github.com/quasor))

### Bug fixes

- Fix Replit embed URL handling. ([#6892](https://github.com/tldraw/tldraw/pull/6892))
- Fix accessibility issue that caused a misleading warning in the console. ([#6904](https://github.com/tldraw/tldraw/pull/6904))
- Fix convert-to-bookmark action. ([#6894](https://github.com/tldraw/tldraw/pull/6894))
- Fix dropdown menu pointer handling. ([#7021](https://github.com/tldraw/tldraw/pull/7021))
- Fix iOS Safari image uploads. ([#7037](https://github.com/tldraw/tldraw/pull/7037))
- Fix Alt+Tab conflict on Windows. ([#7005](https://github.com/tldraw/tldraw/pull/7005))
- Fix text focus issues. ([#7043](https://github.com/tldraw/tldraw/pull/7043))
- Fix `TldrawEditor` usability without `TldrawUiContextProvider`. ([#7053](https://github.com/tldraw/tldraw/pull/7053))
- Fix cross-realm clipboard functionality. ([#7026](https://github.com/tldraw/tldraw/pull/7026))
- Fix iOS ink rendering via coalesced events. ([#6917](https://github.com/tldraw/tldraw/pull/6917))
- Fix menu-open dragging behavior. ([#6918](https://github.com/tldraw/tldraw/pull/6918))

[View release on GitHub](https://github.com/tldraw/tldraw/releases/tag/v4.2.0)

---

### Patch releases

#### v4.2.1

- Fix CommonJS/cjs builds that were broken in v4.2.0. ([#7282](https://github.com/tldraw/tldraw/pull/7282))

[View release on GitHub](https://github.com/tldraw/tldraw/releases/tag/v4.2.1)

## v4.1.0

This month's release introduces a new shader starter kit for WebGL integration, along with API additions for embed control, minimap filtering, and localStorage-backed atoms.

### What's new

#### Shader starter kit ([#6847](https://github.com/tldraw/tldraw/pull/6847))

A new template demonstrating WebGL shader integration with tldraw for dynamic, interactive backgrounds that respond to canvas interactions. Includes four complete examples:

- **Fluid simulation** - Navier-Stokes dynamics
- **Rainbow** - Animated gradients
- **Shadows** - Raymarching and signed distance fields
- **Minimal** - Bare-bones template for custom shaders

Features configurable parameters, localStorage persistence, and full TypeScript support.

### API changes

- Add `embedOnPaste` option to `EmbedDefinition` for opting out of automatic embed-on-paste behavior. ([#6874](https://github.com/tldraw/tldraw/pull/6874))
- Add `hideInMinimap` option to `ShapeUtil` for excluding shapes from the minimap. ([#6327](https://github.com/tldraw/tldraw/pull/6327))
- Add `snapType?: 'point' | 'align'` to `TLHandle` for finer control over handle snapping. The `canSnap` property is now deprecated. ([#6883](https://github.com/tldraw/tldraw/pull/6883))
- Expose `Editor#getShapePageBounds` and `isRecordsDiffEmpty` as public methods. ([#6871](https://github.com/tldraw/tldraw/pull/6871))
- Add `localStorageAtom` to `@tldraw/state` for atoms backed by localStorage with cross-tab syncing. ([#6876](https://github.com/tldraw/tldraw/pull/6876))
- Add inline style picker component variants. ([#6920](https://github.com/tldraw/tldraw/pull/6920))

### Improvements

- Make bookmark titles clickable. ([#6326](https://github.com/tldraw/tldraw/pull/6326))
- Optimize mobile top bar rendering. ([#6895](https://github.com/tldraw/tldraw/pull/6895))
- Add numpad Enter support for entering edit mode. ([#6830](https://github.com/tldraw/tldraw/pull/6830))
- Consolidate preferences menu items. ([#6802](https://github.com/tldraw/tldraw/pull/6802))

### Bug fixes

- Fix accessibility shortcut typo. ([#6811](https://github.com/tldraw/tldraw/pull/6811))
- Fix focus mode action missing. ([#6812](https://github.com/tldraw/tldraw/pull/6812))
- Fix arrow rendering edge case. ([#6799](https://github.com/tldraw/tldraw/pull/6799))
- Fix empty window opening on anchor click. ([#6817](https://github.com/tldraw/tldraw/pull/6817))
- Fix missing translation key. ([#6826](https://github.com/tldraw/tldraw/pull/6826))
- Fix shape index race condition. ([#6884](https://github.com/tldraw/tldraw/pull/6884))
- Fix zero geometry vertices edge case. ([#6885](https://github.com/tldraw/tldraw/pull/6885))
- Fix style panel button overflow. ([#6920](https://github.com/tldraw/tldraw/pull/6920))

[View release on GitHub](https://github.com/tldraw/tldraw/releases/tag/v4.1.0)

---

### Patch releases

#### v4.1.1

- Update the formatting of the `npm create tldraw` command in the CLI output.

[View release on GitHub](https://github.com/tldraw/tldraw/releases/tag/v4.1.1)

#### v4.1.2

- Move `InFrontOfTheCanvas` component back out of the `.tl-canvas` element. ([#7021](https://github.com/tldraw/tldraw/pull/7021))

[View release on GitHub](https://github.com/tldraw/tldraw/releases/tag/v4.1.2)

## v4.0.0

This major release introduces new starter kits, a CLI tool, WCAG 2.2 Level AA accessibility compliance, and significant API enhancements for building custom canvas applications.

### What's new

#### npm create tldraw ([#6753](https://github.com/tldraw/tldraw/pull/6753))

A new CLI tool for quickly scaffolding tldraw projects. Run `npm create tldraw` to initialize projects using templates and starter kits.

#### Starter kits

Five new MIT-licensed starter kits demonstrate different applications of tldraw's canvas:

- **[agent](https://tldraw.dev/starter-kits/agent)** - Cursor-style chatbot interface where conversations occur on the canvas with AI assistance
- **[workflow](https://tldraw.dev/starter-kits/workflow)** - Node-and-wire visual programming interface for automation workflows and state machines
- **[branching-chat](https://tldraw.dev/starter-kits/branching-chat)** - Multi-path conversation explorer for managing conversation branches
- **[chat](https://tldraw.dev/starter-kits/chat)** - Canvas-based image annotation with integrated chatbot
- **[multiplayer](https://tldraw.dev/starter-kits/multiplayer)** - Collaborative whiteboard foundation built on tldraw sync

#### Licensing ([#6707](https://github.com/tldraw/tldraw/pull/6707))

Production deployments now require a license key. Development and localhost environments remain exempt. Available licenses include:

- Free 100-day trial license
- Commercial license for commercial projects
- Hobby license for non-commercial use

#### Accessibility ([#6695](https://github.com/tldraw/tldraw/pull/6695))

Achieves WCAG 2.2 Level AA compliance with an accessibility mode, improved keyboard shortcuts, comprehensive aria-labels, and scrollable menus.

#### Drag from toolbar ([#6681](https://github.com/tldraw/tldraw/pull/6681))

Create shapes by dragging directly from the toolbar, eliminating the click-then-draw workflow.

#### Custom shape clipping ([#6649](https://github.com/tldraw/tldraw/pull/6649))

Shapes can now clip their children, enabling complex visual hierarchies and container-style shapes.

#### Vertical toolbar support ([#6623](https://github.com/tldraw/tldraw/pull/6623))

The toolbar supports vertical orientation for alternative layout configurations.

#### Rich text arrows ([#6594](https://github.com/tldraw/tldraw/pull/6594))

Arrow labels support rich text formatting including bold, italic, and text styling.

### API changes

- 💥 **CSS variables** now start with `--tl-` prefix. Update custom CSS: `--color-background` → `--tl-color-background`, `--space-4` → `--tl-space-4`. ([#6712](https://github.com/tldraw/tldraw/pull/6712))
- 💥 **`Geometry2D.isLabel`** replaced with `excludeFromShapeBounds` flag. ([#6668](https://github.com/tldraw/tldraw/pull/6668))
- 💥 **`open-url` event** now uses `destinationUrl` instead of `url`. ([#6641](https://github.com/tldraw/tldraw/pull/6641))
- 💥 **Arrow shapes** now use `richText` instead of `text` property. Data migrates automatically, but direct access to `arrow.props.text` must update to `arrow.props.richText`. ([#6594](https://github.com/tldraw/tldraw/pull/6594))
- 💥 **`@tldraw/ai` module** removed. Use the [agent starter kit](https://tldraw.dev/starter-kits/agent) instead.
- 💥 **Event propagation behavior** changed. Use `editor.markEventAsHandled()` for custom event handling. ([#6592](https://github.com/tldraw/tldraw/pull/6592))
- 💥 **Style panel** redesigned with legacy picker components removed. ([#6573](https://github.com/tldraw/tldraw/pull/6573))
- 💥 **Toolbar CSS class names** updated for consistency. ([#6551](https://github.com/tldraw/tldraw/pull/6551))
- Add custom messages in `TLSocketRoom` via `sendCustomMessage()` and `onCustomMessageReceived`. ([#6614](https://github.com/tldraw/tldraw/pull/6614)) (contributed by [@mootari](https://github.com/mootari))
- Add `Editor.getShapesAtPoint()` with flexible margin options for precise hit testing. ([#6724](https://github.com/tldraw/tldraw/pull/6724))
- Add `editor.toImageDataUrl()` for generating image data URLs directly from shapes. ([#6658](https://github.com/tldraw/tldraw/pull/6658))
- Add `ShapeUtil.isExportBoundsContainer` and `canCull` methods for controlling export behavior and rendering optimization. ([#6649](https://github.com/tldraw/tldraw/pull/6649))
- Add `EditorAtom` for managing UI state outside React components. ([#6687](https://github.com/tldraw/tldraw/pull/6687))
- Add enhanced handle drag callbacks with timing events for custom drag interactions. ([#6702](https://github.com/tldraw/tldraw/pull/6702))

### Bug fixes

- Fix arrow labels being cut off in exports. ([#6783](https://github.com/tldraw/tldraw/pull/6783))
- Fix PNG physical pixel dimensions parsing. ([#6771](https://github.com/tldraw/tldraw/pull/6771))
- Fix pointer events on mobile firing inconsistently. ([#6738](https://github.com/tldraw/tldraw/pull/6738))
- Fix Chrome embed positioning at non-100% zoom levels. ([#6724](https://github.com/tldraw/tldraw/pull/6724))
- Fix hollow shapes not being properly bound-to by arrows. ([#6705](https://github.com/tldraw/tldraw/pull/6705))

[View release on GitHub](https://github.com/tldraw/tldraw/releases/tag/v4.0.0)

---

### Patch releases

#### v4.0.1

- Fix template exports. ([#6804](https://github.com/tldraw/tldraw/pull/6804))
- Fix documentation. ([#6805](https://github.com/tldraw/tldraw/pull/6805))

[View release on GitHub](https://github.com/tldraw/tldraw/releases/tag/v4.0.1)

#### v4.0.2

- Fix focus mode SDK issue that caused problems when using focus mode in the SDK. ([#6815](https://github.com/tldraw/tldraw/pull/6815))

[View release on GitHub](https://github.com/tldraw/tldraw/releases/tag/v4.0.2)

#### v4.0.3

- Add Claude 4.5 Sonnet to agent starter kit. ([#6852](https://github.com/tldraw/tldraw/pull/6852))
- Ensure templates don't use internal functions. ([#6871](https://github.com/tldraw/tldraw/pull/6871))

[View release on GitHub](https://github.com/tldraw/tldraw/releases/tag/v4.0.3)

#### v4.0.4

- Fix shape index jitter for the first shape created, preventing potential index collisions. ([#6884](https://github.com/tldraw/tldraw/pull/6884))

[View release on GitHub](https://github.com/tldraw/tldraw/releases/tag/v4.0.4)

## v3.15.0

This release introduces `npm create tldraw` for bootstrapping new projects, along with API additions, accessibility improvements, and performance optimizations.

### What's new

#### npm create tldraw

Developers can now initialize new tldraw projects by running `npm create tldraw` in their terminal, selecting from available starter templates.

### API changes

- Add handle interaction callbacks: `onHandleDragStart`, `onHandleDragEnd`, and cancellation callbacks (`onTranslateCancel`, `onResizeCancel`, `onRotateCancel`, `onHandleDragCancel`).
- Add JSX icon support for custom icons in tool overrides, action overrides, buttons, and menu items.
- Add `StateNode.addChild` method enabling dynamic tool functionality additions.
- Convert `DefaultSpinner` component to SVG format, now accepts standard SVG properties.
- Export `HeartToolbarItem` component.

### Improvements

- Enhance "Select All" functionality to intelligently select within a shape's common parent context (e.g., selecting shapes within a frame without selecting the frame itself).
- Add user preference option to disable keyboard shortcuts.
- Prevent frame drops.
- Optimize the `useValue` and `useReactor` hooks.
- Enhance frame rendering efficiency.
- Expand `aria-hidden` application.
- Improve keyboard navigation.
- Add proper `role="radiogroup"` assignment for toggle groups.
- Correct aria slider values.

### Bug fixes

- Fix HTML entity escaping in pasted content.
- Fix text measurement accuracy.
- Fix arrow rendering between circles.
- Fix SVG text outline rendering.
- Fix image toolbar camera movement interactions.

[View release on GitHub](https://github.com/tldraw/tldraw/releases/tag/v3.15.0)

---

### Patch releases

#### v3.15.1

- Fix room change event handling in the sync core library. ([#6543](https://github.com/tldraw/tldraw/pull/6543))

[View release on GitHub](https://github.com/tldraw/tldraw/releases/tag/v3.15.1)

#### v3.15.2

- Fix editor remounting in a loop when given custom assetUrls. ([#6605](https://github.com/tldraw/tldraw/pull/6605))

[View release on GitHub](https://github.com/tldraw/tldraw/releases/tag/v3.15.2)

#### v3.15.3

- Enhance callback functionality for shape utilities. ([#6634](https://github.com/tldraw/tldraw/pull/6634))

[View release on GitHub](https://github.com/tldraw/tldraw/releases/tag/v3.15.3)

#### v3.15.4

- Fix potential bug in TLSyncClient. ([#6660](https://github.com/tldraw/tldraw/pull/6660))

[View release on GitHub](https://github.com/tldraw/tldraw/releases/tag/v3.15.4)

#### v3.15.5

- Show license flags message as a warning. ([#6843](https://github.com/tldraw/tldraw/pull/6843))
- Fix expired internal license verification. ([#6843](https://github.com/tldraw/tldraw/pull/6843))

[View release on GitHub](https://github.com/tldraw/tldraw/releases/tag/v3.15.5)

## v3.14.0

This release introduces contextual toolbars for image and video shapes, enhanced cropping options, and a new PathBuilder API.

### API changes

- 💥 **`Editor#getShapePageGeometry`** removed.
- 💥 **`Editor#getShapesAtPoint`** now returns shapes in descending index order.
- 💥 **Locked shapes** cannot be duplicated; pasting locked shapes unlocks them.
- 💥 **`ShapeUtil.canDropShapes`** removed—drag-and-drop participation now requires implementing handlers.
- Add `TldrawUiContextualToolbar` component enabling developers to create floating toolbars.
- Add `PathBuilder` API for constructing tldraw-style inky and dashed SVGs.
- Add arrow binding configuration for controlling when arrows should be exact and/or bind.
- Add `measureScrollWidth` option for text measurement.
- Add `defaultAutoplay` boolean property for `VideoShapeUtil`.

### Improvements

- Add image and video contextual toolbar with floating controls for enhanced interaction.
- Add download functionality for image and video shapes to download original files.
- Add advanced image cropping options.
- Improve context menu keyboard support.
- Improve style panel keyboard navigation.
- Improve focus management.
- Optimize copy operations.
- Optimize arrow rendering with labels.
- Optimize shape resizing.
- Optimize keyboard interactions.
- Optimize arrow dragging.
- Improve text measurement caching.

### Bug fixes

- Fix page menu scrolling behavior.
- Fix undo functionality for sticky creation.
- Fix bookmark hyperlink handling.
- Fix SVG output and vector calculations.
- Fix frame resizing with multiple selections.
- Fix HTML copy-paste positioning.
- Fix keyboard shortcuts and text overflow issues.

[View release on GitHub](https://github.com/tldraw/tldraw/releases/tag/v3.14.0)

---

### Patch releases

#### v3.14.1

- Fix text measurement bug where styles could leak from one measurement to the next. ([#6388](https://github.com/tldraw/tldraw/pull/6388))

[View release on GitHub](https://github.com/tldraw/tldraw/releases/tag/v3.14.1)

#### v3.14.2

- Fix HTML entities being escaped in clipboard operations. ([#6396](https://github.com/tldraw/tldraw/pull/6396))

[View release on GitHub](https://github.com/tldraw/tldraw/releases/tag/v3.14.2)

## v3.13.0

This release introduces elbow arrows for technical diagramming and continues accessibility improvements toward WCAG compliance.

### What's new

#### Elbow arrows

The arrow shape now supports a new elbow connector style. These arrows travel between points using straight lines joined at right angles rather than curved paths, making them ideal for technical diagrams. Access this option through the arrow tool's line style settings.

### API changes

- 💥 **`ShapeUtil.canEditInReadOnly`** renamed to `ShapeUtil.canEditInReadonly` (note capitalization change).
- 💥 **Arrow target highlights** migrated from `setHintingShapes` to a custom `TldrawOverlays` component; users of `<TldrawEditor />` must pass this component to the `Overlays` slot.
- Add text measurement API that accepts arbitrary style parameters.
- Add new `Overlays` component for camera-following canvas overlays.
- Add `hasCustomTabBehavior` prop to `RichTextLabel` for custom tab logic.

### Improvements

- Auto-select link text when editing shape links.
- Snap pasted text and embeds to grid when enabled.
- Allow Figma embeds to work with selection links.
- Show visual block indicating label placement when editing arrow labels.
- Include background color in single frame exports.
- Allow frames to be double-click resized to fit contents.
- Allow style panel to focus with keyboard shortcut.
- Add proper labels to more UI icons and handles.
- Add container navigation shortcuts for moving selections in/out of frames and groups.
- Make embed shapes keyboard-tabbable.
- Add keyboard navigation to toolbars throughout the application.
- Show rich text editing shortcuts in the help menu.
- Improve performance of programmatic rotation of selected shapes.
- Improve performance of multi-shape dragging operations.
- Optimize selection evaluation to prevent unnecessary re-evaluations.

### Bug fixes

- Fix flattened shape backgrounds.
- Fix screenreader behavior.
- Fix tool consistency.
- Fix text rendering.
- Fix selection accuracy.
- Fix arrow flickering during rotation.

[View release on GitHub](https://github.com/tldraw/tldraw/releases/tag/v3.13.0)

---

### Patch releases

#### v3.13.1

- Show elbow arrow outline indicator on hover when creating elbow arrows. ([#6099](https://github.com/tldraw/tldraw/pull/6099))

[View release on GitHub](https://github.com/tldraw/tldraw/releases/tag/v3.13.1)

#### v3.13.2

- Fix LoadingScreen context bug where custom asset URL configuration was ignored. ([#6283](https://github.com/tldraw/tldraw/pull/6283))

[View release on GitHub](https://github.com/tldraw/tldraw/releases/tag/v3.13.2)

#### v3.13.3

- Fix overriding builtin tools not working. ([#6330](https://github.com/tldraw/tldraw/pull/6330))

[View release on GitHub](https://github.com/tldraw/tldraw/releases/tag/v3.13.3)

#### v3.13.4

- Fix cross-realm functionality issues. ([#6338](https://github.com/tldraw/tldraw/pull/6338))

[View release on GitHub](https://github.com/tldraw/tldraw/releases/tag/v3.13.4)

## v3.12.0

This release focuses on accessibility improvements and API enhancements, with no breaking changes.

### API changes

- Add new `getShapeVisibility` prop replacing the deprecated `isShapeHidden`, allowing hidden children to override parent visibility states.
- Allow frames to display colorful borders and labels by configuring `FrameShapeUtil` with `showColors: true`.
- Add `Geometry2d` helper methods including `intersectLineSegment`, `intersectCircle`, `transform`, and filtering options.
- Export two Tiptap extensions individually for rich text customization.

### Improvements

- Add keyboard-based selection movement between shapes using Tab and Cmd/Ctrl+Arrow keys.
- Add screen reader announcements for tool and shape selection changes.
- Add keyboard resizing capability via Cmd/Ctrl+Alt+Shift with +/- keys.
- Improve semantic HTML5 tags and proper ARIA roles throughout the UI.
- Simplify keyboard shortcut syntax (e.g., `'?!l'` becomes `'alt+shift+l'`).
- Add atomic `Store.mergeRemoteChanges` with correct scope-triggered side effects.
- Add smoother drawing on slower CPUs via `getCoalescedEvents`.
- Add YouTube embed support for time/loop parameters.
- Add optional asset URL overrides for unused Embed shape icons.

### Bug fixes

- Fix Firefox SVG pasting.
- Fix TailwindCSS text export styling.
- Fix image pasting efficiency.
- Fix Miro shape compatibility.
- Fix Safari trackpad zoom.
- Fix performance regressions in rich text and iPad drawing.

[View release on GitHub](https://github.com/tldraw/tldraw/releases/tag/v3.12.0)

---

### Patch releases

#### v3.12.1

- Fix group bounds containing text shapes. ([#5911](https://github.com/tldraw/tldraw/pull/5911))

[View release on GitHub](https://github.com/tldraw/tldraw/releases/tag/v3.12.1)

#### v3.12.2

- Restore DPR constrained shape dimensions. ([#6034](https://github.com/tldraw/tldraw/pull/6034))

[View release on GitHub](https://github.com/tldraw/tldraw/releases/tag/v3.12.2)

## v3.11.0

This release addresses rich text issues following v3.10, adds zoom improvements, and introduces accessibility enhancements.

### API changes

- 💥 **`editor.user.getName()`** no longer returns "New user" when unnamed; returns empty string instead.
- 💥 **`defaultUserPreferences.name`** changed from "New user" to empty string.
- Export `RichTextSVG` from the package.

### Improvements

- Add a new minimum zoom step at 5%.
- Add keyboard shortcuts (Shift +/Shift -) for zooming toward your cursor.
- Add option to hit Enter to continue editing after selection in the style panel.
- Improve rich text rendering performance with many shapes.
- Improve hiding/showing shape indicators on large projects.
- Add page navigation shortcuts (option + arrows).
- Add support for satellite mode in Google Map embeds.
- Add focus rings for accessibility.
- Add nonce support for the editor security.

### Bug fixes

- Fix rich text numbered list geometry bounds.
- Fix React 19 StrictMode compatibility.
- Fix export styling issues.
- Fix missing internationalization strings for rich text elements.

[View release on GitHub](https://github.com/tldraw/tldraw/releases/tag/v3.11.0)

---

### Patch releases

#### v3.11.1

- Fix application of zoomSpeed setting to pinch gestures. ([#5696](https://github.com/tldraw/tldraw/pull/5696))
- Fix unexpected borders in exports from Tailwind CSS pages. ([#5696](https://github.com/tldraw/tldraw/pull/5696))

[View release on GitHub](https://github.com/tldraw/tldraw/releases/tag/v3.11.1)

#### v3.11.2

- Fix sync core bug. ([#6667](https://github.com/tldraw/tldraw/pull/6667))

[View release on GitHub](https://github.com/tldraw/tldraw/releases/tag/v3.11.2)

## v3.10.0

This release introduces rich text as a first-class primitive within the Editor, alongside various performance improvements and bug fixes.

### What's new

#### Rich text support

The SDK now offers rich text as a first-class primitive. The implementation utilizes TipTap with its StarterKit extension plus additional customizations. Developers can configure this functionality through a `textOptions` property to customize TipTap and modify extensions.

### API changes

- 💥 **`text` property** on most shapes replaced with `richText`. When setting values, use `richText: toRichText('some string')` instead of `text: 'some string'`. When retrieving values, use `renderPlaintextFromRichText(props.text)` rather than `props.text`.
- Add asset rehydration for .tldr files containing embedded base64 data.
- Add `BrokenAssetIcon` display on file upload failures.
- Add public collaboration hooks (`usePeerIds`, `usePresence`).
- Add `userId` parameter to collaboration components.

### Bug fixes

- Fix performance regression affecting document loading.
- Fix Firefox export/style embedding for foreignObjects.
- Fix shape reparenting order preservation.
- Fix resize functionality.
- Fix Safari file pasting issues.
- Fix SSR environment compatibility (core-js import removal).
- Fix IME text duplication in Chrome.

[View release on GitHub](https://github.com/tldraw/tldraw/releases/tag/v3.10.0)

---

### Patch releases

#### v3.10.1

- Fix API reference links in state packages. ([#5610](https://github.com/tldraw/tldraw/pull/5610))

[View release on GitHub](https://github.com/tldraw/tldraw/releases/tag/v3.10.1)

#### v3.10.2

- Add `textOptions` to `<TldrawImage />` component. ([#5650](https://github.com/tldraw/tldraw/pull/5650))

[View release on GitHub](https://github.com/tldraw/tldraw/releases/tag/v3.10.2)

#### v3.10.3

- Fix Inter font being embedded in exports. ([#5676](https://github.com/tldraw/tldraw/pull/5676))
- Reduce excessive styling in exported content. ([#5676](https://github.com/tldraw/tldraw/pull/5676))

[View release on GitHub](https://github.com/tldraw/tldraw/releases/tag/v3.10.3)

## v3.9.0

This release focuses on bug fixes and performance enhancements, laying groundwork for v3.10 features.

### API changes

- 💥 **`store.createSelectedComputedCache`** removed; use `store.createCache` with custom selector `computed` instead.
- 💥 **`createComputerCache` API** modified: third argument now accepts options object with `areRecordsEqual` and `areResultsEqual` properties instead of single equality function.

### Improvements

- Improve layout operations including align, distribute, flip, and stack with better compatibility for arrows and overlapping shapes.
- Add new `AtomMap` class to accelerate shape, binding, and record creation/deletion, available as a utility for building custom reactive maps.
- Add horizontal padding to text-bound arrows for improved visual appearance.

### Bug fixes

- Fix pasted image size calculations.
- Remove `canvas-size` dependency resolving bundler compatibility issues.
- Fix SVG copy mimetype.
- Fix text shape cloning crash.
- Fix temporary image preview display during uploads.
- Recognize `https://localhost` as development environment for licensing.

[View release on GitHub](https://github.com/tldraw/tldraw/releases/tag/v3.9.0)

## v3.8.0

This release delivers performance enhancements, UI improvements, and developer experience upgrades alongside bug fixes.

### API changes

- 💥 **`maxDrawShapePoints`** moved to `DrawShapeUtil.configure({ maxPoints })`.
- 💥 **Asset upload** now returns `{ src, meta? }` object instead of string.
- 💥 **JSON export option** removed; `useImageOrVideoAssetUrl` requires width parameter.
- 💥 **`TldrawUiToasts` and `TldrawUiDialogs`** renamed to `DefaultToasts` and `DefaultDialogs`.
- Add new `Editor.toImage` method for refined export functionality.
- Add custom content handlers for pasted tldraw and excalidraw content.
- Add "before-event" listener for custom input event handling.
- Add `onCrop` method for shape-specific cropping customization.

### Improvements

- Add `ShapeUtil.configure` utility for passing options to shape utilities.
- Allow note shapes to resize by scale.
- Add geometric tool shortcut (`g` key) for quick selection.
- Add URL drag-and-drop to create bookmark shapes on canvas.
- Expand support to 40+ languages via i18n enhancements.
- Add React 19 compatibility.
- Improve frame performance through computation relocation.

### Bug fixes

- Fix dialog and edit menu glitches.
- Fix mousewheel scrolling on elements.
- Fix image export width.
- Fix text paste behavior via keyboard shortcuts.

[View release on GitHub](https://github.com/tldraw/tldraw/releases/tag/v3.8.0)

---

### Patch releases

#### v3.8.1

- Fix `TldrawImage` not functioning properly with `format=png`. ([#5429](https://github.com/tldraw/tldraw/pull/5429))

[View release on GitHub](https://github.com/tldraw/tldraw/releases/tag/v3.8.1)

## v3.7.0

This release delivers bug fixes and performance enhancements, including customization options for presence data synchronization.

### What's new

#### Presence sync customization ([#5149](https://github.com/tldraw/tldraw/pull/5149))

Developers can now customize what presence data is synced between clients, or disable presence syncing entirely. This enables optimization for high-latency connections and privacy-focused applications.

### API changes

- Allow `expandSelectionOutlinePx` to return a `Box` object to enable asymmetric expansion across different sides.
- Modify behavior when using `editor.updateShape` to properly handle undefined values.

### Bug fixes

- Fix React Strict Mode listener bug affecting `store.listen`.
- Fix image rotation and cropping functionality issues.
- Fix CSS import failures during export/print operations.
- Fix text measurement div leakage during development hot reloads.
- Fix popover menu viewport overflow problems.
- Fix GitHub Gist URL validation.
- Fix image dimension limits for pasted content.
- Fix pages menu and quick actions rendering.

[View release on GitHub](https://github.com/tldraw/tldraw/releases/tag/v3.7.0)

---

### Patch releases

#### v3.7.1

- Pass custom migrations to `useLocalStore`. ([#5204](https://github.com/tldraw/tldraw/pull/5204))

[View release on GitHub](https://github.com/tldraw/tldraw/releases/tag/v3.7.1)

#### v3.7.2

- Fix pasting files copied from the local filesystem with cmd-v. ([#5225](https://github.com/tldraw/tldraw/pull/5225))

[View release on GitHub](https://github.com/tldraw/tldraw/releases/tag/v3.7.2)

## v3.6.0

This release focuses on developer experience improvements with expanded helper functions and various bug fixes across the editor, embed system, and export functionality.

### What's new

#### Expanded helper functions ([#5041](https://github.com/tldraw/tldraw/pull/5041))

Actions and toasts overrides now have access to a wider range of helper functions, including clipboard interactions, toast notifications, export utilities, printing, and more. This makes it easier to create custom actions and tools that integrate deeply with tldraw's functionality.

### API changes

- 💥 **`setEmbedDefinitions`** changed from an instance method to a static method on the `EmbedShapeUtil` class. Update your embed definitions to use `EmbedShapeUtil.setEmbedDefinitions()` instead of calling it on an instance. ([#5027](https://github.com/tldraw/tldraw/pull/5027))
- 💥 **`ExtractOptionalKeys` and `ExtractRequiredKeys`** utility types removed. Use the new utility type for creating optional properties instead. ([#5055](https://github.com/tldraw/tldraw/pull/5055))

### Improvements

- Add utility type for making undefined properties optional, simplifying type definitions for shape props and other optional configurations. ([#5055](https://github.com/tldraw/tldraw/pull/5055))
- Make notes properly snap to the grid after their position is updated, improving alignment consistency. ([#5010](https://github.com/tldraw/tldraw/pull/5010))
- Add incremental derivation example demonstrating advanced computed value patterns. ([#5038](https://github.com/tldraw/tldraw/pull/5038))

### Bug fixes

- Fix asset resolution when copying and pasting multiple items, including proper handling of video assets. ([#5061](https://github.com/tldraw/tldraw/pull/5061))
- Fix text clipping in scaled frames during export, ensuring all text content is visible in exported images. ([#5022](https://github.com/tldraw/tldraw/pull/5022))
- Fix concurrent export operations interfering with each other's font loading. ([#5022](https://github.com/tldraw/tldraw/pull/5022))
- Fix long-press point accuracy to ensure touch interactions register at the correct coordinates. ([#5032](https://github.com/tldraw/tldraw/pull/5032))
- Fix export naming for single unnamed frames, which now receive appropriate default names. ([#4918](https://github.com/tldraw/tldraw/pull/4918))
- Fix custom embeds not rendering correctly on initial mount. ([#5027](https://github.com/tldraw/tldraw/pull/5027))

[View release on GitHub](https://github.com/tldraw/tldraw/releases/tag/v3.6.0)

---

### Patch releases

#### v3.6.1

- Fix bugs in the editor, state-react, and tldraw packages. ([#5132](https://github.com/tldraw/tldraw/pull/5132))

[View release on GitHub](https://github.com/tldraw/tldraw/releases/tag/v3.6.1)

## v3.5.0

This release introduces enhancements to grid snapping during shape creation, improves layering mechanics, and provides better control over export functionality.

### What's new

#### Grid snapping for shape creation ([#4894](https://github.com/tldraw/tldraw/pull/4894))

Shapes now automatically align to the grid when drawn in grid mode. The new `maybeSnapToGrid` function enables snapping for both click-created and drag-created shapes.

#### Improved layering actions ([#4898](https://github.com/tldraw/tldraw/pull/4898))

The "bring forward" and "send backward" actions now only consider nearby overlapping shapes, making layer management more intuitive when working with complex canvases.

### API changes

- Add `scale` and `pixelRatio` parameters to export utilities for more precise control over export dimensions and quality.
- Expose font size and stroke size APIs.
- Add socket close reason for rate limiting.
- Add option to toggle double-click text creation.

### Improvements

- Improve panning performance for note-heavy canvases and zoomed-out views.
- Improve page menu hover interaction.
- Add custom React provider support in SVG exports.
- Switch default theme to light mode.
- Improve frame heading right-click behavior.

### Bug fixes

- Fix off-screen shape zoom-to-edit.
- Fix erasing state stickiness.
- Fix frame heading drag interactions.
- Fix Safari sleep-related crashes.

[View release on GitHub](https://github.com/tldraw/tldraw/releases/tag/v3.5.0)

---

### Patch releases

#### v3.5.1

- Fix long press on inset canvases. ([#5037](https://github.com/tldraw/tldraw/pull/5037))

[View release on GitHub](https://github.com/tldraw/tldraw/releases/tag/v3.5.1)

## v3.4.0

This release addresses UI bugs, enhances image/video performance, and improves Excalidraw content compatibility.

### API changes

- 💥 **`id` DOM attribute** on shapes removed in favor of a `data-shape-id` attribute on shape wrappers.
- Add `labelColor` property to note shapes.
- Add `useUniqueSafeId` and `useSharedSafeId` utilities for managing DOM IDs in tldraw components.

### Improvements

- Improve tooltip wording in the style panel.
- Prevent arrow label indicators from displaying behind labels.
- Limit page name length in the move-to-page menu.
- Improve image and video rendering performance.

### Bug fixes

- Fix re-render loops caused by inline `options` in the Tldraw component.
- Fix toolbar button outline border-radius alignment.
- Fix color preservation when pasting Excalidraw content.
- Fix unhighlighted menu triggers.
- Fix drag event type handling.
- Fix duplicate image creation on canvas drops.
- Fix link indicator visibility on sticky notes.
- Fix URL parsing in embed dialogs.
- Fix cross-instance rendering conflicts.
- Fix menu options in read-only mode.

[View release on GitHub](https://github.com/tldraw/tldraw/releases/tag/v3.4.0)

---

### Patch releases

#### v3.4.1

- Fix arrows being clipped incorrectly when multiple tldraw instances or exports are present in the DOM. ([#4819](https://github.com/tldraw/tldraw/pull/4819))

[View release on GitHub](https://github.com/tldraw/tldraw/releases/tag/v3.4.1)

## v3.3.0

This release introduces sync improvements, readonly mode support, and various performance enhancements.

### What's new

#### Readonly mode

Servers can now restrict client editing while preserving presence updates using the new "readonly" mode. This enables use cases like view-only sharing and presentation modes where users can see each other's cursors but cannot modify the document.

#### Sync server enhancements

New `TLSocketRoom` methods (`closeSession`, `getRecord`, and `getSessions`) provide finer control over sync connections and room state management.

### API changes

- Add editor option for quick action shortcut placement.
- Add `editor.getIsReadonly()` helper method.

### Improvements

- Pass wheel events through toolbar elements for improved scrolling/zooming.
- Allow Command/Control modifier keys to enable multi-select (previously shift-only).
- Allow text tool to be locked.
- Improve draw shape rendering performance during zoom interactions.

### Bug fixes

- Fix Safari arrow rendering.
- Fix image drop navigation errors.
- Fix watermark layering.
- Fix geometry calculations.
- Fix readonly mode label editing prevention.

[View release on GitHub](https://github.com/tldraw/tldraw/releases/tag/v3.3.0)

---

### Patch releases

#### v3.3.1

- Fix meta key incorrectly triggering the shift key. ([#4703](https://github.com/tldraw/tldraw/pull/4703))

[View release on GitHub](https://github.com/tldraw/tldraw/releases/tag/v3.3.1)

#### v3.3.2

- Fix dropped images being created twice. ([#4727](https://github.com/tldraw/tldraw/pull/4727))

[View release on GitHub](https://github.com/tldraw/tldraw/releases/tag/v3.3.2)

#### v3.3.3

- Fix image duplication when dropping onto the canvas. ([#4789](https://github.com/tldraw/tldraw/pull/4789))
- Fix additional keyboard modifier issues. ([#4789](https://github.com/tldraw/tldraw/pull/4789))

[View release on GitHub](https://github.com/tldraw/tldraw/releases/tag/v3.3.3)

## v3.2.0

This release contains no code changes. It was published to align version numbers across packages. See the [v3.1.0 release notes](https://tldraw.dev/releases/v3.1.0) for information about changes since v3.0.x.

[View release on GitHub](https://github.com/tldraw/tldraw/releases/tag/v3.2.0)

## v3.1.0

This release introduces shape visibility control and server-side data manipulation capabilities, alongside API enhancements and bug fixes.

### What's new

#### Shape visibility control ([#4570](https://github.com/tldraw/tldraw/pull/4570))

The new `isShapeHidden` predicate option prevents shapes from being rendered or reacting to pointer events. This is useful for implementing permission systems in collaborative applications where certain shapes should be invisible to specific users.

#### Server-side document manipulation ([#4581](https://github.com/tldraw/tldraw/pull/4581))

The tldraw sync server now supports server-side document manipulation through the `updateShapes` method, enabling integration with third-party services and automated document updates.

### API changes

- Add option to `Editor.rotateShapesBy` specifying rotation center point.
- Add `useAsset` hook for resolving responsive asset versions.

### Bug fixes

- Fix arrow label text measurement calculations.
- Fix arrow collaborator indicators overlapping labels.
- Fix binding processing error handling.
- Fix watermark opacity and animation behavior.

[View release on GitHub](https://github.com/tldraw/tldraw/releases/tag/v3.1.0)

## v3.0.0

This release introduces a revised licensing model alongside deep links, enhanced image export, custom embeds, and a new text search API.

### What's new

#### Licensing

The most significant change involves updated licenses permitting use in both commercial and non-commercial projects when displaying a "Made with tldraw" watermark. Users can now purchase Business Licenses to remove the watermark, with details available at [tldraw.dev](https://tldraw.dev). ([#4449](https://github.com/tldraw/tldraw/pull/4449), [#4517](https://github.com/tldraw/tldraw/pull/4517), [#4561](https://github.com/tldraw/tldraw/pull/4561))

#### Deep links

New APIs enable creation of shareable URLs pointing to specific canvas locations, such as particular shapes or pages, with automatic URL updates as users navigate.

```typescript
// Create a deep link to a specific shape
const url = editor.createDeepLink({
	shapeId: 'shape-id',
	viewport: 'fit',
})

// The URL updates automatically as you navigate
editor.on('change', () => {
	const currentUrl = editor.getCurrentDeepLink()
})
```

See the [deep links guide](https://tldraw.dev/docs/editor#Deep-links) for more information. ([#4498](https://github.com/tldraw/tldraw/pull/4498))

#### Enhanced image export

Custom shapes now export as images by default through HTML/CSS embedding within SVG files, eliminating the need for developers to implement custom SVG rendering methods.

```typescript
// Custom shapes automatically export as images
class MyShapeUtil extends ShapeUtil<MyShape> {
  // No need to implement toSvg() anymore
  component(shape: MyShape) {
    return <div className="my-shape">{/* ... */}</div>
  }
}
```

#### Custom embeds API

Developers can now customize which external websites (YouTube, Google Maps, CodeSandbox, etc.) appear as embeddable content, or add entirely new embed types.

```typescript
// Add a custom embed type
const customEmbedDef = {
  type: 'custom',
  title: 'My Custom Embed',
  hostnames: ['mysite.com'],
  toEmbedUrl: (url: string) => `https://mysite.com/embed/${url}`,
  fromEmbedUrl: (url: string) => url.replace('/embed/', '/')
}

<Tldraw embedDefs={[customEmbedDef, ...defaultEmbedDefs]} />
```

#### Text search via getText API

A new `getText` API provides reliable access to human-readable shape content, facilitating text search implementation within applications.

```typescript
// Search through all shapes for text content
const searchResults = editor.getCurrentPageShapes().filter((shape) => {
	const text = editor.getText(shape.id)
	return text?.toLowerCase().includes(searchTerm.toLowerCase())
})
```

### API changes

- 💥 **`TLRotationSnapshot`** type removed from the public API. Use `editor.getShapePageTransform(shape).rotation()` instead.
- 💥 **`onEditorMount`** callback in `createTLStore` renamed to `onMount` for consistency.
- 💥 **`Editor.mark()`** deprecated in favor of `Editor.markHistoryStoppingPoint()` for clearer intent.
- 💥 **`TLSvgOptions`** type renamed to `TLImageExportOptions` to better reflect its broader use in image export.
- 💥 **Focus event behavior**: Calling `updateInstanceState({isFocused: true})` now triggers standard focus events consistently. Use `editor.focus()` for reliable focus behavior.
- Add `editor.getText(shapeId)` method for accessing shape text content.
- Add `editor.createDeepLink(options)` for generating shareable URLs.
- Add `embedDefs` prop to Tldraw component for customizing embed types.
- Expose additional shape export utilities for custom image generation.

### Improvements

- Improve bookmark rendering for URLs without preview images. ([#4460](https://github.com/tldraw/tldraw/pull/4460))
- Improve handling of asset storage and persistence. ([#4542](https://github.com/tldraw/tldraw/pull/4542))
- Improve documentation search functionality. ([#4485](https://github.com/tldraw/tldraw/pull/4485))
- Refine watermark appearance and behavior. ([#4589](https://github.com/tldraw/tldraw/pull/4589), [#4622](https://github.com/tldraw/tldraw/pull/4622))

[View release on GitHub](https://github.com/tldraw/tldraw/releases/tag/v3.0.0)

---

### Patch releases

#### v3.0.1

- Fix documentation search functionality and article issues. ([#4515](https://github.com/tldraw/tldraw/pull/4515))

[View release on GitHub](https://github.com/tldraw/tldraw/releases/tag/v3.0.1)

#### v3.0.2

- Fix build caching issue causing "You have multiple versions of tldraw libraries installed" errors. ([#4525](https://github.com/tldraw/tldraw/pull/4525))
- Fix container null error. ([#4524](https://github.com/tldraw/tldraw/pull/4524))

[View release on GitHub](https://github.com/tldraw/tldraw/releases/tag/v3.0.2)

#### v3.0.3

- Fix bugs in the editor and tldraw packages. ([#4541](https://github.com/tldraw/tldraw/pull/4541))

[View release on GitHub](https://github.com/tldraw/tldraw/releases/tag/v3.0.3)

## v2.4.0

This release introduces our sync engine as a general-use library, new animation options for shapes, support for image flipping, and many bug fixes and developer experience enhancements.

### What's new

#### Sync ([#4031](https://github.com/tldraw/tldraw/pull/4031))

For the first time, we're releasing our real-time collaboration engine—the one we developed for tldraw.com—as a general library. The SDK still supports any backend for collaboration, but we hope this will be the easiest option for developers. See the new `sync` package in the repository and the multiplayer-demo example.

#### Interpolation ([#4162](https://github.com/tldraw/tldraw/pull/4162))

You can use `Editor.animateShapes` to animate shapes on the canvas. Previously only position and rotation would animate. Now you can implement `ShapeUtil.getInterpolatedProps` to describe how your custom shape's other properties should animate.

```ts
override getInterpolatedProps(
  startShape: Shape,
  endShape: Shape,
  t: number
): Shape['props'] {
  return {
    ...endShape.props,
    w: lerp(startShape.props.w, endShape.props.w, t),
    h: lerp(startShape.props.h, endShape.props.h, t),
  }
}
```

#### Editor.run ([#4042](https://github.com/tldraw/tldraw/pull/4042))

The new `Editor.run` method runs a function within additional context. By default, the function runs inside a transaction, meaning all changes settle at once—improving performance and avoiding unnecessary renders. You can also use options to ignore history or ignore locked shapes.

```ts
editor.run(
	() => {
		editor.createShapes(myShapes)
		editor.sendToBack(myShapes)
		editor.selectNone()
	},
	{ history: 'ignore' }
)
```

#### Assets

As part of our work on sync, we have a new system for handling large assets like images and videos. Provide a [`TLAssetStore`](https://tldraw.dev/reference/tlschema/TLAssetStore) to control how assets are uploaded and retrieved. Use [`Editor.uploadAsset`](https://tldraw.dev/reference/editor/Editor#uploadAsset) and [`Editor.resolveAssetURL`](https://tldraw.dev/reference/editor/Editor#resolveAssetUrl) in your shapes and tools.

### API changes

- 💥 **`editor.history.ignore(cb)`** replaced with `editor.run(cb, {history: 'ignore'})`.
- 💥 **`editor.batch`** deprecated, replaced with `editor.run`.
- 💥 **`@tldraw/state` imports**: If you're importing from `@tldraw/state` directly, the `track` function and all hooks (e.g. `useValue`) have moved to `@tldraw/state-react`. ([#4170](https://github.com/tldraw/tldraw/pull/4170))
- Add `DefaultMenuPanel` export. ([#4193](https://github.com/tldraw/tldraw/pull/4193))
- Make `fileSize` property of `TLImageAsset` and `TLVideoAsset` optional. ([#4206](https://github.com/tldraw/tldraw/pull/4206))
- Allow passing partial `TLEditorSnapshot` to `TldrawImage` and `useTLStore`. ([#4190](https://github.com/tldraw/tldraw/pull/4190))
- Make `EffectScheduler` and `useStateTracking` public. ([#4155](https://github.com/tldraw/tldraw/pull/4155))
- Add `setDefaultValue` to `StyleProp`. ([#4044](https://github.com/tldraw/tldraw/pull/4044))
- Add `ShapeUtil.getInterpolatedProps`. ([#4162](https://github.com/tldraw/tldraw/pull/4162))
- Add `Editor.run`, replacing `Editor.batch`. ([#4042](https://github.com/tldraw/tldraw/pull/4042))

### Improvements

- Add horizontal and vertical flipping for images. ([#4113](https://github.com/tldraw/tldraw/pull/4113))
- Allow custom tools to decide whether they are affected by shape lock. ([#4208](https://github.com/tldraw/tldraw/pull/4208))
- Serve icons as a single SVG instead of many individual requests. ([#4150](https://github.com/tldraw/tldraw/pull/4150))
- Make paste-at-point behavior based on a user preference. ([#4104](https://github.com/tldraw/tldraw/pull/4104))
- Show toast when uploading an unsupported file type or file that's too large. ([#4114](https://github.com/tldraw/tldraw/pull/4114))
- Show toast when pasting fails due to missing clipboard permissions. ([#4117](https://github.com/tldraw/tldraw/pull/4117))
- Add `ShapeIndicators` component for custom logic about when to display indicators. ([#4083](https://github.com/tldraw/tldraw/pull/4083))
- Animate shape opacity changes. ([#4242](https://github.com/tldraw/tldraw/pull/4242))

### Bug fixes

- Fix font style export to SVG. ([#4195](https://github.com/tldraw/tldraw/pull/4195))
- Fix `force` flag for camera methods. ([#4214](https://github.com/tldraw/tldraw/pull/4214))
- Show user's color scheme in menu by default. ([#4184](https://github.com/tldraw/tldraw/pull/4184))
- Fix padding for dynamically scaled text shapes. ([#4140](https://github.com/tldraw/tldraw/pull/4140))
- Fix `cameraOptions` via React causing editor to re-mount. ([#4089](https://github.com/tldraw/tldraw/pull/4089))
- Allow high-res images to be uploaded. ([#4198](https://github.com/tldraw/tldraw/pull/4198))
- Prevent locked shapes from being updated, grouped, or ungrouped programmatically. ([#4042](https://github.com/tldraw/tldraw/pull/4042))
- Fix `snapshots` prop in `createTLStore`. ([#4233](https://github.com/tldraw/tldraw/pull/4233))
- Fix grid backgrounds with multiple tldraw instances. ([#4132](https://github.com/tldraw/tldraw/pull/4132))
- Fix inputs staying in place while viewport-following. ([#4108](https://github.com/tldraw/tldraw/pull/4108))
- Fix bookmarks rendering across devices. ([#4118](https://github.com/tldraw/tldraw/pull/4118))
- Fix `InFrontOfTheCanvas` component stack order. ([#4024](https://github.com/tldraw/tldraw/pull/4024))
- Fix frame headers not stopping edit on focus loss. ([#4092](https://github.com/tldraw/tldraw/pull/4092))

[View release on GitHub](https://github.com/tldraw/tldraw/releases/tag/v2.4.0)

---

### Patch releases

#### v2.4.1

- Fix `assets` prop on `<Tldraw />` and `<TldrawEditor />` components not being respected. ([#4285](https://github.com/tldraw/tldraw/pull/4285))

[View release on GitHub](https://github.com/tldraw/tldraw/releases/tag/v2.4.1)

#### v2.4.2

- Fix bugs and improve the sync engine and related packages. ([#4309](https://github.com/tldraw/tldraw/pull/4309))

[View release on GitHub](https://github.com/tldraw/tldraw/releases/tag/v2.4.2)

#### v2.4.3

- Fix bugs in the tldraw package. ([#4328](https://github.com/tldraw/tldraw/pull/4328))

[View release on GitHub](https://github.com/tldraw/tldraw/releases/tag/v2.4.3)

#### v2.4.4

- Fix bugs in the editor, sync, tldraw, and tlschema packages. ([#4352](https://github.com/tldraw/tldraw/pull/4352))

[View release on GitHub](https://github.com/tldraw/tldraw/releases/tag/v2.4.4)

#### v2.4.5

- Fix unhandled promise rejection during strict mode in the editor. ([#4407](https://github.com/tldraw/tldraw/pull/4407))

[View release on GitHub](https://github.com/tldraw/tldraw/releases/tag/v2.4.5)

#### v2.4.6

- Improve rendering performance in the store and tldraw packages. ([#4434](https://github.com/tldraw/tldraw/pull/4434))

[View release on GitHub](https://github.com/tldraw/tldraw/releases/tag/v2.4.6)

## v2.3.0

This release focuses on image and video storage improvements, a new fonts/icons CDN, image performance optimizations, and a new flatten feature.

### What's new

#### Image and video storage improvements ([#3836](https://github.com/tldraw/tldraw/pull/3836))

When using local-only storage, images and videos are now stored in a separate IndexedDB table instead of as base64-encoded blobs in the room's JSON. This significantly improves performance and reduces the size of room data.

#### Fonts and icons CDN ([#3923](https://github.com/tldraw/tldraw/pull/3923))

Improved our CDN for assets (fonts and icons). We were using unpkg before, but now we're using Cloudflare directly for more fine-grained control and better performance. ([#3927](https://github.com/tldraw/tldraw/pull/3927))

#### Image level-of-detail ([#3827](https://github.com/tldraw/tldraw/pull/3827))

For higher-resolution images (over a couple megabytes), the image gets transformed to an appropriate size depending on your viewport, zoom level, and network speed. This helps with bandwidth and browser memory usage when you have multiple high-res images on the board.

#### Flatten ([#3933](https://github.com/tldraw/tldraw/pull/3933))

You can now select multiple shapes and flatten them into a single image. For those moments when you need to press things together.

#### Security improvements ([#3884](https://github.com/tldraw/tldraw/pull/3884))

Locked down referrer network requests to make sure we weren't leaking tldraw rooms to external media/iframe requests. ([#3881](https://github.com/tldraw/tldraw/pull/3881))

### API changes

- Add fill-fill style, accessible via `Alt`+`F`. ([#3966](https://github.com/tldraw/tldraw/pull/3966))
- Make `ArrowBindingUtil` public. ([#3913](https://github.com/tldraw/tldraw/pull/3913))
- Add `getSnapshot` and `loadSnapshot` methods to `Editor` class. ([#3912](https://github.com/tldraw/tldraw/pull/3912))

### Improvements

- Allow users to use system appearance (dark/light) mode. ([#3703](https://github.com/tldraw/tldraw/pull/3703))
- Remove ability to rename document while in readonly mode. ([#3911](https://github.com/tldraw/tldraw/pull/3911))
- Add delay and easing to edge scrolling. ([#3950](https://github.com/tldraw/tldraw/pull/3950))

### Bug fixes

- Fix copy/paste for older versions of Firefox. ([#4008](https://github.com/tldraw/tldraw/pull/4008))
- Fix copy/paste in Firefox 127+. ([#4003](https://github.com/tldraw/tldraw/pull/4003))
- Fix not allowing editing locked shapes when switching edit modes. ([#4007](https://github.com/tldraw/tldraw/pull/4007))
- Fix text shape measurement in React strict mode. ([#4001](https://github.com/tldraw/tldraw/pull/4001))
- Fix border color for following user. ([#3975](https://github.com/tldraw/tldraw/pull/3975))
- Fix scale issue with new draw lines. ([#3971](https://github.com/tldraw/tldraw/pull/3971))
- Fix edge scrolling at odd browser zoom levels. ([#3973](https://github.com/tldraw/tldraw/pull/3973))
- Fix appearance of solid-style heart shapes. ([#3963](https://github.com/tldraw/tldraw/pull/3963))
- Fix position of multiple assets when pasted/dropped onto canvas. ([#3965](https://github.com/tldraw/tldraw/pull/3965))
- Fix indicator for stylus-drawn draw shapes. ([#3962](https://github.com/tldraw/tldraw/pull/3962))

[View release on GitHub](https://github.com/tldraw/tldraw/releases/tag/v2.3.0)

## v2.2.0

This release introduces the bindings system, camera constraints, and configurable options—major features that unlock new types of applications you can build with tldraw.

### What's new

#### Bindings ([#3326](https://github.com/tldraw/tldraw/pull/3326))

Bindings allow you to create relationships between shapes. Our default arrow shapes are a great example: each end of the arrow can bind to another shape. When that shape moves, so does the arrow.

Before this change, arrows were hard-coded into the library. Now, with the bindings system, you can create arrows, constraint systems, visual programming environments, and much more.

Check out the [bindings guide](https://tldraw.dev/docs/editor#Bindings) for more information. ([#3780](https://github.com/tldraw/tldraw/pull/3780)) ([#3797](https://github.com/tldraw/tldraw/pull/3797)) ([#3800](https://github.com/tldraw/tldraw/pull/3800)) ([#3871](https://github.com/tldraw/tldraw/pull/3871))

#### Camera constraints ([#3282](https://github.com/tldraw/tldraw/pull/3282))

You can now limit the camera to a fixed area of the canvas. This is useful for creating experiences that don't quite fit the "infinite canvas" paradigm: document annotators, image editors, slideshow creators, and more.

See the [camera constraints guide](https://tldraw.dev/docs/editor#Camera-options) for more information. ([#3747](https://github.com/tldraw/tldraw/pull/3747)) ([#3814](https://github.com/tldraw/tldraw/pull/3814))

#### Configurable options ([#3799](https://github.com/tldraw/tldraw/pull/3799))

You can now override many options which were previously hard-coded constants. Pass an `options` prop into the tldraw component to change the maximum number of pages, grid steps, or other previously hard-coded values.

See [`TldrawOptions`](https://tldraw.dev/reference/editor/TldrawOptions) for details. ([#3900](https://github.com/tldraw/tldraw/pull/3900))

#### 💥 Undo/redo changes

The history system has been significantly reworked:

1. **History options simplified**: `squashing`, `ephemeral`, and `preserveRedoStack` flags have been consolidated. Use `editor.history.ignore(fn)` or `editor.history.batch(fn, {history: 'record-preserveRedoStack'})` instead.

2. **Automatic recording**: Everything that touches the store is now recorded in undo/redo (unless part of `mergeRemoteChanges`). Use `editor.history.ignore(fn)` for changes you don't want recorded.

3. **Side effects captured**: Changes in side-effects are now captured by undo/redo. Wrap in `editor.history.ignore` for the old behavior.

### API changes

- 💥 **`canBind`** now accepts an options object instead of just the shape. See [`TLShapeUtilCanBindOpts`](https://tldraw.dev/reference/editor/TLShapeUtilCanBindOpts).
- 💥 **`editor.sideEffects.registerBatchCompleteHandler`** replaced with `editor.sideEffects.registerOperationCompleteHandler`. ([#3748](https://github.com/tldraw/tldraw/pull/3748))
- 💥 **`editor.getArrowInfo(shape)`** replaced with `getArrowInfo(editor, shape)`.
- 💥 **`editor.getArrowsBoundTo(shape)`** removed—use `editor.getBindingsToShape(shape, 'arrow')` instead.
- 💥 **Arrow shape `start` and `end` properties** are now always points. Use `getArrowBindings(editor, shape)` to check for bindings.
- 💥 **Types renamed**: `ShapeProps` → `RecordProps`, `ShapePropsType` → `RecordPropsType`, `TLShapePropsMigrations` → `TLPropsMigrations`, `SchemaShapeInfo` → `SchemaPropsInfo`.
- Add `editor.blur` method. ([#3875](https://github.com/tldraw/tldraw/pull/3875))
- Add better defaults for `createTLStore`. ([#3886](https://github.com/tldraw/tldraw/pull/3886))
- Add `getSnapshot` and `loadSnapshot` for easier document persistence. See [State Snapshots](https://tldraw.dev/docs/persistence#State-Snapshots). ([#3811](https://github.com/tldraw/tldraw/pull/3811))
- Add `select` option to `Editor.groupShapes` and `Editor.ungroupShapes`. ([#3690](https://github.com/tldraw/tldraw/pull/3690))
- `InFrontOfTheCanvas` now has access to the editor's UI context. ([#3782](https://github.com/tldraw/tldraw/pull/3782))
- `useEditor` and other context-based hooks now throw an error when used out-of-context. ([#3750](https://github.com/tldraw/tldraw/pull/3750))
- Add `defaultShapeSchemas` for custom multiplayer implementations. ([#3613](https://github.com/tldraw/tldraw/pull/3613))

### Improvements

- Add a heart shape to the geo shape set. ([#3787](https://github.com/tldraw/tldraw/pull/3787))
- Improve rendering for bookmarks without preview images. ([#3856](https://github.com/tldraw/tldraw/pull/3856))
- Improve undo/redo UX around image cropping. ([#3891](https://github.com/tldraw/tldraw/pull/3891))
- Increase default limit of shapes per page from 2000 to 4000. ([#3716](https://github.com/tldraw/tldraw/pull/3716))
- Expand accepted image types to include webp, webm, apng, and avif. ([#3730](https://github.com/tldraw/tldraw/pull/3730))
- Prune unused assets when loading `.tldr` files. ([#3689](https://github.com/tldraw/tldraw/pull/3689))
- Improve handling of mouse-type devices that support pressure (e.g., Wacom tablets). ([#3639](https://github.com/tldraw/tldraw/pull/3639))
- Change text shapes to be left-aligned by default. ([#3627](https://github.com/tldraw/tldraw/pull/3627))
- Add Desmos graph embed type. ([#3608](https://github.com/tldraw/tldraw/pull/3608))

### Bug fixes

- Fix 'insert media' undo removing other changes. ([#3910](https://github.com/tldraw/tldraw/pull/3910))
- Fix referrer being sent for bookmarks and images. ([#3881](https://github.com/tldraw/tldraw/pull/3881))
- Fix minimum drag distance being wrong when zoomed. ([#3873](https://github.com/tldraw/tldraw/pull/3873))
- Fix cropped images not exporting properly. ([#3837](https://github.com/tldraw/tldraw/pull/3837))
- Fix spacebar and middle mouse button panning. ([#3791](https://github.com/tldraw/tldraw/pull/3791)) ([#3792](https://github.com/tldraw/tldraw/pull/3792))
- Fix cursor and shapes wiggling when following someone's viewport. ([#3695](https://github.com/tldraw/tldraw/pull/3695))
- Fix cross-browser focus management issues. ([#3718](https://github.com/tldraw/tldraw/pull/3718))
- Fix imports in Astro. ([#3742](https://github.com/tldraw/tldraw/pull/3742))
- Fix copy/paste for patterned shapes as SVG/PNG. ([#3708](https://github.com/tldraw/tldraw/pull/3708))
- Fix RTL text layout for SVG exports. ([#3680](https://github.com/tldraw/tldraw/pull/3680))
- Fix clicking on minimap sometimes not changing viewport. ([#3617](https://github.com/tldraw/tldraw/pull/3617))

[View release on GitHub](https://github.com/tldraw/tldraw/releases/tag/v2.2.0)

---

### Patch releases

#### v2.2.1

- Improve documentation and make `ArrowBindingUtil` public. ([#3958](https://github.com/tldraw/tldraw/pull/3958))
- Add `editor.getSnapshot()` and `editor.loadSnapshot()` methods. ([#3958](https://github.com/tldraw/tldraw/pull/3958))
- Fix CSS styling for bookmark elements. ([#3958](https://github.com/tldraw/tldraw/pull/3958))

[View release on GitHub](https://github.com/tldraw/tldraw/releases/tag/v2.2.1)

#### v2.2.2

- Fix text measurement breaking when `Tldraw` was used with React strict mode. ([#4004](https://github.com/tldraw/tldraw/pull/4004))

[View release on GitHub](https://github.com/tldraw/tldraw/releases/tag/v2.2.2)

#### v2.2.3

- Fix copy/paste functionality in Firefox 127+. ([#4006](https://github.com/tldraw/tldraw/pull/4006))

[View release on GitHub](https://github.com/tldraw/tldraw/releases/tag/v2.2.3)

#### v2.2.4

- Fix copy/paste functionality for older versions of Firefox. ([#4011](https://github.com/tldraw/tldraw/pull/4011))

[View release on GitHub](https://github.com/tldraw/tldraw/releases/tag/v2.2.4)

## v2.1.0

This release includes completely redesigned sticky notes, an overhaul to our migration system, and significant performance improvements.

### What's new

#### New stickies ([#3249](https://github.com/tldraw/tldraw/pull/3249))

Sticky notes have been completely redesigned with a fresh look and improved functionality.

#### 💥 New migrations ([#3220](https://github.com/tldraw/tldraw/pull/3220))

The migrations system has been overhauled for better reliability and developer experience.

- The `Migrations` type is now called `LegacyMigrations`
- The serialized schema format (returned by `StoreSchema.serialize()` and `Store.getSnapshot()`) has changed
- `compareRecordVersions` and `RecordVersion` have been removed
- `compareSchemas` is gone—use `schema.getMigrationsSince(prevSchema)` instead
- `defineMigrations` has been deprecated. See [updating legacy shape migrations](https://tldraw.dev/docs/persistence#Updating-legacy-shape-migrations-defineMigrations) for upgrade instructions
- `migrate` has been removed

#### 💥 Input buffering ([#3223](https://github.com/tldraw/tldraw/pull/3223))

Events are now buffered and sent to state nodes every tick, instead of immediately. This unlocks big performance improvements but could introduce subtle issues with custom tools—make sure to test any custom tools thoroughly.

#### 💥 React-powered SVG exports ([#3117](https://github.com/tldraw/tldraw/pull/3117))

If any of your shapes implement `toSvg` for exports, you'll need to replace your implementation with a new version that returns JSX instead of manually constructing SVG DOM nodes.

`editor.getSvg` is deprecated. Use `editor.getSvgElement` or `editor.getSvgString` instead.

#### 💥 Component-based toolbar customization ([#3067](https://github.com/tldraw/tldraw/pull/3067))

If you're using the `toolbar` callback to override toolbar items, switch to using the new `Toolbar` component override. See the [toolbar groups example](https://tldraw.dev/examples/ui/toolbar-groups) for details.

### API changes

- 💥 **`getRenderingShapes`** no longer returns `isCulled`—use `getCulledShapes` instead.
- Add `Editor.getStyleForNextShape`. ([#3039](https://github.com/tldraw/tldraw/pull/3039))
- Add `usePreloadAssets` export. ([#3545](https://github.com/tldraw/tldraw/pull/3545))

### Improvements

- Improve color contrast. ([#3486](https://github.com/tldraw/tldraw/pull/3486))
- Add long press event. ([#3275](https://github.com/tldraw/tldraw/pull/3275))
- Add severity colors and icons to toasts. ([#2988](https://github.com/tldraw/tldraw/pull/2988))
- Improve handling of broken images/videos. ([#2990](https://github.com/tldraw/tldraw/pull/2990))
- Improve performance of draw shapes. ([#3464](https://github.com/tldraw/tldraw/pull/3464))
- Use WebGL to draw the minimap. ([#3510](https://github.com/tldraw/tldraw/pull/3510))
- Improve reactivity bookkeeping performance. ([#3471](https://github.com/tldraw/tldraw/pull/3471)) ([#3487](https://github.com/tldraw/tldraw/pull/3487))
- Improve selection/erasing performance. ([#3454](https://github.com/tldraw/tldraw/pull/3454))
- Improve performance of text shapes on iOS/Safari. ([#3429](https://github.com/tldraw/tldraw/pull/3429))
- Reduce rendered DOM nodes for geo shapes and arrows. ([#3283](https://github.com/tldraw/tldraw/pull/3283))
- Improve shape rendering performance. ([#3176](https://github.com/tldraw/tldraw/pull/3176))

### Bug fixes

- Fix copy error sound in Safari. ([#3536](https://github.com/tldraw/tldraw/pull/3536))
- Fix arrow label positioning overlapping bound shapes. ([#3512](https://github.com/tldraw/tldraw/pull/3512))
- Fix cursor chat button appearing when not in select tool. ([#3485](https://github.com/tldraw/tldraw/pull/3485))
- Fix alt-duplicating shapes sometimes not working. ([#3488](https://github.com/tldraw/tldraw/pull/3488))
- Fix camera sliding after pinch. ([#3462](https://github.com/tldraw/tldraw/pull/3462))
- Fix text shapes overflowing their bounds when resized. ([#3327](https://github.com/tldraw/tldraw/pull/3327))
- Fix incorrectly rotated handles on rotated cropping images. ([#3093](https://github.com/tldraw/tldraw/pull/3093))
- Fix videos not being sized correctly. ([#3047](https://github.com/tldraw/tldraw/pull/3047))
- Fix `localStorage` crash in React Native webviews. ([#3043](https://github.com/tldraw/tldraw/pull/3043))

[View release on GitHub](https://github.com/tldraw/tldraw/releases/tag/v2.1.0)

---

### Patch releases

#### v2.1.1

- Fix missing export for `createShapePropsMigrationIds`, part of the new migrations API. ([#3594](https://github.com/tldraw/tldraw/pull/3594))
- Add exports for `defaultEditorAssetUrls`, `PORTRAIT_BREAKPOINT`, `useDefaultColorTheme`, and `getPerfectDashProps`. ([#3594](https://github.com/tldraw/tldraw/pull/3594))

[View release on GitHub](https://github.com/tldraw/tldraw/releases/tag/v2.1.1)

#### v2.1.2

- Revert a performance optimization that caused computed caches to sometimes not invalidate correctly, leading to stale data and crashes. ([#3611](https://github.com/tldraw/tldraw/pull/3611))

[View release on GitHub](https://github.com/tldraw/tldraw/releases/tag/v2.1.2)

#### v2.1.3

- Expose migrations, validators, and versions from tlschema. ([#3613](https://github.com/tldraw/tldraw/pull/3613))
- Add `defaultShapeSchemas` which can be passed directly to `createTLSchema`. ([#3613](https://github.com/tldraw/tldraw/pull/3613))

[View release on GitHub](https://github.com/tldraw/tldraw/releases/tag/v2.1.3)

#### v2.1.4

- Fix text selection flakiness when clicking into text shapes. ([#3643](https://github.com/tldraw/tldraw/pull/3643))
- Fix edit→edit not working correctly when unfilled geo shapes are on top of other shapes. ([#3643](https://github.com/tldraw/tldraw/pull/3643))

[View release on GitHub](https://github.com/tldraw/tldraw/releases/tag/v2.1.4)

## v2.0.0

This is the initial public release of the tldraw SDK—a TypeScript library for creating infinite canvas experiences in React. After an extensive alpha and beta period, the SDK is ready for production use.

### What's new

#### The editor

At the heart of tldraw is the `Editor` class, a single object that manages all editor state and provides the API for programmatic control. The editor maintains document state through a reactive store system containing JSON-serializable records.

```tsx
import { Tldraw } from 'tldraw'

function App() {
	return (
		<Tldraw
			onMount={(editor) => {
				// Full programmatic control
				editor.createShapes([{ type: 'geo', x: 100, y: 100 }])
				editor.selectAll()
				editor.zoomToFit()
			}}
		/>
	)
}
```

#### Default shapes

The SDK ships with a complete set of default shapes:

- **Geo shapes**: Rectangle, ellipse, triangle, diamond, pentagon, hexagon, octagon, star, rhombus, oval, trapezoid, arrow-shapes, cloud, and x-box
- **Draw**: Freehand drawing with pressure sensitivity support
- **Arrow**: Connectable arrows with labels, multiple arrowhead styles, and curved/straight/elbow routing
- **Text**: Rich text with font, size, and alignment options
- **Note**: Sticky notes with customizable colors
- **Image & Video**: Media embedding with cropping support
- **Frame**: Grouping containers that clip their contents
- **Line**: Multi-point lines with various spline options
- **Highlight**: Transparent highlight strokes
- **Embed**: Web content embedding (YouTube, Figma, Google Maps, and more)
- **Bookmark**: URL previews with metadata

#### Default tools

A full set of tools for interacting with the canvas:

- **Select**: Selection, multi-select, resize, rotate, and transform shapes
- **Hand**: Pan the canvas
- **Draw**: Freehand drawing
- **Eraser**: Remove shapes by drawing over them
- **Arrow, Line, Geo, Text, Note, Frame, Highlight**: Shape creation tools
- **Laser**: Temporary pointer for presentations
- **Zoom**: Zoom in/out with click or marquee

#### State management

The SDK uses a reactive signals system for state management. All editor state is observable, with automatic dependency tracking to prevent unnecessary re-renders.

```tsx
import { track, useEditor } from 'tldraw'

const SelectedShapeCount = track(() => {
	const editor = useEditor()
	return <div>{editor.getSelectedShapeIds().length} selected</div>
})
```

#### The store

Document data lives in a `TLStore`—a reactive client-side database that supports:

- JSON-serializable records for shapes, pages, assets, and instance state
- Reactive updates via signals
- Snapshot export/import for persistence
- Schema migrations for backwards compatibility

#### Tool system

Tools are implemented as hierarchical state machines using `StateNode`. Events flow through a state chart, enabling complex multi-step interactions:

```tsx
class MyTool extends StateNode {
	static id = 'my-tool'

	onPointerDown(info) {
		const point = this.editor.inputs.currentPagePoint
		this.editor.createShape({ type: 'geo', x: point.x, y: point.y })
	}
}
```

#### Shape system

Every shape type has a `ShapeUtil` class defining its behavior—geometry, rendering, interactions, and more. Create custom shapes by extending `ShapeUtil`:

```tsx
class CardShapeUtil extends ShapeUtil<CardShape> {
	static type = 'card'

	getGeometry(shape) {
		return new Rectangle2d({ width: shape.props.w, height: shape.props.h })
	}

	component(shape) {
		return <div className="card">{shape.props.title}</div>
	}
}
```

#### User interface

A complete, responsive UI system with:

- Toolbar with tool selection and shape-specific options
- Style panel for colors, fills, fonts, and more
- Context menus with shape actions
- Keyboard shortcuts dialog
- Page management
- Zoom controls and minimap
- Actions menu and quick actions bar
- Help menu and debug panel

All UI components can be overridden or extended via the `components` prop.

#### Features

- **Multi-page documents**: Create and manage multiple pages per document
- **Undo/redo**: Full history management with marks and batching
- **Copy/paste**: Between pages, documents, and external applications
- **Export**: SVG, PNG, and JSON export
- **Zoom & pan**: Pinch, scroll, keyboard shortcuts, and programmatic control
- **Grid & snapping**: Align shapes to grid and each other
- **Grouping**: Group shapes together
- **Locking**: Prevent shape modification
- **Duplication**: Alt-drag to duplicate
- **Alignment & distribution**: Align and distribute selected shapes
- **Flip**: Horizontal and vertical flipping
- **Styles**: Color, fill, dash, size, font, and more
- **Dark mode**: Full dark mode support
- **Localization**: 30+ languages supported
- **Accessibility**: Keyboard navigation and screen reader support
- **Touch support**: Full touch and pen input support
- **Pressure sensitivity**: For supported devices

[View release on GitHub](https://github.com/tldraw/tldraw/releases/tag/v2.0.0)

---

### Patch releases

#### v2.0.1

- Avoid randomness at init time, improving consistency for SSR and testing scenarios. ([#3076](https://github.com/tldraw/tldraw/pull/3076))

[View release on GitHub](https://github.com/tldraw/tldraw/releases/tag/v2.0.1)

#### v2.0.2

- Fix JPG export functionality. ([#3199](https://github.com/tldraw/tldraw/pull/3199))

[View release on GitHub](https://github.com/tldraw/tldraw/releases/tag/v2.0.2)
