Styles

The styles system manages visual properties like color, size, font, fill, and dash patterns across shapes. Style properties differ from regular shape properties in two key ways: they apply consistently across multiple shapes at once, and the editor remembers the last-used value to automatically apply it to newly created shapes. This creates a coherent visual experience where users can set a color once and have it persist across their work.

Styles are defined using StyleProp instances that specify valid values and defaults. The editor tracks "shared styles" across the current selection—computing whether all selected shapes share the same value or have different values—to drive the UI and enable batch updates.

How it works

StyleProp

A StyleProp represents a reusable style property that can be applied across different shape types. Each StyleProp has a unique identifier, a default value, and optional validation. The editor treats StyleProp instances specially, automatically saving their values and applying them to new shapes.

You define a StyleProp using one of two static methods:

import { StyleProp, T } from 'tldraw'

// Define a numeric style property
const LineWidthStyle = StyleProp.define('myApp:lineWidth', {
	defaultValue: 2,
	type: T.number,
})

// Define an enumerated style property
const CapStyle = StyleProp.defineEnum('myApp:cap', {
	defaultValue: 'round',
	values: ['round', 'square', 'butt'],
})

The unique identifier should be namespaced to avoid conflicts with other style properties. Use your app or library name as a prefix.

Shape integration

To use a style property in your shape, include the StyleProp instance in your shape's props definition. The editor recognizes StyleProp instances and handles them specially: it saves their values, applies them to new shapes, and tracks them across selections.

import { DefaultColorStyle, DefaultSizeStyle, RecordProps, T, TLBaseShape } from 'tldraw'

// Define your shape type with the value types for styles
type TLMyShape = TLBaseShape<
	'my-shape',
	{
		w: number
		h: number
		color: string // Will be one of the default color names
		size: string // Will be one of the default size values
	}
>

// Pass StyleProp instances in the props object for validation
const myShapeProps: RecordProps<TLMyShape> = {
	w: T.number,
	h: T.number,
	color: DefaultColorStyle,
	size: DefaultSizeStyle,
}

When you create a shape, provide the actual style values. If you omit a style prop, the editor uses its saved value from previous shapes:

editor.createShape({
	type: 'my-shape',
	props: {
		w: 100,
		h: 100,
		color: 'red',
		size: 'm',
	},
})

Shared styles

The editor computes shared styles across the current selection. Use Editor.getSharedStyles to get a map of each style property to its status: either "shared" (all shapes have the same value) or "mixed" (shapes have different values).

const sharedStyles = editor.getSharedStyles()
const colorStyle = sharedStyles.get(DefaultColorStyle)

if (colorStyle && colorStyle.type === 'shared') {
	console.log('All shapes are', colorStyle.value)
} else if (colorStyle && colorStyle.type === 'mixed') {
	console.log('Shapes have different colors')
}

For convenience, use getAsKnownValue when you only care about the shared case:

const sharedStyles = editor.getSharedStyles()
const color = sharedStyles.getAsKnownValue(DefaultColorStyle)
// Returns the color if all shapes share it, undefined otherwise

The getSharedStyles method examines each selected shape, extracts its style values, and compares them. For groups, it recursively examines the group's children rather than the group itself, since groups don't have visual styles.

When no shapes are selected, getSharedStyles returns the styles for the current tool if that tool creates shapes. This lets the UI show and modify the styles that will be applied to the next shape.

Style persistence

When you set a style on selected shapes, the editor saves that value as the "style for next shapes." The next shape you create will automatically have the same style value.

// Set color for selected shapes - also saves it for next shapes ([`Editor.setStyleForSelectedShapes`](/reference/editor/Editor#setStyleForSelectedShapes))
editor.setStyleForSelectedShapes(DefaultColorStyle, 'blue')

// Create a new shape - it will be blue because 'blue' was saved
editor.createShape({ type: 'geo', props: { w: 100, h: 100 } })

You can set the style for next shapes without affecting the current selection using Editor.setStyleForNextShapes:

editor.setStyleForNextShapes(DefaultColorStyle, 'green')

Default styles

The @tldraw/tlschema package provides a set of default style properties that the built-in shapes use. These styles cover the most common visual properties and integrate with tldraw's theme system for consistent appearance.

Color styles control the visual color of shapes and their labels. Colors reference theme values rather than raw hex codes, allowing shapes to adapt to light and dark modes.

Appearance styles affect how shapes are drawn and filled. These work together to create the hand-drawn aesthetic that defines tldraw's visual style.

Text styles control typography for text shapes and labels. The alignment styles handle both the text itself and how content positions within shape bounds.

Shape-specific styles apply to particular shape types rather than being universal. These are defined alongside their respective shape utilities.

Note that opacity is not a style property. It's a regular property on the base shape (TLBaseShape) that all shapes inherit, and it doesn't persist to new shapes or sync across selections like style properties do.

Using styles

Getting styles

Use getSharedStyles to examine the current selection's styles:

const styles = editor.getSharedStyles()

// Check if all shapes share a color
const color = styles.get(DefaultColorStyle)
if (color?.type === 'shared') {
	console.log('Shared color:', color.value)
}

To get the style value for the next shape to be created, use Editor.getStyleForNextShape:

const nextColor = editor.getStyleForNextShape(DefaultColorStyle)

Setting styles

Use Editor.setStyleForSelectedShapes to change styles on the current selection:

// Change color for all selected shapes
editor.setStyleForSelectedShapes(DefaultColorStyle, 'red')

// Change size
editor.setStyleForSelectedShapes(DefaultSizeStyle, 'l')

This method recursively applies the style to all shapes in the selection, including shapes nested inside groups. It only updates shapes that support the given style property.

Use Editor.setStyleForNextShapes to change the style for subsequently created shapes:

// Next shapes will be blue
editor.setStyleForNextShapes(DefaultColorStyle, 'blue')

Custom styles

You can create custom style properties for your shapes using StyleProp.define or StyleProp.defineEnum.

Defining a custom style

For numeric or complex types, use StyleProp.define:

import { StyleProp, T } from 'tldraw'

export const MyLineWidthStyle = StyleProp.define('myApp:lineWidth', {
	defaultValue: 2,
	type: T.number,
})

For enumerated values, use StyleProp.defineEnum:

export const MyPatternStyle = StyleProp.defineEnum('myApp:pattern', {
	defaultValue: 'solid',
	values: ['solid', 'striped', 'dotted', 'checkered'],
})

Using custom styles in shapes

Include your custom style in your shape's props definition:

import { RecordProps, T, TLBaseShape } from 'tldraw'

type TLMyShape = TLBaseShape<
	'my-shape',
	{
		w: number
		h: number
		lineWidth: number
		pattern: string
	}
>

const myShapeProps: RecordProps<TLMyShape> = {
	w: T.number,
	h: T.number,
	lineWidth: MyLineWidthStyle,
	pattern: MyPatternStyle,
}

The editor automatically recognizes StyleProp instances in your props and handles them during shape creation, selection, and updates.

Prev
Store
Next
Text measurement