snapshot of https://github.com/tldraw/tldraw.git (main, 2026-09-23; blobs > 131072B omitted)
Some checks failed
Deploy analytics worker / Deploy analytics worker to ${{ (github.ref == 'refs/heads/production' && 'production') || (github.ref == 'refs/heads/main' && 'staging') || 'preview' }} (push) Failing after 1s
i18n upload strings / Upload latest strings (push) Failing after 1s
Deploy bemo / Deploy bemo to ${{ (github.ref == 'refs/heads/bemo-production' && 'production') || (github.ref == 'refs/heads/main' && 'staging') || 'preview' }} (push) Failing after 1s
Deploy grafana resources / ${{ github.event_name == 'push' && 'Deploy grafana resources' || 'Validate grafana resources (dry run)' }} (push) Failing after 0s
End to end tests (dotcom) / Check for relevant changes (push) Failing after 0s
End to end tests (examples) / Check for relevant changes (push) Failing after 0s
End to end tests (mermaid readability) / Check for relevant changes (push) Failing after 1s
End to end tests (perf) / Check for relevant changes (push) Failing after 0s
Publish editor extensions / Publish editor extensions (push) Failing after 1s
Publish SDK packages / Remove the publish-packages label (push) Has been skipped
Checks / Tests & checks (push) Has been cancelled
Checks / Build all projects (push) Has been cancelled
Deploy templates / Deploy templates (push) Has been cancelled
End to end tests (dotcom) / End to end tests (dotcom) (push) Has been cancelled
End to end tests (mermaid readability) / End to end tests (mermaid readability) (push) Has been cancelled
End to end tests (perf) / End to end tests (perf) (push) Has been cancelled
Deploy .com / Deploy dotcom to ${{ (github.ref == 'refs/heads/production' && 'production') || (github.ref == 'refs/heads/main' && 'staging') || 'preview' }} (push) Has been cancelled
End to end tests (examples) / End to end tests (examples) (push) Has been cancelled
Publish SDK packages / Publish SDK packages (push) Has been cancelled
Publish SDK packages / Tell user released version (push) Has been cancelled
Publish SDK packages / Publishes code templates to separate repositories (push) Has been cancelled
Publish SDK packages / Deploys the template demos with the new SDK version (push) Has been cancelled
Some checks failed
Deploy analytics worker / Deploy analytics worker to ${{ (github.ref == 'refs/heads/production' && 'production') || (github.ref == 'refs/heads/main' && 'staging') || 'preview' }} (push) Failing after 1s
i18n upload strings / Upload latest strings (push) Failing after 1s
Deploy bemo / Deploy bemo to ${{ (github.ref == 'refs/heads/bemo-production' && 'production') || (github.ref == 'refs/heads/main' && 'staging') || 'preview' }} (push) Failing after 1s
Deploy grafana resources / ${{ github.event_name == 'push' && 'Deploy grafana resources' || 'Validate grafana resources (dry run)' }} (push) Failing after 0s
End to end tests (dotcom) / Check for relevant changes (push) Failing after 0s
End to end tests (examples) / Check for relevant changes (push) Failing after 0s
End to end tests (mermaid readability) / Check for relevant changes (push) Failing after 1s
End to end tests (perf) / Check for relevant changes (push) Failing after 0s
Publish editor extensions / Publish editor extensions (push) Failing after 1s
Publish SDK packages / Remove the publish-packages label (push) Has been skipped
Checks / Tests & checks (push) Has been cancelled
Checks / Build all projects (push) Has been cancelled
Deploy templates / Deploy templates (push) Has been cancelled
End to end tests (dotcom) / End to end tests (dotcom) (push) Has been cancelled
End to end tests (mermaid readability) / End to end tests (mermaid readability) (push) Has been cancelled
End to end tests (perf) / End to end tests (perf) (push) Has been cancelled
Deploy .com / Deploy dotcom to ${{ (github.ref == 'refs/heads/production' && 'production') || (github.ref == 'refs/heads/main' && 'staging') || 'preview' }} (push) Has been cancelled
End to end tests (examples) / End to end tests (examples) (push) Has been cancelled
Publish SDK packages / Publish SDK packages (push) Has been cancelled
Publish SDK packages / Tell user released version (push) Has been cancelled
Publish SDK packages / Publishes code templates to separate repositories (push) Has been cancelled
Publish SDK packages / Deploys the template demos with the new SDK version (push) Has been cancelled
This commit is contained in:
124
apps/docs/content/sdk-features/locked-shapes.mdx
Normal file
124
apps/docs/content/sdk-features/locked-shapes.mdx
Normal file
@@ -0,0 +1,124 @@
|
||||
---
|
||||
title: Locked shapes
|
||||
created_at: 12/28/2025
|
||||
updated_at: 12/29/2025
|
||||
keywords:
|
||||
- locked
|
||||
- lock
|
||||
- unlock
|
||||
- protection
|
||||
- immutable
|
||||
status: published
|
||||
date: 12/28/2025
|
||||
readability: 9
|
||||
voice: 9
|
||||
completeness: 9
|
||||
accuracy: 10
|
||||
notes: Add links to examples. Could use more casual contractions where "doesn't" opportunities exist.
|
||||
---
|
||||
|
||||
Locked shapes can't be selected, moved, resized, edited, or deleted through normal interactions. Lock a shape when you want it to stay exactly where it is: background elements, reference images, or layout guides that shouldn't move while you work on other parts of the canvas.
|
||||
|
||||
## The isLocked property
|
||||
|
||||
Every [shape](/docs/shapes) has an `isLocked` boolean property. When `true`, the shape is protected from most modifications:
|
||||
|
||||
```typescript
|
||||
// Create a locked shape
|
||||
editor.createShape({
|
||||
type: 'geo',
|
||||
x: 100,
|
||||
y: 100,
|
||||
isLocked: true,
|
||||
props: { w: 200, h: 150, geo: 'rectangle' },
|
||||
})
|
||||
|
||||
// Check if a shape is locked
|
||||
const shape = editor.getShape(shapeId)
|
||||
if (shape.isLocked) {
|
||||
// Shape is locked
|
||||
}
|
||||
```
|
||||
|
||||
## Toggling lock state
|
||||
|
||||
Use [Editor#toggleLock](?) to flip the lock state of shapes. If shapes have mixed lock states, they all become locked:
|
||||
|
||||
```typescript
|
||||
// Lock selected shapes
|
||||
editor.toggleLock(editor.getSelectedShapeIds())
|
||||
|
||||
// Lock specific shapes by ID
|
||||
editor.toggleLock([shapeId1, shapeId2])
|
||||
```
|
||||
|
||||
When you lock shapes that were all previously unlocked, they automatically deselect. This prevents accidentally manipulating them while they're protected.
|
||||
|
||||
## What locking prevents
|
||||
|
||||
Locked shapes resist most operations:
|
||||
|
||||
| Operation | Behavior |
|
||||
| ------------ | -------------------------------------------------------------------------------------------------------- |
|
||||
| Selection | Clicking a locked shape doesn't select it. Brush selection and [Editor#selectAll](?) skip locked shapes. |
|
||||
| Movement | Pointer drags pass through locked shapes to the canvas. |
|
||||
| Modification | [Editor#updateShape](?) ignores locked shapes, except for an update that unlocks them. |
|
||||
| Deletion | [Editor#deleteShapes](?) and [Editor#duplicateShapes](?) skip locked shapes. |
|
||||
| Grouping | [Editor#groupShapes](?) and [Editor#ungroupShapes](?) skip locked shapes. |
|
||||
| Editing | Double-clicking a locked shape doesn't enter edit mode. |
|
||||
|
||||
Right-clicking a locked shape still selects it so the context menu can offer an unlock option. If you want left-click, brush, and scribble selection to include locked shapes too, set the `selectLockedShapes` option in [TldrawOptions](?). The shapes stay protected from moves, edits, and deletes; `selectAll` still skips them.
|
||||
|
||||
## Ancestor locking
|
||||
|
||||
Lock state inherits through the shape hierarchy. If a shape's ancestor is locked, the shape behaves as locked too. Check this with [Editor#isShapeOrAncestorLocked](?):
|
||||
|
||||
```typescript
|
||||
// True if the shape or any of its parents is locked
|
||||
const isProtected = editor.isShapeOrAncestorLocked(shape)
|
||||
```
|
||||
|
||||
Locking a frame or group protects its children from pointer interactions and [Editor#updateShapes](?) without locking each shape individually. Bulk operations such as [Editor#deleteShapes](?) only check each shape's own `isLocked`, so a child of a locked frame can still be deleted programmatically.
|
||||
|
||||
## Bypassing locks programmatically
|
||||
|
||||
Sometimes you need to modify locked shapes from code: migrations, admin tools, or automated operations. Wrap your operations in [Editor#run](?) with `ignoreShapeLock: true`:
|
||||
|
||||
```typescript
|
||||
editor.run(
|
||||
() => {
|
||||
// These operations affect locked shapes
|
||||
editor.updateShape({ id: lockedShapeId, type: 'geo', x: 200 })
|
||||
editor.deleteShapes([lockedShapeId])
|
||||
},
|
||||
{ ignoreShapeLock: true }
|
||||
)
|
||||
```
|
||||
|
||||
This bypasses the lock check for all operations inside the callback.
|
||||
|
||||
Even with `ignoreShapeLock: true`, some behaviors remain unchanged: locked shapes still can't be selected by clicking, pointer events still pass through to the canvas, and `selectAll()` still skips locked shapes. The flag affects [Editor#updateShapes](?), [Editor#deleteShapes](?), [Editor#duplicateShapes](?), [Editor#groupShapes](?), and [Editor#ungroupShapes](?), not the selection and interaction model.
|
||||
|
||||
## Shapes that can be edited while locked
|
||||
|
||||
Some shapes have interactive content that should remain usable even when locked. Embed shapes (YouTube videos, Figma files, interactive maps) are a good example: you might want the embed locked in place but still playable or navigable.
|
||||
|
||||
[ShapeUtils](/sdk-features/shapes#ShapeUtil) can override [ShapeUtil#canEditWhileLocked](?) (default `false`) to allow editing interactions on locked shapes. The built-in embed shape returns `true` unless its embed definition says otherwise:
|
||||
|
||||
```typescript
|
||||
class MyInteractiveShapeUtil extends ShapeUtil<MyShape> {
|
||||
override canEditWhileLocked(shape: MyShape): boolean {
|
||||
return true
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
When this returns `true`, [Editor#canEditShape](?) allows the shape to enter edit mode while locked, so users can interact with its content without moving or resizing it. It doesn't make the shape hit-testable on its own: with default options a double-click on a locked shape still passes through to the canvas. The shape enters edit mode when you call [Editor#setEditingShape](?), or when the user selects it (for example with `selectLockedShapes` enabled) and presses Enter or double-clicks.
|
||||
|
||||
## Locking in the UI
|
||||
|
||||
In the default tldraw UI, users can lock shapes through the context menu or the keyboard shortcut (`Shift+L`). Right-clicking a locked shape opens the context menu with an unlock option, and the main menu has an "Unlock all" action.
|
||||
|
||||
## Related examples
|
||||
|
||||
- [Locked shapes](/examples/editor-api/locked-shapes) - Create locked shapes and modify them with `ignoreShapeLock`.
|
||||
Reference in New Issue
Block a user