tldraw offline is the local whiteboard for you and your agents.Download the app

BlogProduct

My favorite API is a trash can

Kevin Ingersoll

Hi, it's Kevin, tldraw Product Engineer, here.

Since joining tldraw, I've been enjoying sending my agents on side quests: exploring game ideas on a multiplayer canvas and building mini apps inside tldraw offline. Across them all, there’s one quiet API of the tldraw SDK that made all of this so much simpler.

It's an empty object.

"meta": {}

Every record in the tldraw store carries a property called meta. Every shape, every page, every asset and binding, the document itself, even the camera and the pointer. It travels across sync and persists to the database.

It’s like a trash can: a place I can put all my junk.

Where do I put my stuff?

When I am building something on top of an API, I often need a place to put my own related data. While working on a tower defense game prototype, I needed every shape on the canvas to be a game object: a drawn line was a wall, and a wall had hit points. Where do the hit points live? The line shape belongs to tldraw's store. The wall belongs to my game.

The usual answer is a mapping table, a little database of shape IDs pointing at my game state or vice versa. But then I’ve got to keep it in sync. The player duplicates a wall, and my table doesn't hear about it. The player erases a wall, I clean up my row, and then they press cmd+z and the wall comes back, but its hit points don't. Every feature the SDK ships is another thing my table can potentially fall out of sync with.

Luckily, tldraw already had an answer: put my custom data in the record itself.

{
  "id": "shape:hE2Jia9cJnksozJhiZ8qC",
  "typeName": "shape",
  "type": "draw",
  "x": 320,
  "y": 128,
  "props": { "color": "black", "size": "m" },
  "meta": {
    "hp": 3,
    "owner": "player_2"
  }
}

Records in tldraw are plain JSON objects in a flat reactive map. meta is just another part of the record.

My third-party data is actually first-party data

Because meta is an ordinary part of the record, it inherits all the benefits of the SDK. It persists wherever the document persists. It syncs to every collaborator in a multiplayer room. Undo and redo restore it. Copy, paste, and duplicate carry it. Export a .tldr file and it's in there. I didn't have to write any code to get this, it’s already built in.

The SDK helps with the common patterns, too. To initialize meta for every new shape:

<Tldraw
  onMount={(editor) => {
    editor.getInitialMetaForShape = (shape) => ({
      createdBy: editor.user.getExternalId(),
      createdAt: Date.now(),
    })
  }}
/>

To keep an updatedAt fresh, register a side effect that runs before any shape change:

editor.sideEffects.registerBeforeChangeHandler('shape', (prev, next) => ({
  ...next,
  meta: {
    ...next.meta,
    updatedAt: Date.now(),
  },
}))

And if untyped JSON makes you nervous, the schema accepts validators for meta keys, so bad data fails loudly just like it would for other fields.

We use it everywhere

On tldraw.com, we use meta to track a file's storage quota and whether assets are hidden. In fairies, we lean on multiplayer presence meta to track their position and even their outfit and pose. In our agent starter kit, agents leave notes in shapes’ meta so they remember what they’re for. Computer uses meta for LLM inputs and outputs.

We’re also playing around with a GitHub-backed project management tool in tldraw offline. Our issues and PRs can be organized spatially on the canvas, connected with arrows, grouped into collections, and kept in sync. Each shape is associated with a GitHub issue/PR using meta.

Building with our own tools greatly informs how we design our APIs, because we can directly feel what it’s like to use these tools. And meta just happens to be one of my favorites features to fall out of this dogfooding.

Dear API providers

People build products around your objects, and they have data about those objects that you will never predict. Yet, many SDK and API providers miss this, and it makes building on top much more painful.

So here's my ask: if you ship an API or an SDK, please give me a place for my own data.

Steve recently told contributors to stay away from his trash. I'm here to celebrate the opposite: tldraw gave everything a little trash can and promised to carry around my trash.

If you want to try it for yourself, fork one of our starter kits or download tldraw offline. If you do, tell us about it on X or in Discord. We’d love to see what you’re building!

Trusted by these companies

  • Loveable
  • bigpi
  • CADChat
  • Google
  • Replit
  • BlackRock
  • ClickUp
  • Autodesk
  • Google Stitch
  • Luma
  • Runway
  • SchoolAI
  • Honeycomb
  • Padlet
  • Genio
  • Pollination
  • JAM
  • Craft
  • Legendkeeper
  • Mobbin
  • Matilda workspace
  • Brisk
  • Aries
  • AlAI
  • Dirac