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
125 lines
5.8 KiB
Plaintext
125 lines
5.8 KiB
Plaintext
---
|
|
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`.
|