Skip to main content

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

type
string
required
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

editor
Editor
required
The editor instance that this shape utility is associated with.
constructor(public editor: Editor)

Configuration

options
object
default:"{}"
Customization options for the shape util. Override this property to provide shape-specific options.
override options = {
  showTextOutline: true,
  autoplay: false,
}

configure()

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
shape
Shape
required
The shape to get geometry for
opts
TLGeometryOpts
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} />
  }
}

Build docs developers (and LLMs) love