- Add a Frame selection action that wraps selected shapes in a frame with cmd+shift+f
- Add embedConfig option to EmbedShapeUtil for passing per-embed configuration such as API keys
- Add allowVideoAutoplay option to TldrawOptions to control video shape autoplay behavior
- Add ShapeUtil.getContentElement and ShapeUtil.onReleaseContentElement for app-owned content element lifecycle
- Add shift+q shortcut to copy styles of hovered shape and apply them to next created shapes
- Add double-tap-and-drag zoom gesture for touch devices
- Rewrite the freehand ink algorithm to compute draw and highlighter strokes 2–3x faster and produce 57% smaller SVG path data
- Type collaborator user-id APIs as TLUserId instead of string in TLInstancePresence, TLInstance, Editor methods, and presence hooks
- Open context menu with right-click from any tool and with touch long-press on select tool
- Highlight shapes in frame tool as you drag out a frame to show which shapes it will enclose
- Show immediate loading placeholder with spinner for pasted bookmarks at paste location
- Make selection corner rotate handles easier to grab
- Reduce default indentation on rich-text lists so nested lists take up less horizontal space
- Load mermaid dependency in @tldraw/mermaid lazily so the package is requirable from CommonJS
- Remove triple-click and quadruple-click event tracking from ClickManager, including triple_click and quadruple_click events and onTripleClick and onQuadrupleClick handlers
- Move ShapeIndicatorOverlayUtil and TLShapeIndicatorOverlay from @tldraw/editor to tldraw
- Drop support for Node 20, with minimum supported Node now >=22.12.0
- Remove StrokePoint.vector and setStrokePointRadii from the freehand stroke pipeline
- Remove the obsolete selection-background slot from @tldraw/editor including DefaultSelectionBackground, TLSelectionBackgroundProps, TLEditorComponents.SelectionBackground, and useTransform helper
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)
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.
Frame selection (#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.
ClickManagerno longer tracks triple or quadruple clicks: thetriple_clickandquadruple_clickevents and theonTripleClick/onQuadrupleClickhandlers have been removed, andTLClickEventNameis now just'double_click'. (#8897) -
💥 Move
ShapeIndicatorOverlayUtilandTLShapeIndicatorOverlayfrom@tldraw/editortotldraw. Both are still exported fromtldraw; update any imports of these symbols that came directly from@tldraw/editor. (#9018) -
💥 Drop support for the EOL Node 20. The minimum supported Node is now
>=22.12.0, the first version with nativerequire()of ES modules, so tldraw can depend on ESM-only packages. (#9098) -
💥 Type the collaborator user-id APIs as
TLUserIdinstead ofstring.TLInstancePresence.userId/followingUserId,TLInstance.followingUserId/highlightedUserIds,Editor.startFollowingUser(),Editor.zoomToUser(),usePresence(),usePeerIds(), and the people-menu prop types now useTLUserId. Runtime behavior is unchanged:TLUserIdis a branded string, so cast plain strings at the call site, for exampleeditor.startFollowingUser(userId as TLUserId). (#9002) -
💥 Remove
StrokePoint.vectorandsetStrokePointRadiifrom the freehand stroke pipeline. The vector is derivable from consecutive points and radius/taper computation now happens insidegetStrokeandsvgInk; nothing in the SDK consumed these outside the pipeline itself. (#9154) -
💥 Remove the obsolete selection-background slot from
@tldraw/editor:DefaultSelectionBackground,TLSelectionBackgroundProps,TLEditorComponents.SelectionBackground, and theuseTransformhelper. These were unused and have no replacement; remove any references to them. (#9362) -
Add an
embedConfigoption toEmbedShapeUtilfor passing per-embed configuration such as API keys. The default Google Maps embed no longer readsprocess.env.NEXT_PUBLIC_GC_API_KEY— pass the key explicitly instead. (#9068)EmbedShapeUtil.configure({ embedConfig: { google_maps: { apiKey: '...' } }, }) -
Add an
allowVideoAutoplayoption toTldrawOptions. When set tofalse, no video shape autoplays regardless of its per-shapeautoplayprop — including pasted and restored shapes. Defaults totrue. (#8943) -
Add
ShapeUtil.getContentElementandShapeUtil.onReleaseContentElement, an app-owned content element lifecycle that lets stateful shape content (such as cross-origin iframes) survive unmounting the editor. (#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)
- Add a
shift+qshortcut that copies the styles of the hovered shape and applies them to the next shapes you create. (#9028) - Add a double-tap-and-drag zoom gesture for touch devices. (#7858)
- As you drag out a frame, the frame tool now highlights the shapes it will enclose. (#9102)
- Pasted bookmarks now show an immediate loading placeholder with a spinner at the paste location, instead of waiting for metadata before appearing. (#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)
- Make the selection corner rotate handles easier to grab. (#9374)
- Reduce the default indentation on rich-text lists so nested lists take up less horizontal space. (#9287)
- Hide the "back to content" button during the animation that pans back to the canvas when focusing it. (#9278)
- Remove the extraneous
titletooltips on menu items. (#9315) @tldraw/mermaidnow loads its ESM-onlymermaiddependency lazily, so the package is requirable from CommonJS and mermaid only loads when a diagram is created. (#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)
- Include
DOCS.mddocumentation in published npm packages, and a generated docs and release notes rollup in thetldrawpackage, so documentation is available when browsing package artifacts. (#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)
- Improve performance when resizing many shapes at once. (#9153)
- Improve change tracking and sync performance when many shapes change at once. (#9151, #9152)
- Improve validation performance during high-frequency updates. (#9150)
- Speed up editor load by caching generated pattern fill defs and deduplicating icon preloads, so neither is rebuilt on every editor mount. (#9345, #9351)
- Remove the
jittered-fractional-indexingdependency and speed up index key generation and validation; newly generated index keys are slightly shorter. (#9352) - Improve minimap rendering performance on large documents. (#9355)
Bug fixes
- Fix the fill style dropdown trigger showing a misleading tooltip for the currently selected fill. (#9023)
- Fix a two-finger pinch unintentionally changing the selection on touch devices. (#9006)
- Pasting content while dragging, translating, resizing, or rotating no longer steals selection from the shape being manipulated. (#8976)
- Fix a bug where pressing
Tabwhile editing a list inside a note shape created a new note instead of indenting the list item. (#8958) - Fix the viewport shifting slightly when zooming past the minimum or maximum zoom level. (#8957)
- Improve the contrast of disabled buttons and menu items in dark mode. (#8931)
- Fix
debounce(...).cancel()leaving an awaited call hanging forever; it now rejects the pending promise. (#8683) - Fix arrows jumping to incorrect positions after undoing or redoing shape deletions involving bindings. (#8283)
- Fix touch long-press on shape-creation tools so the context menu opens without leaving a stray shape behind. (#8946)
- Fix text labels drifting vertically between browsers (e.g. Chrome vs Safari) in multiplayer by snapping line-height to whole pixels. (#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, #9002)
- Fix releasing a modifier while still holding a shortcut key triggering the adjacent non-modifier shortcut. (#9099)
- Fix redo (
cmd+shift+z) not firing after an undo (cmd+z) on macOS when cmd is kept held. (#9139) - Fix releasing a fast right-click pan zooming the camera to the maximum zoom level. (#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, #9184)
- Fix double-clicking an arrow handle no longer toggling the arrowhead on that end. (#9180)
- Fix the camera jumping when a one-finger hand-tool pan transitions into a two-finger pinch zoom. (#9182)
- Fix arrows becoming partially un-hoverable and un-clickable when bindings were created or updated without a shape change (e.g.
createBindingsaftercreateShapes). (#9183) - Fix camera state getting stuck at
movingwhen the editor is disposed during camera movement (e.g. React strict mode with a shared store). (#9201) - Fix the following indicator border being hidden beneath UI panels. (#9210)
- Fix the sync version-mismatch handshake reporting
CLIENT_TOO_OLDwhen the server is the one running an older version; it now correctly reportsSERVER_TOO_OLD. (#9212) - Fix ctrl+click on macOS not selecting locked shapes or opening the context menu; it now behaves like a right-click. (#9234)
- Fix a crash that could occur when undo or redo restored an editing state for a shape that no longer exists. (#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)
- Fix style scrubbing selecting the wrong option when you release the pointer over a different style section. (#9280)
- Fix empty rich text (an empty array) not being treated as an empty label. (#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)
- Fix inconsistent easing when clicking the minimap to move the camera. (#9355)
- Fix sync clients crashing when a malformed WebSocket message is received. (#9356)
- Fix a crash when measuring viewport bounds after the editor document body is unavailable. (#9357)
- Fix highlighted rich text showing an outline in dark mode. (#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)
- Fix a crash when a shape is deleted mid-click (e.g. by a remote user or an undo) while clicking on it. (#9376)
- Fix the license manager treating loopback hosts other than
localhost(such as127.0.0.1and::1) as production environments. (#9384) - Fix the crop of a grouped, cropped image shifting when the group is flipped by dragging a resize handle. (#9395)