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:
159
apps/docs/content/sdk-features/readonly.mdx
Normal file
159
apps/docs/content/sdk-features/readonly.mdx
Normal file
@@ -0,0 +1,159 @@
|
||||
---
|
||||
title: Readonly mode
|
||||
created_at: 12/28/2025
|
||||
updated_at: 12/28/2025
|
||||
keywords:
|
||||
- readonly
|
||||
- view only
|
||||
- disable editing
|
||||
- presentation
|
||||
- embed
|
||||
status: published
|
||||
date: 12/28/2025
|
||||
readability: 9
|
||||
voice: 9
|
||||
completeness: 9
|
||||
accuracy: 10
|
||||
notes: '"What readonly mode allows" paragraph is dense - could use bullet points or a table.'
|
||||
---
|
||||
|
||||
Readonly mode turns the editor into a viewer. Users can pan, zoom, and select shapes to inspect them, but they can't create, modify, or delete anything. Use readonly mode when you want to display canvas content without allowing changes: embedding documents in a presentation, sharing a design for feedback, or showing a preview of saved work.
|
||||
|
||||
```tsx
|
||||
import { Tldraw } from 'tldraw'
|
||||
import 'tldraw/tldraw.css'
|
||||
|
||||
export default function ReadOnlyViewer() {
|
||||
return (
|
||||
<div style={{ position: 'fixed', inset: 0 }}>
|
||||
<Tldraw
|
||||
onMount={(editor) => {
|
||||
editor.updateInstanceState({ isReadonly: true })
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
```
|
||||
|
||||
## Enabling readonly mode
|
||||
|
||||
Readonly state lives in the editor's instance state. Toggle it with [Editor#updateInstanceState](?) and read it with [Editor#getIsReadonly](?):
|
||||
|
||||
```typescript
|
||||
// Enable readonly mode
|
||||
editor.updateInstanceState({ isReadonly: true })
|
||||
|
||||
// Disable readonly mode
|
||||
editor.updateInstanceState({ isReadonly: false })
|
||||
|
||||
// Check current state
|
||||
const isReadonly = editor.getIsReadonly()
|
||||
```
|
||||
|
||||
Readonly state is session-scoped and is not saved by `persistenceKey`, so set it on every load, as the example above does in `onMount`. Loading a snapshot with [Editor#loadSnapshot](?) preserves the current in-memory value.
|
||||
|
||||
## What readonly mode blocks
|
||||
|
||||
When readonly is enabled, the editor prevents document mutations. Methods that block include:
|
||||
|
||||
| Category | Blocked methods |
|
||||
| ---------- | ------------------------------------------------------------------------------------------------------------------------------ |
|
||||
| Shapes | `createShape`, `deleteShapes`, `updateShape`, `groupShapes`, `ungroupShapes`, `toggleLock` |
|
||||
| Pages | `createPage`, `deletePage`, `renamePage`, `updatePage`, `moveShapesToPage` |
|
||||
| Assets | `createAssets`, `updateAssets`, `deleteAssets` |
|
||||
| Transforms | `flipShapes`, `packShapes`, `stackShapes`, `alignShapes`, `distributeShapes`, `stretchShapes`, `rotateShapesBy`, `resizeShape` |
|
||||
| Styles | `setStyleForSelectedShapes`, `setOpacityForSelectedShapes` |
|
||||
| Content | `putExternalContent`, `replaceExternalContent`, `putContentOntoCurrentPage`, plus the `cut` and `paste` UI actions |
|
||||
|
||||
Methods that only touch instance state, such as `setStyleForNextShapes` and `setOpacityForNextShapes`, still work.
|
||||
|
||||
The toolbar automatically hides editing tools. Only the select tool, hand tool, and laser pointer remain visible. UI actions like undo and redo are also disabled in readonly mode.
|
||||
|
||||
## What readonly mode allows
|
||||
|
||||
Navigation and viewing operations work normally. You can pan, zoom, and use camera methods like `zoomIn`, `zoomOut`, `zoomToFit`, and `zoomToSelection`. Selection works too: clicking shapes, brush selection, `selectAll`, and `selectNone`.
|
||||
|
||||
You can also hover over shapes to inspect them and switch between pages. Exporting with the `exportAs` or `copyAs` helper functions works because exporting doesn't modify the document.
|
||||
|
||||
## Using the useReadonly hook
|
||||
|
||||
In React components, the [useReadonly](?) hook provides reactive access to the readonly state (and returns `false` outside an editor context). Import it from `tldraw`:
|
||||
|
||||
```tsx
|
||||
import { useReadonly } from 'tldraw'
|
||||
|
||||
function ReadonlyIndicator() {
|
||||
const isReadonly = useReadonly()
|
||||
|
||||
if (!isReadonly) return null
|
||||
|
||||
return <div className="readonly-badge">View only</div>
|
||||
}
|
||||
```
|
||||
|
||||
The component re-renders automatically when readonly state changes.
|
||||
|
||||
## Actions in readonly mode
|
||||
|
||||
[TLUiActionItem](?) has a `readonlyOk` property that determines whether the UI offers the action in readonly mode. When an action has `readonlyOk: false` (the default), menus hide it and its keyboard shortcut is ignored in readonly mode.
|
||||
|
||||
Built-in actions that work in readonly mode include zoom controls (`zoom-in`, `zoom-out`, `zoom-to-fit`, `zoom-to-selection`), selection actions (`select-all`, `select-none`, `copy`), export actions (`export-as-svg`, `export-as-png`, `print`), navigation (`back-to-content`, `change-page-prev`, `change-page-next`), and preference toggles like `toggle-dark-mode`.
|
||||
|
||||
When defining custom actions, set `readonlyOk: true` if the action should work in readonly mode:
|
||||
|
||||
```typescript
|
||||
import { TLUiOverrides } from 'tldraw'
|
||||
|
||||
const overrides: TLUiOverrides = {
|
||||
actions(editor, actions, helpers) {
|
||||
actions['share-link'] = {
|
||||
id: 'share-link',
|
||||
label: 'action.share-link',
|
||||
readonlyOk: true,
|
||||
onSelect() {
|
||||
// This works in readonly mode
|
||||
navigator.clipboard.writeText(window.location.href)
|
||||
},
|
||||
}
|
||||
return actions
|
||||
},
|
||||
}
|
||||
```
|
||||
|
||||
## Shapes that remain interactive
|
||||
|
||||
Some shapes have interactive content that works even when the document is readonly. Embed shapes (YouTube videos, Figma files, interactive maps) are the built-in case: the embed itself is locked in place, but users can still play videos or interact with the embedded content.
|
||||
|
||||
ShapeUtils can override [ShapeUtil#canEditInReadonly](?) to allow editing interactions on their shapes:
|
||||
|
||||
```typescript
|
||||
class InteractiveWidgetUtil extends ShapeUtil<InteractiveWidget> {
|
||||
override canEditInReadonly(shape: InteractiveWidget): boolean {
|
||||
return true
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
When this returns `true`, double-clicking the shape enters edit mode even in readonly mode. The shape's interactive content becomes usable while the shape itself stays fixed.
|
||||
|
||||
## Forcing operations in readonly mode
|
||||
|
||||
For programmatic use cases like migrations or admin tools, you can bypass the readonly check by passing `force: true` to [Editor#putExternalContent](?) and [Editor#replaceExternalContent](?):
|
||||
|
||||
```typescript
|
||||
// This works even in readonly mode
|
||||
editor.putExternalContent({ type: 'files', files: myFiles, point: { x: 0, y: 0 } }, { force: true })
|
||||
```
|
||||
|
||||
Only these two methods support this option. Use it sparingly since readonly mode exists to prevent unintended changes.
|
||||
|
||||
## Collaboration and readonly mode
|
||||
|
||||
When using tldraw's sync packages for collaboration, readonly mode integrates with room permissions. A user with read-only access to a shared document sees the editor in readonly mode automatically. The sync layer communicates the mode through a reactive signal, and the editor updates `isReadonly` in response.
|
||||
|
||||
Changes from other collaborators still appear (the document updates in real time) but the readonly user can't contribute changes themselves.
|
||||
|
||||
## Related examples
|
||||
|
||||
- [Read-only](/examples/configuration/readonly): set up a readonly editor that disables all editing functionality.
|
||||
Reference in New Issue
Block a user