Releases
How tldraw is versioned
We do not follow SemVer.
- Major version bumps are very rare and we reserve them for special changes that signify a paradigm shift of some kind.
- Minor version bumps are released on a regular cadence - approximately monthly. They may contain breaking changes. We aim to make breaking changes as minimally disruptive as possible, but tldraw is actively evolving as we add new features. We recommend updating tldraw at a similar pace to our release cadence, and be sure to check the release notes.
- Patch version bumps are for bugfixes and hotfixes that can't wait for the next cadence release.
Changelog
v2.4.1
fix assets prop on tldraw component (#4283) (#4285)
- The
assets
prop on the<Tldraw />
and<TldrawEditor />
components is now respected.
🐛 Bug Fix
⚠️ Pushed to v2.4.x
- v2.4.x: fix workflow files (@SomeHats)
Authors: 1
- alex (@SomeHats)
v2.4.0
Welcome to the 2.4.0 release of the tldraw SDK. This month we’ve been working to bring our sync engine (the backend we developed for tldraw.com) into the SDK as a technology for general use. This release also includes new animation options for shapes, support for image flipping, and a long list of bug fixes and developer experience enhancements. Read on for the highlights and see the bottom of these notes for the list of significant fixes and changes.
Thank you also to our first-time contributors Albert Brand (@AlbertBrand) and Guillaume Grossetie (@ggrossetie)!
What's new
Sync
For the first time, we’re releasing our real-time collaboration engine—the one we developed for tldraw.com—as a general library that developers can use for their own projects. The SDK will of course still support any backend for collaboration, however we hope this will be the best and easiest for developers to use alongside the rest of the tldraw SDK. While we’re shipping the code in this release, we still have some work to do on our messaging and our documentation, so keep an eye out for an announcement in the coming weeks. For now, see the new sync
package in the repository and the new multiplayer-demo example in the tldraw repository.
Interpolation
Did you know that tldraw has basic support for animation? You can use the Editor.animateShapes
method to animate shapes on the canvas, but up to now only the position and rotation properties would be animated. In this release, we’ve given the ShapeUtil
class a new getInterpolatedProps
method. You can use this to describe how your custom shape’s other properties should be animated. We’ve only started to implement this feature for our own shapes, but check our BaseBoxShapeUtil
as an example.
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
You can use the new Editor.run
method to run a function within some additional context. By default, the function will be run inside of a transaction, meaning that all changes made during the transaction will be settled at once. This improves performance and avoids unnecessary renders in the user interface. You can also use Editor.run
’s contextual options to ignore history or ignore locked shapes.
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. You can provide a TLAssetStore
to control how assets are uploaded and retrieved.
In your own shapes & tools, these options are available through the new Editor.uploadAsset
and Editor.resolveAssetURL
methods.
Breaking changes
editor.history.ignore(cb)
has been replaced witheditor.run(cb, {history: ‘ignore’})
editor.batch
is deprecated, replaced witheditor.run
- If you’re importing from
@tldraw/state
directly, thetrack
function and all hooks (e.g.useValue
) have moved to@tldraw/state-react
.
Improvements
- Images now support horizontal and vertical flipping. (#4113)
- Custom tools can now decide whether or not they are affected by shape lock. #4208
- We now serve our icons as a single SVG rather than many individual requests. (#4150)
- Paste at point behavior is now based on a user preference. (#4104)
- We now show a toast when uploading an unsupported file type or a file that is too large. (#4114)
- We now show a toast when pasting failed due to missing clipboard permissions. (#4117)
- You can use the new
ShapeIndicators
component to add custom logic about when to display indicators. (#4083) - A shape’s opacity will now also animate. (#4242)
API changes
- The
@tldraw/state
library is now split into@tldraw/state
and@tldraw/state-react
. (#4170) - We now export the
DefaultMenuPanel
. (#4193) - The
fileSize
property ofTLImageAsset
andTLVideoAsset
is now optional. (#4206) - You can now pass a partial
TLEditorSnapshot
toTldrawImage
anduseTLStore
. (#4190) - We explicitly type our defaults for better documentation. (#4191)
EffectScheduler
anduseStateTracking
are now public. (#4155)]- Add
setDefaultValue
toStyleProp
. (#4044) - Add
ShapeUtil.getInterpolatedProps
. (#4162) - Add
Editor.run
, replacingEditor.batch
(#4042)
Bug fixes
- The font style is now correctly exported as SVG. (#4195)
- The
force
flag is now taken into account for additional camera methods. (#4214) - The user's color scheme is shown in the menu by default. (#4184)
- The padding for text shapes is now correct for dynamically scaled text shapes. (#4140)
- Changing
cameraOptions
via react no longer causes the editor to re-mount. (#4089) - High-res images may now be uploaded. (#4198)
- Locked shapes can no longer be updated, grouped, and ungrouped programmatically. (#4042)
- The snapshots prop is now used by
createTLStore
. (#4233 - Grid backgrounds work properly with multiple tldraw instances. (#4132)
- The offline icon is back! (#4127)
- Inputs stay in the right place while viewport-following. (#4108)
- Bookmarks render correctly across devices. (#4118)
- The
InFrontOfTheCanvas
component is displayed at the right stack-order. (#4024) - Frame headers stop editing correctly when they lose focus. (#4092)
- The distance offset for duplicated shapes now matches other duplication distance offsets. (#4056)
- Filled draw shapes work close with correct distance when dynamic sized. (#3974)
- Remove an artificial delay in showing an image. (#4058)
- The context menu no longer displays an empty edit menu. (#4037)
- Right-clicking the selected shape no longer selects its children. (#4034)
Authors
- Albert Brand (@AlbertBrand)
- alex (@SomeHats)
- David Sheldrick (@ds300)
- Guillaume Grossetie (@ggrossetie)
- Mime Čuvalo (@mimecuvalo)
- Mitja Bezenšek (@MitjaBezensek)
- Steve Ruiz (@steveruizok)
- Taha (@Taha-Hassan-Git)
v2.3.0
The headlines
- 🖼️ Images/video storage improvements/hooks
- ⛲️ Font/Icons CDN
- 🗜️ Images network/performance improvements/hooks (dogfooding currently, public release soon)
- 🪗 Flattening
- 📚Documentation improvements
- 🔐 Security improvements
🖼️ Images/video storage improvements/hooks
When using a local-only storage of the whiteboard, we now store the images and videos in a separate indexedDB table. This is instead of storing them as base64-encoded blobs in the room's JSON. That wasn't very performant and blew up the size of the room's JSON quite a bit. This separates out those assets into a separate retrieval mechanism for better separation-of-concerns.
⛲️ Font/Icons CDN
We improved our out-of-the-box/batteries-included CDN for our assets (fonts/icons). We were using unpkg before but now we're just using Cloudflare directly for more fine-grained control.
🗜️ Images network/performance improvements/hooks (dogfooding currently, public release soon)
We've been working on something we call 'level-of-detail' internally for images. We're testing this currently on tldraw.com where for higher-resolution images (over a couple megabytes), the image will get transformed to an appropriate size depending on things like your viewport, zoom level, current network speed, among other considerations. This helps with bandwidth usage and browser memory usage especially when you have multiple high-res images on the board.
🪗 Flattening
@steveruizok worked on adding being able to select multiple shapes and flatten them into an image. For those moments when you need to press 🙏, clamp 🗜️, and squeeze things together. 🪗
📚 Documentation improvements
@SomeHats did a ton of unglorious work to make our documentation much more manageable. So sparkly ✨
🔐 Security improvements
We locked down our referrer network requests in #3884 and #3881 to make sure we weren't leaking tldraw rooms to external media/iframe requests.
🚀 Features
- Styling: Secretly adds a fill-fill style (
Alt
-F
) #3966 (@steveruizok) - Flattening: Add Flatten, a new menu item to flatten shapes into images #3933 (@steveruizok)
- Images/videos: make option to transform urls dynamically to provide different sized images on demand. (only internal API for now) #3827 (@mimecuvalo)
- Images/videos: store as reference to blob in indexedDB instead of storing directly as base64 in the snapshot. #3836 (@mimecuvalo)
- Font/Icons CDN: Start using our own cdn instead of unpkg. #3923 (@MitjaBezensek)
- API Change: Make
ArrowBindingUtil
public. #3913 (@ds300)
🔐 Security
- Security: enforce use of our fetch function and its default
referrerpolicy
#3884 (@mimecuvalo)
💄 Improvements
- Theme: Allow users to use system's appearance (dark / light) mode. #3703 (@MitjaBezensek)
- Font/Icons CDN: improve font loading timing on
tldraw.com
. #3927 (@mimecuvalo) (@steveruizok) - API Change:
getSnapshot
andloadSnapshot
onEditor
class #3912 (@ds300) - Document name: Remove ability to rename document while in readonly mode #3911 (@Taha-Hassan-Git)
- Viewport: Add a delay and easing to edge scrolling. #3950 (@steveruizok)
🐛 Bug Fixes
- Images/videos: follow-up fixes for Level-of-detail #3934 (@mimecuvalo)
- Images/videos: avoid double request for animated images. #3924 (@mimecuvalo)
- Images/videos: don't transform SVGs #3972 (@mimecuvalo)
- Images/videos: don't resize images that are culled #3970 (@mimecuvalo)
- Images/videos: use correct id for asset #3968 (@mimecuvalo)
- Images/videos: fix
isAnimated
checks when adding an image to a shared room. #3967 (@mimecuvalo) - Images/videos: fix copy/paste for new asset resolver mechanic. #3959 (@mimecuvalo)
- Images/videos: fix up videos with
indexedDB
#3954 (@mimecuvalo) - Images/videos: fix up missing timeout from bad merge #3964 (@mimecuvalo)
- Font/Icons CDN: add crossorigin tag for preloaded fonts #3953 (@mimecuvalo)
- Font/Icons CDN: Retrigger canary packaging. #3944 (@MitjaBezensek)
- Font/Icons CDN: Retrigger canary package build to publish a new package and upload assets to R2. #3943 (@MitjaBezensek)
- Font/Icons CDN: Retrigger canary package build to publish a new package and upload assets to R2. #3942 (@MitjaBezensek)
- Font/Icons CDN: Fix an issue with uploading the static assets. #3941 (@MitjaBezensek)
- Font/Icons CDN: Upload our static assets (fonts, icons, embed-icons, translations) to a R2 bucket so that we can move away from using unpkg and start using our own cdn. #3921 (@MitjaBezensek)
- Fix copy/paste for older versions of Firefox #4008 (@mimecuvalo)
- Fix copy/paste in Firefox 127+ #4003 (@mimecuvalo)
- Fix not allowing editing locked shapes when edit→edit mode. #4007 (@mimecuvalo)
- Fix a bug causing text shape measurement to work incorrectly when using react strict mode #4001 (@SomeHats)
- Fix typo on
CardShapeUtil
name in the custom shape example documentation. #3998 (@bholmesdev) - Fix border color for following user. #3975 (@ds300)
- Fix scale issue with new draw lines #3971 (@steveruizok)
- Fix edge scrolling at odd browser zoom levels. #3973 (@ds300)
- Fix the appearance of solid-style heart shapes. #3963 (@steveruizok)
- Fix the position of multiple assets when pasted / dropped onto the canvas. #3965 (@steveruizok)
- Fix a bug with the indicator for stylus-drawn draw shapes. #3962 (@steveruizok)
- Fix bookmark padding #3955 (@mimecuvalo)
- Rename
color-scheme
totheme
#3991 (@mimecuvalo)
📚 Documentation
- Generated docs cleanup #3935 (@SomeHats)
- Inline documentation links in type excerpts #3931 (@SomeHats)
- Better generated docs for react components #3930 (@SomeHats)
- Assets: mark assetOptions as
@internal
#4014 (@mimecuvalo) - Fix the license in the readme file for the
store
package. #3990 (@MitjaBezensek) - Better auto-generated docs for Tldraw and TldrawEditor #4012 (@SomeHats)
- Document inherited members in reference #3956 (@SomeHats)
- Improve filtering of examples #3929 (@Taha-Hassan-Git)
🛠️ VS Code
- VS Code 2.0.36 release. #3922 (@MitjaBezensek)
- Fix VS Code publishing. #3976 (@MitjaBezensek)
- Automate publishing of the VS Code extension. #3905 (@MitjaBezensek)
- Introduce a new CI environment and use it for publishing vs code extension. #3981 (@MitjaBezensek)
Authors: 7
- alex (@SomeHats)
- Ben Holmes (@bholmesdev)
- David Sheldrick (@ds300)
- Mime Čuvalo (@mimecuvalo)
- Mitja Bezenšek (@MitjaBezensek)
- Steve Ruiz (@steveruizok)
- Taha (@Taha-Hassan-Git)
v2.2.4
clipboard: fix copy/paste bad typo, ugh (#4008) (#4011)
- Clipboard: fix copy/paste for older versions of Firefox
🐛 Bug Fix
tldraw
- clipboard: fix copy/paste bad typo, ugh (#4008) #4011 (@mimecuvalo)
Authors: 1
- Mime Čuvalo (@mimecuvalo)
v2.2.3
Release Notes
clipboard: fix copy/paste on Firefox (#4003) (#4006)
- Clipboard: fix copy/paste in Firefox 127+
🐛 Bug Fix
tldraw
- clipboard: fix copy/paste on Firefox (#4003) #4006 (@mimecuvalo)
Authors: 1
- Mime Čuvalo (@mimecuvalo)
v2.2.2
2.2.2 (#4004)
Fixes a bug that would break text measurement when Tldraw
was used with react strict mode (#4001)
🐛 Bug Fix
Authors: 1
- alex (@SomeHats)
v2.2.1
2.2.1 (#3958)
- Docs improvements (#3930, #3931, #3935, #3956)
- Make
ArrowBindingUtil
public (#3913) - Add
editor.getSnapshot()
andeditor.loadSnapshot
(#3912) - CSS tweaks on bookmark elements (#3955)
🐛 Bug Fix
@tldraw/editor
,tldraw
- 2.2.1 #3958 (@SomeHats @ds300 @mimecuvalo)
Authors: 3
- alex (@SomeHats)
- David Sheldrick (@ds300)
- Mime Čuvalo (@mimecuvalo)
v2.2.0
Bindings
Bindings allow you to create relationships between shapes. Our default arrow shapes are a great example of this: each end of the arrow can bind to the shape it's pointing to. When that shape moves, so does the arrow. Before this change, it wasn't possible to build things like arrows on top of the tldraw sdk - arrows were hard-coded into the library. Now, with bindings, you can create arrows, constraint systems, visual programming environments, and much more.
Check out the bindings guide for more information. (#3326, #3780, #3797, #3800, #3871)
Camera constraints
You can now limit the camera in tldraw to a certain fixed area of the canvas. This is useful for creating experiences that don't quite fit the "infinite canvas" paradigm: document annotators, image editor, slideshow creators, etc.
See the camera constraints guide for more information. (#3282, #3747, #3814, #3828, #3844, #3863)
Configurable options prop
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
for details. (#3799, #3900)
Breaking changes
- The
canBind
flag now accepts an options object instead of just the shape in question. If you're relying on its arguments, check outTLShapeUtilCanBindOpts
for its replacement. editor.sideEffects.registerBatchCompleteHandler
has been replaced witheditor.sideEffects.registerOperationCompleteHandler
(#3748)editor.getArrowInfo(shape)
has been replaced withgetArrowInfo(editor, shape)
editor.getArrowsBoundTo(shape)
has been removed. Instead, useeditor.getBindingsToShape(shape, 'arrow')
and follow thefromId
of each binding to the corresponding arrow shape- These types have moved from
@tldraw/editor
totldraw
:TLArcInfo
TLArrowInfo
TLArrowPoint
- The
start
andend
properties onTLArrowShape
no longer havetype: point | binding
. Instead, they're always a point, which may be out of date if a binding exists. To check for & retrieve arrow bindings, usegetArrowBindings(editor, shape)
instead. getArrowTerminalsInArrowSpace
must be passed aTLArrowBindings
as a third argument:getArrowTerminalsInArrowSpace(editor, shape, getArrowBindings(editor, shape))
- The following types have been renamed:
ShapeProps
->RecordProps
ShapePropsType
->RecordPropsType
TLShapePropsMigrations
->TLPropsMigrations
SchemaShapeInfo
->SchemaPropsInfo
Undo/redo
1. History Options
Previously, some (not all!) commands accepted a history options object with squashing
, ephemeral
, and preserveRedoStack
flags. Squashing enabled/disabled a memory optimisation (storing individual commands vs squashing them together). Ephemeral stopped a command from affecting the undo/redo stack at all. Preserve redo stack stopped commands from wiping the redo stack. These flags were never available consistently - some commands had them and others didn't.
In this version, most of these flags have been removed. squashing
is gone entirely (everything squashes & does so much faster than before). There were a couple of commands that had a special default - for example, updateInstanceState
used to default to being ephemeral
. Those maintain the defaults, but the options look a little different now - {ephemeral: true}
is now {history: 'ignore'}
and {preserveRedoStack: true}
is now {history: 'record-preserveRedoStack'}
.
If you were previously using these options in places where they've now been removed, you can use wrap them with editor.history.ignore(fn)
or editor.history.batch(fn, {history: 'record-preserveRedoStack'})
. For example,
editor.nudgeShapes(..., { ephemeral: true })
can now be written as
editor.history.ignore(() => {
editor.nudgeShapes(...)
})
2. Automatic recording
Previously, only commands (e.g. editor.updateShapes
and things that use it) were added to the undo/redo stack. Everything else (e.g. editor.store.put
) wasn't. Now, everything that touches the store is recorded in the undo/redo stack (unless it's part of mergeRemoteChanges
). You can use editor.history.ignore(fn)
as above if you want to make other changes to the store that aren't recorded - this is short for editor.history.batch(fn, {history: 'ignore'})
When upgrading to this version of tldraw, you shouldn't need to change anything unless you're using store.put
, store.remove
, or store.applyDiff
outside of store.mergeRemoteChanges
. If you are, you can preserve the functionality of those not being recorded by wrapping them either in mergeRemoteChanges
(if they're multiplayer-related) or history.ignore
as appropriate.
3. Side effects
Before this diff, any changes in side-effects weren't captured by the undo-redo stack. This was actually the motivation for this change in the first place! But it's a pretty big change, and if you're using side effects we recommend you double-check how they interact with undo/redo before/after this change. To get the old behaviour back, wrap your side effects in editor.history.ignore
.
4. Mark options
Previously, editor.mark(id)
accepted two additional boolean parameters: onUndo
and onRedo
. If these were set to false, then when undoing or redoing we'd skip over that mark and keep going until we found one with those values set to true. We've removed those options - if you're using them, let us know and we'll figure out an alternative!
Improvements
- Nicer rendering for bookmarks without preview images. (#3856)
- Improve undo/redo UX around image cropping. (#3891)
- Disable toolbar items that don't work when not in select mode. (#3819)
- ❤️ We've added a heart shape to the geo shape set. (#3787)
- Detect coarse pointers (ie touch) more reliably. (#3572, #3656, #3795)
- Reduce padding when zooming to fit. (#3798)
- Increase the default limit of shapes per page from 2000 to 4000 (#3716)
- Unify list of accepted image types and expand to include webp, webm, apng, & avif. (#3730)
- Prunes unused assets when loading a
.tldr
file. (#3689) - Improve handling of mouse-type devices that support pressure, e.g. wacom tablets. They now use the same freehand options as true pen-type inputs. (#3639)
- Separate the text align property for text shapes and labels. Text shapes are now left-aligned by default. (#3627)
- Add desmos graph embed type. (#3608)
- Tweak default gap value to be consistent with sticky note gaps. (#3606)
API changes
- Add
editor.blur
method. (#3875) - Better defaults for
createTLStore
. (#3886) - Add
getSnapshot
andloadSnapshot
for easier loading/saving of tldraw documents. Read more here. (#3811) - Add
select
option toEditor.groupShapes
andEditor.ungroupShapes
. (#3690) InFrontOfTheCanvas
now has access to the editor's UI context (#3782)useEditor
and other context-based hooks will now throw an error when used out-of-context, instead of returning a fake value. (#3750)- Expose migrations, validators, and versions from tlschema. Previously, we weren't exporting migrations & validators for our default shapes. This meant that it wasn't possible to make your own tlschema with both our default shapes and some of your own (e.g. for custom multiplayer). This fixes that by exposing all the migrations, validators, and versions from tlschema, plus
defaultShapeSchemas
which can be passed directly tocreateTLSchema
.(#3613)
Bug fixes
- Fix 'insert media' undo removing other changes. (#3910)
- Fix referrer being sent for bookmarks and images. (#3881)
- Prevent stale shape data sometimes being used in render. (#3882)
- Fix an issue with pen pressure. (#3877)
- Fixed a bug where the minimum distance for a drag was wrong when zoomed in or out. (#3873)
- Make sure timers/animation frames are disposed along with the editor. (#3852)
- Fix some inconsistencies with text label rendering. (#3830)
- Fixed cropped images not exporting properly. (#3837)
- Fix bug with spacebar & middle mousenbutton panning. (#3791, #3792)
- Make sure any in-progress interactions are cancelled when switching page/ (#3771
- Fixes a bug that caused the cursor & shapes to wiggle around when following someone else's viewport. (#3695)
- Fix some long-stanging cross-browser issues with focus management. (#3718)
- Fix bug preventing imports in Astro. (#3742)
- Fixes an issue with copy pasting shapes as svg and png not correctly working for patterned shapes. (#3708)
- Fix RTL text layout for SVG exports. (#3680)
- Fixes a rare crash effecting text shapes on mobile. (#3672)
- Fix textbox direction when it contains both RTL and LTR languages /(#3188)
- Fix an links in embeds that open the embedded site (e.g. YouTube). (#3609)
- Fix pasting not working from Edit menu. (#3623)
- Fixed a bug with resizing text shapes from the left and right while holding alt. (#3632)
- Fix a bug where locked shapes could still be hovered. (#3575)
- Fix clicking on the minimap sometimes not changing the viewport. (#3617)
- Fix an issue with the minimap bugging out after you change the window's height. (#3621)
Translations
- Update French, Hungarian, & Korean translations.
- Add Bahasa Indonesia translation. (#3649)
Authors: 14
- alex (@SomeHats)
- CodeTorso (@CodeTorso)
- David Sheldrick (@ds300)
- Eric Mika (@kitschpatrol)
- Eswar Prasad Clinton. A (@eswarclynn)
- fakerr (@not-first)
- Lorenzo Lewis (@lorenzolewis)
- Lu Wilson (@TodePond)
- Mime Čuvalo (@mimecuvalo)
- Mitja Bezenšek (@MitjaBezensek)
- Mohammad Kazemi (@mokazemi)
- Steve Ruiz (@steveruizok)
- Taha (@Taha-Hassan-Git)
- Trevor Dobbertin (@Trevato)
v2.1.4
Release Notes
textfields: fix up flakiness in text selection and for unfilled geo shapes fix edit->edit (#3577) (#3643)
- Text labels: fix up regression to selection when clicking into a text shape. (was causing flaky selections)
- Text labels: fix edit→edit not working as expected when unfilled geo shapes are on 'top' of other shapes.
🐛 Bug Fix
@tldraw/editor
,tldraw
- textfields: fix up flakiness in text selection (#3578)
- textfields: for unfilled geo shapes fix edit->edit (#3577) #3643 (@mimecuvalo)
Authors: 1
- Mime Čuvalo (@mimecuvalo)
v2.1.3
Expose migrations, validators, and versions from tlschema (#3613)
Previously, we weren't exporting migrations & validators for our default shapes. This meant that it wasn't possible to make your own tlschema with both our default shapes and some of your own (e.g. for custom multiplayer). This fixes that by exposing all the migrations, validators, and versions from tlschema, plus defaultShapeSchemas
which can be passed directly to createTLSchema
📚 SDK Changes
Authors: 1
- alex (@SomeHats)