Documentation Index
Fetch the complete documentation index at: https://mintlify.com/tldraw/tldraw/llms.txt
Use this file to discover all available pages before exploring further.
The ShapeUtil class is the foundation for creating custom shapes in tldraw. Every shape type requires a corresponding ShapeUtil class that defines how the shape behaves, renders, and responds to user interactions.
Overview
ShapeUtil is an abstract class that provides the core functionality for shape handling. You extend this class to create custom shapes or use built-in shape utilities like ArrowShapeUtil, GeoShapeUtil, etc.
Type signature
abstract class ShapeUtil<Shape extends TLShape = TLShape>
Static properties
The unique identifier for this shape type. Must match the shape’s type property.static override type = 'myShape' as const
props
RecordProps<TLUnknownShape>
Defines the shape’s properties with validation and style information.static override props = {
color: DefaultColorStyle,
text: T.string,
w: T.number,
h: T.number,
}
migrations
LegacyMigrations | TLPropsMigrations | MigrationSequence
Migrations for updating shape props over time when your shape structure changes.
Constructor
The editor instance that this shape utility is associated with.constructor(public editor: Editor)
Configuration
Customization options for the shape util. Override this property to provide shape-specific options.override options = {
showTextOutline: true,
autoplay: false,
}
Static method to customize shape options without extending the class.
const CustomGeoUtil = GeoShapeUtil.configure({
showTextOutline: false,
})
Abstract methods (required)
getDefaultProps()
Returns the default properties for new shapes of this type.
abstract getDefaultProps(): Shape['props']
Example:
getDefaultProps(): MyShape['props'] {
return {
w: 100,
h: 100,
color: 'black',
text: '',
}
}
getGeometry()
Returns the geometric representation of the shape for hit testing, bounds calculation, and collision detection.
abstract getGeometry(shape: Shape, opts?: TLGeometryOpts): Geometry2d
The shape to get geometry for
Additional options including context information
Example:
getGeometry(shape: MyShape): Geometry2d {
return new Rectangle2d({
width: shape.props.w,
height: shape.props.h,
isFilled: true,
})
}
component()
Returns a React component to render the shape as HTML/SVG.
abstract component(shape: Shape): any
Example:
component(shape: MyShape) {
return (
<SVGContainer>
<rect width={shape.props.w} height={shape.props.h} fill="blue" />
</SVGContainer>
)
}
indicator()
Returns a React component for the selection indicator (shown when shape is selected).
abstract indicator(shape: Shape): any
Example:
indicator(shape: MyShape) {
return <rect width={shape.props.w} height={shape.props.h} />
}
Capability methods
These methods determine what operations are allowed on the shape.
canEdit()
canEdit(shape: Shape, info: TLEditStartInfo): boolean
Returns whether the shape can be double-clicked to enter edit mode. Default: false
canResize()
canResize(shape: Shape): boolean
Returns whether the shape can be resized. Default: true
canBind()
canBind(opts: TLShapeUtilCanBindOpts): boolean
Returns whether the shape can participate in bindings (like arrows). Default: true
canCrop()
canCrop(shape: Shape): boolean
Returns whether the shape can be cropped. Default: false
canSnap()
canSnap(shape: Shape): boolean
Returns whether other shapes can snap to this shape. Default: true
canBeLaidOut()
canBeLaidOut(shape: Shape, info: TLShapeUtilCanBeLaidOutOpts): boolean
Returns whether the shape can participate in layout operations (align, distribute, etc.). Default: true
Visual configuration methods
hideResizeHandles()
hideResizeHandles(shape: Shape): boolean
Returns whether to hide resize handles when selected. Default: false
hideRotateHandle()
hideRotateHandle(shape: Shape): boolean
Returns whether to hide rotation handle when selected. Default: false
hideSelectionBoundsBg()
hideSelectionBoundsBg(shape: Shape): boolean
Returns whether to hide selection bounds background. Default: false
hideSelectionBoundsFg()
hideSelectionBoundsFg(shape: Shape): boolean
Returns whether to hide selection bounds foreground. Default: false
isAspectRatioLocked()
isAspectRatioLocked(shape: Shape): boolean
Returns whether the shape’s aspect ratio is locked during resize. Default: false
Indicator rendering
useLegacyIndicator()
useLegacyIndicator(): boolean
Returns true to use SVG-based indicators, false to use canvas-based indicators. Default: true
getIndicatorPath()
getIndicatorPath(shape: Shape): TLIndicatorPath | undefined
Returns a Path2D for canvas-based indicator rendering (more efficient than SVG).
Example:
getIndicatorPath(shape: MyShape): Path2D {
const path = new Path2D()
path.rect(0, 0, shape.props.w, shape.props.h)
return path
}
Event handlers
onBeforeCreate()
onBeforeCreate?(next: Shape): Shape | void
Called just before a shape is created. Return a modified shape or void.
onBeforeUpdate()
onBeforeUpdate?(prev: Shape, next: Shape): Shape | void
Called just before a shape is updated. Return a modified shape or void.
onResize()
onResize?(
shape: Shape,
info: TLResizeInfo<Shape>
): Omit<TLShapePartial<Shape>, 'id' | 'type'> | undefined | void
Called when a shape is resized. Return updated properties.
onTranslate()
onTranslate?(initial: Shape, current: Shape): TLShapePartial<Shape> | void
Called when a shape is being translated (moved).
onRotate()
onRotate?(initial: Shape, current: Shape): TLShapePartial<Shape> | void
Called when a shape is being rotated.
onDoubleClick()
onDoubleClick?(shape: Shape): TLShapePartial<Shape> | void
Called when a shape is double-clicked.
onEditStart()
onEditStart?(shape: Shape): void
Called when a shape enters edit mode.
onEditEnd()
onEditEnd?(shape: Shape): void
Called when a shape exits edit mode.
Export methods
toSvg()
toSvg?(shape: Shape, ctx: SvgExportContext): ReactElement | null | Promise<ReactElement | null>
Returns an SVG representation of the shape for export.
getCanvasSvgDefs()
getCanvasSvgDefs(): TLShapeUtilCanvasSvgDef[]
Returns SVG definitions (patterns, gradients, etc.) to add to the canvas.
Utility methods
getText()
getText(shape: Shape): string | undefined
Returns the text content of the shape for search and accessibility.
getHandles()
getHandles?(shape: Shape): TLHandle[]
Returns an array of handles for the shape (like arrow endpoints).
getFontFaces()
getFontFaces(shape: Shape): TLFontFace[]
Returns font faces needed to render the shape.
getInterpolatedProps()
getInterpolatedProps?(startShape: Shape, endShape: Shape, progress: number): Shape['props']
Returns interpolated props for animating between two shapes.
Example: Custom shape
import { ShapeUtil, Rectangle2d, HTMLContainer } from 'tldraw'
type MyShape = TLBaseShape<'myShape', {
w: number
h: number
text: string
}>
class MyShapeUtil extends ShapeUtil<MyShape> {
static override type = 'myShape' as const
getDefaultProps(): MyShape['props'] {
return { w: 100, h: 100, text: '' }
}
getGeometry(shape: MyShape) {
return new Rectangle2d({
width: shape.props.w,
height: shape.props.h,
isFilled: true,
})
}
component(shape: MyShape) {
return (
<HTMLContainer>
<div style={{
width: shape.props.w,
height: shape.props.h,
border: '2px solid black',
}}>
{shape.props.text}
</div>
</HTMLContainer>
)
}
indicator(shape: MyShape) {
return <rect width={shape.props.w} height={shape.props.h} />
}
}