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:
137
apps/docs/content/sdk-features/focus.mdx
Normal file
137
apps/docs/content/sdk-features/focus.mdx
Normal file
@@ -0,0 +1,137 @@
|
||||
---
|
||||
title: Focus
|
||||
created_at: 12/20/2025
|
||||
updated_at: 12/29/2025
|
||||
keywords:
|
||||
- focus
|
||||
- blur
|
||||
- keyboard
|
||||
- FocusManager
|
||||
- container
|
||||
- input
|
||||
status: published
|
||||
date: 12/20/2025
|
||||
readability: 9
|
||||
voice: 9
|
||||
completeness: 9
|
||||
accuracy: 10
|
||||
notes: ''
|
||||
---
|
||||
|
||||
Focus determines whether the editor receives keyboard shortcuts and scroll wheel gestures. When focused, these inputs go to the editor. When unfocused, they pass through to the rest of your page.
|
||||
|
||||
```tsx
|
||||
import { Tldraw } from 'tldraw'
|
||||
import 'tldraw/tldraw.css'
|
||||
|
||||
export default function App() {
|
||||
return (
|
||||
<div style={{ position: 'fixed', inset: 0 }}>
|
||||
<Tldraw
|
||||
autoFocus={false}
|
||||
onMount={(editor) => {
|
||||
// Focus later, for example when the user clicks the canvas
|
||||
const container = editor.getContainer()
|
||||
const focus = () => editor.focus()
|
||||
container.addEventListener('pointerdown', focus)
|
||||
return () => container.removeEventListener('pointerdown', focus)
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
```
|
||||
|
||||
## Controlling focus
|
||||
|
||||
Use [Editor#focus](?) and [Editor#blur](?) to programmatically control focus:
|
||||
|
||||
```typescript
|
||||
editor.focus()
|
||||
editor.blur()
|
||||
editor.getIsFocused() // true or false
|
||||
```
|
||||
|
||||
Both methods accept options to control whether the container element should also receive or lose DOM focus:
|
||||
|
||||
```typescript
|
||||
editor.focus({ focusContainer: false })
|
||||
editor.blur({ blurContainer: false })
|
||||
```
|
||||
|
||||
Both are no-ops when the editor is already in that state. If the editor is focused but the container has lost DOM focus, call `editor.getContainer().focus()` directly.
|
||||
|
||||
### Focus/blur options
|
||||
|
||||
| Method | Option | Default | Description |
|
||||
| ------- | ---------------- | ------- | ------------------------------------------------------- |
|
||||
| `focus` | `focusContainer` | `true` | Whether to also call `focus()` on the container element |
|
||||
| `blur` | `blurContainer` | `true` | Whether to also call `blur()` on the container element |
|
||||
|
||||
## Why focus is separate from DOM focus
|
||||
|
||||
The editor tracks focus separately from the browser's DOM focus. The browser's focus model isn't reliable enough for an editor like tldraw: iframes aren't considered descendants of their parent elements, and many menus are portalled into other parts of the document tree.
|
||||
|
||||
The editor maintains its own `isFocused` state in the instance record, readable with the reactive [Editor#getIsFocused](?). This lets you distinguish between "editor focus" (whether the editor responds to keyboard shortcuts) and "element focus" (which HTML element is active in the DOM).
|
||||
|
||||
When `isFocused` changes, the editor adds or removes the `tl-container__focused` CSS class on the container. Use this class for styling instead of `:focus` or `:focus-within` pseudo-selectors, which can't reliably detect editor focus.
|
||||
|
||||
## Auto-focus
|
||||
|
||||
The `autoFocus` prop controls whether the editor focuses when it mounts. It defaults to `true`. Set it to `false` when embedding the editor in a page where you don't want it to capture keyboard input immediately.
|
||||
|
||||
```tsx
|
||||
<Tldraw autoFocus={false} />
|
||||
```
|
||||
|
||||
## Focus ring visibility
|
||||
|
||||
The editor manages focus ring visibility for accessibility. Focus rings appear around focused elements during keyboard navigation but are hidden during mouse interactions.
|
||||
|
||||
When you press Tab, ArrowUp, or ArrowDown, the editor removes the `tl-container__no-focus-ring` class to show focus rings. Mouse clicks add the class back to hide them. Focus rings stay hidden while editing a shape, and while the container itself is focused with shapes selected (arrow keys nudge shapes then).
|
||||
|
||||
## Completing interactions on blur
|
||||
|
||||
When you call `editor.blur()`, it calls `editor.complete()` to finish any ongoing interaction like a drag or draw operation. This prevents the editor from being left mid-interaction when focus is lost.
|
||||
|
||||
## Multiple editors
|
||||
|
||||
When you have multiple editors on the same page, you'll need to manage focus yourself. The browser's DOM focus alone isn't enough to reliably switch which editor receives keyboard input.
|
||||
|
||||
```tsx
|
||||
import { useState } from 'react'
|
||||
import { Editor, Tldraw } from 'tldraw'
|
||||
import 'tldraw/tldraw.css'
|
||||
|
||||
function MultipleEditors() {
|
||||
const [editors, setEditors] = useState<Editor[]>([])
|
||||
|
||||
function handleEditorFocus(focusedEditor: Editor) {
|
||||
for (const editor of editors) {
|
||||
if (editor === focusedEditor) {
|
||||
editor.focus()
|
||||
} else {
|
||||
editor.blur()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
<div onFocus={() => editors[0] && handleEditorFocus(editors[0])}>
|
||||
<Tldraw autoFocus={false} onMount={(e) => setEditors((prev) => [...prev, e])} />
|
||||
</div>
|
||||
<div onFocus={() => editors[1] && handleEditorFocus(editors[1])}>
|
||||
<Tldraw autoFocus={false} onMount={(e) => setEditors((prev) => [...prev, e])} />
|
||||
</div>
|
||||
</>
|
||||
)
|
||||
}
|
||||
```
|
||||
|
||||
This switches focus when a wrapper receives DOM focus. To also blur when the user clicks elsewhere on the page, listen for pointer down on the page as the [multiple editors example](/examples/layout/multiple) does.
|
||||
|
||||
## Related examples
|
||||
|
||||
- [Editor focus](/examples/editor-api/editor-focus) - Control editor focus with focus and blur methods.
|
||||
- [Multiple editors](/examples/layout/multiple) - Manage focus between multiple tldraw instances.
|
||||
Reference in New Issue
Block a user