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)
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:
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, #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)
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)
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, #7826, #7840, #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()andeditor.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)
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:
// 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)
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)
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)
New select dropdown primitive wrapping Radix UI's Select, following existing tldraw UI patterns.
🔜 Consolidated options prop (#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.
// Before
<Tldraw cameraOptions={{ isLocked: true }} deepLinks textOptions={{ ... }} />
// After
<Tldraw options={{ camera: { isLocked: true }, deepLinks: true, text: { ... } }} />Migration guide
Replace standalone props with equivalent options fields:
cameraOptions={...}→options={{ camera: { ... } }}textOptions={...}→options={{ text: { ... } }}deepLinksordeepLinks={...}→options={{ deepLinks: true }}oroptions={{ deepLinks: { ... } }}
The deprecated props still work. When both the deprecated prop and the options field are provided, the options value takes precedence.
API changes
- 🔜
TldrawEditorBaseProps.cameraOptions,TldrawEditorBaseProps.textOptions,TldrawEditorBaseProps.deepLinksdeprecated in favor ofoptions.camera,options.text,options.deepLinks. (#7888) - 🔜
TLShapeUtilCanBindOpts.fromShapeTypeandtoShapeTypereplaced withfromShapeandtoShapeacceptingTLShape | { type }. (#7821) - Remove
editor.spatialIndexfrom public API. The spatial index is now internal; useeditor.getShapesAtPoint()andeditor.getShapeAtPoint()for shape queries. (#7699) - Add
TldrawOptions.camera,TldrawOptions.text, andTldrawOptions.deepLinksfields to theoptionsprop. (#7888) - Add
TldrawUiSelect,TldrawUiSelectTrigger,TldrawUiSelectValue,TldrawUiSelectContent,TldrawUiSelectItemcomponents and associated prop types. AddiconTypesexport for enumerating available icons. (#7566) - Add
useCanApplySelectionAction()hook for checking if selection actions should be enabled. (#7811) - Add
TLInstance.cameraState: 'idle' | 'moving'for tracking camera movement state. (#7826) - Add
quickZoomPreservesScreenBoundstoTldrawOptions. (#7836) - Add
fillExtratoSTYLESobject for additional fill style options. (#7885) - Make
Editor.getShapeIdsInsideBounds()public. (#7863) - Add
ShapeUtil.getIndicatorPath()method andTLIndicatorPathtype for canvas-based indicator rendering. AddShapeUtil.useLegacyIndicator()to control whether shapes use SVG or canvas indicators. (#7708) - Add
isZoomDirectionInvertedtoTLUserPreferencesinterface andUserPreferencesManager.getIsZoomDirectionInverted()method. AddToggleInvertZoomItemcomponent export andtoggle-invert-zoomaction. (#7732) - Add
completetoTL_SCRIBBLE_STATESenum andScribbleManager.complete(id)method for marking scribbles as complete before fading. (#7760) - Add scribble session system:
ScribbleManager.startSession(),stopSession(),clearSession(),extendSession(),isSessionActive(),addScribbleToSession(),addPointToSession(), andScribbleSessionOptionstype. AddLaserTool.getSessionId(). AddlaserFadeoutMstoTldrawOptions. (#7681) - Add
FpsSchedulerclass to create FPS-throttled function queues with configurable target rates. (#7418)
Improvements
- Improve performance in large canvases and multiplayer rooms by optimizing Set comparisons, reducing memory allocations, and caching string hashes. (#7840)
- Improve panning performance in large documents by skipping hover hit-testing during camera movement. (#7826)
- Improve performance when translating arrows together with bound shapes by skipping unnecessary reparenting. (#7733)
- Add R-tree spatial indexing for O(log n) shape queries, improving performance of brushing, selection, and erasing with many shapes. (#7676)
- Improve laser pointer to keep all strokes visible during a session (telestrator pattern), with strokes fading together when drawing stops. (#7681)
- Improve laser pointer strokes with proper taper when lifting the pointer by adding a 'complete' state to the scribble lifecycle. (#7760)
- Improve quick zoom to use zoom-to-fit instead of fixed 5%, add "quick peek" behavior, and add edge scrolling. (#7836)
- Remove
core-jspolyfill dependency from@tldraw/editor. (#7769) - Reduce network traffic by squashing pending push requests before sending, so rapid edits result in fewer network calls. (#7724)
- Reduce solo-mode network traffic by using a dedicated FPS-based scheduler that throttles to 1 FPS when no collaborators are present. (#7657)
- Improve performance by separating UI and network scheduling into independent queues with configurable target FPS. (#7418)
- Improve frame label sizing on smaller viewports and when zoomed out. (#7746)
- Add image pipeline starter template with visual node-based AI image generation workflows. (#7863)
- Restructure agent starter template with manager-based architecture and mode system. (#7640)
Bug fixes
- Fix geo shapes with text not being resizable to a smaller size. (#7878)
- Fix shapes exploding when resizing unaligned arrows. (#7855)
- Fix Safari pinch zoom resetting selection to previous shapes. (#7777)
- Fix toggle-lock action firing when no shapes are selected. (#7815)
- Fix menu items (zoom to selection, cut, copy, delete) being enabled when not in select tool. (#7811)
- Fix excessive tab indentation in text shapes (now 2 spaces instead of 8). (#7796)
- Fix canvas-in-front elements (cursors, following indicators) appearing in front of UI panels instead of behind them. (#7865)
- Fix license console message colors for warnings and errors. (#7850)
- Fix asset resolution being delayed by 500ms when updating an image shape's asset. (#7612)
- Fix Durable Object SQLite migration in the multiplayer Cloudflare template. (#7829, #7832, #7834, #7835)
- Fix
zoomToFitandgetCurrentPageBoundsto ignore hidden shapes when computing bounds. (#7770) - Fix collaborator shape indicators not rendering after the canvas indicator change. (#7759)
- Fix rich text content not updating correctly with message squashing due to reference comparison. (#7758)
- Fix keyboard shortcut menu item labels to use consistent ellipsis formatting. (#7757)
- Fix spatial index not removing shapes when moved to a different page. (#7700)
- Fix tldraw failing to load in CJS environments (tsx, ts-node, Jest) due to ESM-only rbush dependency. (#7905)