Indicators

Indicators are the outlines that appear around shapes when they're selected or hovered. They provide visual feedback about which shapes are active. They also help users understand the bounds of each shape.

How indicators work

When you select a shape in tldraw, an indicator appears as a stroke around the shape's geometry. Indicators are separate from the shape's visual appearance so they can be styled consistently across all shape types.

Each ShapeUtil defines how its indicator should be drawn by implementing the required indicator method:

import { HTMLContainer, Rectangle2d, ShapeUtil } from 'tldraw'

class CardShapeUtil extends ShapeUtil<CardShape> {
	static override type = 'card'

	getDefaultProps(): CardShape['props'] {
		return { w: 100, h: 100 }
	}

	getGeometry(shape: CardShape) {
		return new Rectangle2d({
			width: shape.props.w,
			height: shape.props.h,
			isFilled: true,
		})
	}

	component(shape: CardShape) {
		return <HTMLContainer>Hello</HTMLContainer>
	}

	indicator(shape: CardShape) {
		return <rect width={shape.props.w} height={shape.props.h} />
	}
}

The indicator method returns SVG elements that describe the shape's outline. The editor automatically positions and styles these elements based on selection state.

When indicators appear

By default, indicators appear in these situations:

StateDescription
SelectedThe shape is in the current selection
HoveredThe pointer is over the shape (desktop only, not touch)
HintingThe shape is being referenced during an operation

Indicators are hidden during certain interactions, like when changing styles or during tool operations that would make indicators distracting.

Rendering approaches

tldraw supports two ways to render indicators: SVG-based and canvas-based.

SVG indicators (default)

The default approach uses React to render indicators as SVG elements. This is simpler to implement. It works well for most shapes:

class MyShapeUtil extends ShapeUtil<MyShape> {
	indicator(shape: MyShape) {
		return <rect width={shape.props.w} height={shape.props.h} rx={5} />
	}
}

SVG indicators support any valid SVG elements. The editor wraps them in a styled <g> element that applies the indicator color.

Canvas indicators

Canvas-based rendering improves performance for complex shapes. Override useLegacyIndicator to return false and implement getIndicatorPath:

class MyShapeUtil extends ShapeUtil<MyShape> {
	override useLegacyIndicator() {
		return false
	}

	override getIndicatorPath(shape: MyShape): Path2D | undefined {
		const path = new Path2D()
		path.roundRect(0, 0, shape.props.w, shape.props.h, 5)
		return path
	}

	indicator(shape: MyShape) {
		// Required method. Used only when useLegacyIndicator returns true (the default).
		return <rect width={shape.props.w} height={shape.props.h} rx={5} />
	}
}

Canvas indicators are drawn on a single canvas layer. This is more efficient when many shapes are selected. Most built-in shapes use canvas indicators.

Complex canvas indicators

For indicators that need clipping or multiple paths (like arrows with labels), return an object instead of a plain Path2D:

override getIndicatorPath(shape: MyShape): TLIndicatorPath | undefined {
	const bodyPath = new Path2D()
	bodyPath.moveTo(0, 0)
	bodyPath.lineTo(100, 100)

	const arrowheadPath = new Path2D()
	arrowheadPath.moveTo(90, 95)
	arrowheadPath.lineTo(100, 100)
	arrowheadPath.lineTo(95, 90)

	const labelClipPath = new Path2D()
	labelClipPath.rect(40, 40, 20, 20)

	return {
		path: bodyPath,
		clipPath: labelClipPath,        // Areas to exclude from the main path
		additionalPaths: [arrowheadPath] // Extra paths to stroke
	}
}

Customizing indicator display

Override the indicator components to control when and how indicators appear.

Show all indicators

To show indicators for every shape on the canvas:

import { DefaultShapeIndicators, TLComponents, Tldraw } from 'tldraw'

const components: TLComponents = {
	ShapeIndicators: () => {
		return <DefaultShapeIndicators showAll />
	},
}

export default function App() {
	return <Tldraw components={components} />
}

Hide all indicators

To hide indicators entirely:

const components: TLComponents = {
	ShapeIndicators: () => {
		return <DefaultShapeIndicators hideAll />
	},
}

Custom indicator logic

For complete control over which shapes show indicators, use the OnTheCanvas component:

import { TLComponents, Tldraw, useEditor, useEditorComponents, useValue } from 'tldraw'

const components: TLComponents = {
	OnTheCanvas: () => {
		const editor = useEditor()

		const renderingShapes = useValue(
			'rendering shapes',
			() =>
				editor.getRenderingShapes().filter((info) => {
					// Custom filter logic here
					const shape = editor.getShape(info.id)
					return shape?.type === 'geo' // Only show indicators for geo shapes
				}),
			[editor]
		)

		const { ShapeIndicator } = useEditorComponents()
		if (!ShapeIndicator) return null

		return (
			<div style={{ position: 'absolute', top: 0, left: 0, zIndex: 9999 }}>
				{renderingShapes.map(({ id }) => (
					<ShapeIndicator key={id + '_indicator'} shapeId={id} />
				))}
			</div>
		)
	},
}

Custom indicator styling

To change the color or appearance of indicators:

import { DefaultShapeIndicator, TLComponents, TLShapeIndicatorProps } from 'tldraw'

const CustomShapeIndicator = (props: TLShapeIndicatorProps) => {
	return <DefaultShapeIndicator {...props} color="red" opacity={0.5} />
}

const components: TLComponents = {
	ShapeIndicator: CustomShapeIndicator,
}

The DefaultShapeIndicator component accepts these props:

PropTypeDescription
shapeIdTLShapeIdThe shape to show an indicator for
userIdstringThe collaborator's user ID (for multiplayer)
colorstringStroke color (default: var(--tl-color-selected))
opacitynumberOpacity from 0 to 1
classNamestringAdditional CSS class
hiddenbooleanWhether to hide this indicator

Collaborator indicators

In multiplayer sessions, indicators show other users' selections. These appear with the collaborator's assigned color and slightly reduced opacity. The canvas indicator system handles collaborator indicators automatically.

TopicDescription
ShapesCreating custom shapes with ShapeUtil
User interfaceCustomizing tldraw's UI components
Custom indicators exampleWorking example of indicator customization
Prev
Assets
Next
Collaboration