apps/docs/content/sdk-features/text-shape.mdx
The text shape displays formatted text on the canvas. It has two modes: auto-size, where the shape grows to fit its content, and fixed-width, where text wraps at a set width. The text tool creates text shapes. See TextShapeUtil and TLTextShape.
Text shapes operate in two modes controlled by the autoSize property:
| Mode | Behavior | Use case |
|---|---|---|
| Auto-size | Shape width expands to fit content; text never wraps | Labels, titles, short annotations |
| Fixed-width | Text wraps at the shape's width boundary; height grows as needed | Paragraphs, longer descriptions, callouts |
When autoSize is true (the default), the shape grows to fit its text. The shape widens as you type, and text never wraps unless you press Enter.
import { toRichText } from 'tldraw'
editor.createShape({
type: 'text',
x: 100,
y: 100,
props: {
richText: toRichText('This text will never wrap'),
autoSize: true,
},
})
Which edge stays put as an auto-sized shape grows depends on textAlign: with start the left edge stays fixed and the shape grows to the right, with middle the center stays fixed, and with end the right edge stays fixed.
When autoSize is false, the shape uses a fixed width specified by the w property. Text wraps when it reaches this boundary, and the shape grows vertically to accommodate additional lines.
editor.createShape({
type: 'text',
x: 100,
y: 100,
props: {
richText: toRichText('This text will wrap when it reaches the specified width'),
autoSize: false,
w: 200, // Width in pixels
},
})
To convert an auto-sized shape to fixed-width, drag its left or right edge handle. The shape switches to fixed-width mode and keeps that width as you type.
The textAlign property controls horizontal text alignment:
| Value | Description |
|---|---|
start | Left-aligned (or right-aligned in RTL locales) |
middle | Center-aligned |
end | Right-aligned (or left-aligned in RTL locales) |
editor.createShape({
type: 'text',
x: 100,
y: 100,
props: {
richText: toRichText('Centered text'),
textAlign: 'middle',
autoSize: false,
w: 300,
},
})
Text shapes always align vertically to the middle of the shape's geometry.
The text tool (T key) creates text shapes in two ways. Double-clicking empty canvas with the select tool also creates an auto-sized text shape.
Click anywhere on the canvas to create an auto-sized text shape. The shape appears centered at your click position and immediately enters edit mode. Start typing to add content.
Hold and drag horizontally to create a fixed-width text shape. Once the pointer has been down for at least 150ms and the horizontal drag exceeds about six times the base drag distance (larger for coarse pointers, scaled by zoom), the tool creates a fixed-width shape at that width and hands off to the select tool's resize state. Keep dragging to adjust the width, then release to start editing. Quick, short drags create auto-sized shapes instead.
| Action | Result |
|---|---|
| Click | Create auto-sized text at click position |
| Drag horizontally | Create fixed-width text with dragged width |
| Enter (shape selected) | Switch to the select tool and edit the shape |
| Escape | Exit text tool, return to select tool |
| Cmd/Ctrl+Enter | Confirm text and exit edit mode |
Double-click a text shape or press Enter while it's selected to enter edit mode. The shape displays a cursor and you can type, select, and format text using the rich text editor.
Text shapes use the same rich text system as notes, geo shapes, and arrow labels. You get bold, italic, code, highlighting, and more through keyboard shortcuts or the rich text toolbar.
When editing ends, the shape deletes itself if its text is empty or only trailing whitespace. This keeps invisible shapes off the canvas.
Text shapes support two resize behaviors:
Dragging any handle other than the left or right edges scales the whole shape proportionally. The scale property tracks this multiplier.
// A text shape at 2x scale
editor.createShape({
type: 'text',
props: {
richText: toRichText('Scaled up'),
scale: 2,
},
})
Dragging the left or right edge handles adjusts only the width. For auto-sized shapes, this converts them to fixed-width mode. For already fixed-width shapes, this changes where text wraps.
When editor.user.getIsDynamicResizeMode() is true, new text shapes are created with a scale inversely proportional to the current zoom level. At 200% zoom, new shapes get scale: 0.5; at 50% zoom, they get scale: 2. This keeps text visually consistent regardless of your zoom level when creating it. Use Editor#getResizeScaleFactor to get the same value:
const scale = editor.getResizeScaleFactor()
Arrows can bind to text shapes just like other shapes. When an arrow with no arrowhead binds to a text shape, the shape's geometry is widened by extraArrowHorizontalPadding on each side (10 by default) so the bare line ends short of the glyphs. Arrows with arrowheads bind to the unpadded box. See Configuration to change the padding.
Text shapes display an outline in the canvas background color, which keeps text readable when it overlaps other shapes. The outline uses CSS text-shadow and is on by default. Safari skips it because text-shadow performs poorly there. Turn it off with the showTextOutline option (see Configuration).
| Property | Type | Default | Description |
|---|---|---|---|
richText | TLRichText | empty | Text content with formatting |
color | TLDefaultColorStyle | 'black' | Text color |
size | TLDefaultSizeStyle | 'm' | Font size preset (s, m, l, xl) |
font | TLDefaultFontStyle | 'draw' | Font family (draw, sans, serif, mono) |
textAlign | TLDefaultTextAlignStyle | 'start' | Horizontal alignment (start, middle, end) |
autoSize | boolean | true | When true, shape resizes to fit content |
w | number | 8 | Width when autoSize is false |
scale | number | 1 | Scale factor applied to the shape |
| Option | Type | Default | Description |
|---|---|---|---|
extraArrowHorizontalPadding | number | 10 | Extra horizontal padding when an arrow without an arrowhead binds |
showTextOutline | boolean | true | Display text outline for readability (skipped on Safari for performance) |
import { Tldraw, TextShapeUtil } from 'tldraw'
import 'tldraw/tldraw.css'
const ConfiguredTextUtil = TextShapeUtil.configure({
extraArrowHorizontalPadding: 20,
showTextOutline: false,
})
export default function App() {
return (
<div style={{ position: 'fixed', inset: 0 }}>
<Tldraw shapeUtils={[ConfiguredTextUtil]} />
</div>
)
}
When grid mode is enabled (editor.getInstanceState().isGridMode), the text tool snaps new text shapes to the grid.
To create text with formatting, construct the rich text JSON structure directly:
editor.createShape({
type: 'text',
x: 100,
y: 100,
props: {
richText: {
type: 'doc',
content: [
{
type: 'paragraph',
content: [
{ type: 'text', text: 'Regular and ' },
{ type: 'text', text: 'bold', marks: [{ type: 'bold' }] },
{ type: 'text', text: ' text' },
],
},
],
},
autoSize: true,
},
})
Marks include bold, italic, strike, underline, code, link, and highlight. See Rich text for the formatting system.