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:
157
apps/docs/content/sdk-features/indicators.mdx
Normal file
157
apps/docs/content/sdk-features/indicators.mdx
Normal file
@@ -0,0 +1,157 @@
|
||||
---
|
||||
title: Indicators
|
||||
created_at: 01/31/2026
|
||||
updated_at: 01/31/2026
|
||||
keywords:
|
||||
- indicator
|
||||
- selection
|
||||
- hover
|
||||
- outline
|
||||
- ShapeUtil
|
||||
- getIndicatorPath
|
||||
- hinting
|
||||
status: published
|
||||
date: 01/31/2026
|
||||
---
|
||||
|
||||
Indicators are the colored outlines that appear around shapes when they're selected, hovered, or being interacted with. Every shape defines its own indicator through the [ShapeUtil#getIndicatorPath](?) method.
|
||||
|
||||
## How indicators work
|
||||
|
||||
When you hover over or select a shape, tldraw draws an outline that matches the shape's geometry. The indicator is separate from the shape itself: the [ShapeIndicatorOverlayUtil](?) strokes it on a canvas overlay above the shapes, in the theme's selection color.
|
||||
|
||||
Indicators appear in three contexts:
|
||||
|
||||
| Context | When it appears | Stroke weight |
|
||||
| -------- | ------------------------------------------------------------------------------------------------- | ------------- |
|
||||
| Selected | Shape is in the current selection | 1.5px |
|
||||
| Hovered | Pointer is over an unselected shape while the select tool is idle (fine pointers only, not touch) | 1.5px |
|
||||
| Hinting | Shape is a drop target during drag operations | 2.5px |
|
||||
|
||||
For collaborative editing, [CollaboratorShapeIndicatorOverlayUtil](?) also shows which shapes other users have selected, in each collaborator's cursor color. Locked shapes never show indicators.
|
||||
|
||||
## Defining an indicator
|
||||
|
||||
Every [ShapeUtil](?) must implement the `getIndicatorPath` method. This method returns a `Path2D`, a richer [TLIndicatorPath](?) object for indicators that need clipping or additional stroked paths, or `undefined` to draw no indicator:
|
||||
|
||||
```tsx
|
||||
import { ShapeUtil, TLShape, Rectangle2d, T, RecordProps } from 'tldraw'
|
||||
|
||||
declare module 'tldraw' {
|
||||
export interface TLGlobalShapePropsMap {
|
||||
myshape: { w: number; h: number }
|
||||
}
|
||||
}
|
||||
|
||||
type MyShape = TLShape<'myshape'>
|
||||
|
||||
class MyShapeUtil extends ShapeUtil<MyShape> {
|
||||
static override type = 'myshape' as const
|
||||
static override props: RecordProps<MyShape> = {
|
||||
w: T.number,
|
||||
h: T.number,
|
||||
}
|
||||
|
||||
getDefaultProps() {
|
||||
return { w: 100, h: 100 }
|
||||
}
|
||||
|
||||
getGeometry(shape: MyShape) {
|
||||
return new Rectangle2d({
|
||||
width: shape.props.w,
|
||||
height: shape.props.h,
|
||||
isFilled: true,
|
||||
})
|
||||
}
|
||||
|
||||
component(shape: MyShape) {
|
||||
return <div style={{ width: shape.props.w, height: shape.props.h }} />
|
||||
}
|
||||
|
||||
getIndicatorPath(shape: MyShape) {
|
||||
const path = new Path2D()
|
||||
path.rect(0, 0, shape.props.w, shape.props.h)
|
||||
return path
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
The `getIndicatorPath` method receives the shape and returns paths in the shape's local coordinate space. You don't need to set stroke color or width—tldraw applies those automatically based on context.
|
||||
|
||||
### Common indicator patterns
|
||||
|
||||
For circular shapes, use an ellipse path:
|
||||
|
||||
```tsx
|
||||
getIndicatorPath(shape: MyShape) {
|
||||
const { w, h } = shape.props
|
||||
const path = new Path2D()
|
||||
path.ellipse(w / 2, h / 2, w / 2, h / 2, 0, 0, Math.PI * 2)
|
||||
return path
|
||||
}
|
||||
```
|
||||
|
||||
For complex paths, use the shape's geometry:
|
||||
|
||||
```tsx
|
||||
getIndicatorPath(shape: MyShape) {
|
||||
const geometry = this.editor.getShapeGeometry(shape)
|
||||
return new Path2D(geometry.toSimpleSvgPath())
|
||||
}
|
||||
```
|
||||
|
||||
### Indicators with labels
|
||||
|
||||
Shapes with labels may need to clip the indicator where the label appears. Arrow shapes do this to prevent the indicator from overlapping label text. Return an object with `path`, an optional `clipPath`, and optional `additionalPaths`. The clip is applied even-odd before stroking `path`, so an outer rectangle plus the label rectangle punches a hole for the label. `additionalPaths` are stroked afterwards without the clip:
|
||||
|
||||
```tsx
|
||||
override getIndicatorPath(shape: MyShape) {
|
||||
const path = new Path2D()
|
||||
path.moveTo(0, 0)
|
||||
path.lineTo(shape.props.w, shape.props.h)
|
||||
|
||||
// Even-odd: the outer rect keeps everything, the inner rect punches a hole
|
||||
const clipPath = new Path2D()
|
||||
clipPath.rect(-100, -100, shape.props.w + 200, shape.props.h + 200)
|
||||
clipPath.rect(40, 40, 20, 20)
|
||||
|
||||
return { path, clipPath }
|
||||
}
|
||||
```
|
||||
|
||||
## Hinting shapes
|
||||
|
||||
Hinting shapes are shapes that receive a highlighted indicator during drag operations. Use [Editor#setHintingShapes](?) to mark shapes as drop targets:
|
||||
|
||||
```tsx
|
||||
// Highlight a shape as a potential drop target
|
||||
editor.setHintingShapes([targetShapeId])
|
||||
|
||||
// Clear hinting
|
||||
editor.setHintingShapes([])
|
||||
```
|
||||
|
||||
## Customizing indicators
|
||||
|
||||
The stroke widths and paint order live on `ShapeIndicatorOverlayUtil.options` (`lineWidth`, `hintedLineWidth`, `zIndex`). Adjust them with `configure`, or subclass the util and override `getOverlays()` to change which shapes get indicators, then pass it through the `overlayUtils` prop. See [Overlay utils](/sdk-features/overlay-utils) for how overlay utils are registered and replaced.
|
||||
|
||||
```tsx
|
||||
import { ShapeIndicatorOverlayUtil, Tldraw } from 'tldraw'
|
||||
|
||||
const ThickIndicators = ShapeIndicatorOverlayUtil.configure({ lineWidth: 3 })
|
||||
|
||||
function App() {
|
||||
return <Tldraw overlayUtils={[ThickIndicators]} />
|
||||
}
|
||||
```
|
||||
|
||||
## Related articles
|
||||
|
||||
- [Shapes](/sdk-features/shapes) - Learn how to create custom shapes with their own indicators
|
||||
- [Selection](/sdk-features/selection) - Understand how selection state controls indicator visibility
|
||||
- [Overlay utils](/sdk-features/overlay-utils) - The overlay system that draws indicators
|
||||
|
||||
## Related examples
|
||||
|
||||
- [Custom shape](/examples/shapes/tools/custom-shape) - Create a custom shape with an indicator
|
||||
- [Custom indicators](/examples/ui/indicators-logic) - Control which shapes show indicators
|
||||
Reference in New Issue
Block a user