Releases: tldraw/tldraw
v4.5.6
v4.5.5
v4.5.4
v4.5.3
v4.5.2
v4.5.1
v4.5.0
This release adds click-through on transparent image pixels, configurable embed definitions via EmbedShapeUtil.configure(), correct high-DPI image sizing across platforms, and a new Editor.resizeToBounds() method. It also includes SVG sanitization for external content, 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)
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)
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.
import { EmbedShapeUtil, DEFAULT_EMBED_DEFINITIONS } from 'tldraw'
const shapeUtils = [
EmbedShapeUtil.configure({
embedDefinitions: [...DEFAULT_EMBED_DEFINITIONS, myCustomEmbed],
}),
]Migration guide
Before:
EmbedShapeUtil.setEmbedDefinitions([...DEFAULT_EMBED_DEFINITIONS, myEmbed])After:
const shapeUtils = [
EmbedShapeUtil.configure({
embedDefinitions: [...DEFAULT_EMBED_DEFINITIONS, myEmbed],
}),
]The embeds prop on Tldraw still works but is deprecated. Use EmbedShapeUtil.configure() instead.
API changes
-
🔜
EmbedShapeUtil.setEmbedDefinitions()deprecated. UseEmbedShapeUtil.configure({ embedDefinitions: [...] })instead. (#8034) -
🔜
Tldrawembedsprop is deprecated. Configure embed definitions viaEmbedShapeUtil.configure(). (#8034) -
Add
Geometry2d.ignoreHit(point)for rejecting hit tests on transparent pixels. (#7942) -
Add
Editor.resizeToBounds(shapes, bounds)for resizing shapes to fit target bounds. (#8120) -
Add
Editor.getResizeScaleFactor()for computing the dynamic size scale factor at the current zoom level. (#8042) -
Add
sanitizeSvg(svgText: string)export for sanitizing SVG content against XSS and data exfiltration. (#7896) -
Add experimental
experimental__onDropOnCanvasoption to intercept canvas drop events. Returntruefrom the callback to prevent the editor's default drop behavior. (#7911)<Tldraw options={{ experimental__onDropOnCanvas: (point, event) => { // Handle drop at page-space point return true // prevent default }, }} />
-
Add optional
pixelRatioproperty toTLImageAssetfor correct high-DPI image sizing. Detected automatically from PNG metadata. (#8163) -
Replace TypeScript enums (
MigrationFailureReason,PORTRAIT_BREAKPOINT) withconstobject + type alias pattern for compatibility with Node's built-in TypeScript support (strip-types). Values are unchanged. (#8084)
Improvements
- Improve arrow component rendering performance with finer-grained reactivity. (#8167)
- Simplify paste-parent selection to use
canReceiveNewChildrenOfTypeinstead of frame-specific checks. Prevent edge-only overlap from auto-reparenting pasted shapes into adjacent frames. (#8057) - Save link and alt-text values when clicking outside the editor instead of discarding changes. (#8037)
- Add SVG sanitization on paste and file drop to prevent XSS and data exfiltration. (#7896)
- Fix circular dependencies across
@tldraw/state,@tldraw/editor, and@tldraw/tldrawpackages to improve compatibility with Jest mocking and tree-shaking. (#7935) - Fix high-DPI image sizing to work correctly across macOS and Windows by detecting the source DPI baseline from PNG metadata. (#8163)
Bug fixes
- Fix shapes pasted with Ctrl+V not being parented to a frame when they land inside one. (#7938)
- Fix a crash when cropping custom shapes that don't include
isCirclein their crop schema. (#7931) - Fix a crash when loading session state without a
currentPageId(e.g. when using deep links). (#7994) - Fix U+2028/U+2029 line separators breaking chunked sync messages. (#7918)
- Fix "Download original" not triggering a download for cross-origin assets. (#8090)
- Fix arrow endpoints terminating at invisible clipped shape boundaries instead of at the frame edge. (#7932)
- Fix sticky notes having a hard shadow instead of a soft drop shadow when exported as SVG. (#7934)
- Fix rich text toolbar staying open when the editing shape is deleted by another user. (#8050)
- Fix SVG sanitizer stripping embedded SVG data URIs on
<image>elements. Nested SVGs are now recursively sanitized instead of blocked. (#8087) - Fix
TldrawSelectionForegroundcrashing when used withoutTldrawUiContextProvider. (#8011) - Fix dynamic-size shapes losing shadows and dashes too early when zoomed out. (#8040)
- Fix a crash when resizing draw or highlight shapes to zero width or height. (#8067)
- Fix crash when enabling Debug SVG with shapes on the canvas. (#8101)
- Fix
localOffsetmutation bug instretchShapeswhen shapes have parent transforms. (#8120) - Fix arrow SVG export producing invalid negative
<foreignObject>dimensions when arrows have no text label. (#8137) - Fix arrow endpoint flickering when anchors are at exact shape boundaries. (#8130)
- Fix shapes dragged from the toolbar not respecting dynamic size mode. (#8042)
- Fix dragging unselected shapes that have an
onClickhandler. (#7936) - Fix false positive "multiple instances" warning in Next.js dev mode. (#7933)
- Fix missing alt text on rendered image shapes in some cases. (#8158)
- Fix drawing on tablets that report zero pen pressure. (#5693)
- Fix
create-tldrawCLI to always create a subdirectory from the project name. (#8161)
v4.4.1
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](https://github.com/tldraw/tldraw/pull/...