apps/docs/content/releases/v4.4.0.mdx
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.
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
}
}
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.
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.
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.
This release includes several performance improvements for large canvases:
editor.getShapesAtPoint() and editor.getShapeAtPoint().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 endsA 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.
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.
New select dropdown primitive wrapping Radix UI's Select, following existing tldraw UI patterns.
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: { ... } }} />
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.
TldrawEditorBaseProps.cameraOptions, TldrawEditorBaseProps.textOptions, TldrawEditorBaseProps.deepLinks deprecated in favor of options.camera, options.text, options.deepLinks. (#7888)TLShapeUtilCanBindOpts.fromShapeType and toShapeType replaced with fromShape and toShape accepting TLShape | { type }. (#7821)editor.spatialIndex from public API. The spatial index is now internal; use editor.getShapesAtPoint() and editor.getShapeAtPoint() for shape queries. (#7699)TldrawOptions.camera, TldrawOptions.text, and TldrawOptions.deepLinks fields to the options prop. (#7888)TldrawUiSelect, TldrawUiSelectTrigger, TldrawUiSelectValue, TldrawUiSelectContent, TldrawUiSelectItem components and associated prop types. Add iconTypes export for enumerating available icons. (#7566)useCanApplySelectionAction() hook for checking if selection actions should be enabled. (#7811)TLInstance.cameraState: 'idle' | 'moving' for tracking camera movement state. (#7826)quickZoomPreservesScreenBounds to TldrawOptions. (#7836)fillExtra to STYLES object for additional fill style options. (#7885)Editor.getShapeIdsInsideBounds() public. (#7863)ShapeUtil.getIndicatorPath() method and TLIndicatorPath type for canvas-based indicator rendering. Add ShapeUtil.useLegacyIndicator() to control whether shapes use SVG or canvas indicators. (#7708)isZoomDirectionInverted to TLUserPreferences interface and UserPreferencesManager.getIsZoomDirectionInverted() method. Add ToggleInvertZoomItem component export and toggle-invert-zoom action. (#7732)complete to TL_SCRIBBLE_STATES enum and ScribbleManager.complete(id) method for marking scribbles as complete before fading. (#7760)ScribbleManager.startSession(), stopSession(), clearSession(), extendSession(), isSessionActive(), addScribbleToSession(), addPointToSession(), and ScribbleSessionOptions type. Add LaserTool.getSessionId(). Add laserFadeoutMs to TldrawOptions. (#7681)FpsScheduler class to create FPS-throttled function queues with configurable target rates. (#7418)core-js polyfill dependency from @tldraw/editor. (#7769)zoomToFit and getCurrentPageBounds to ignore hidden shapes when computing bounds. (#7770)