Custom tool (sticker)

A minimal custom tool that drops a heart emoji wherever you click.

import { StateNode, Tldraw, toRichText } from 'tldraw'
import 'tldraw/tldraw.css'

// There's a guide at the bottom of this file!

const OFFSET = 12

// [1]
class StickerTool extends StateNode {
	static override id = 'sticker'

	// [a]
	override onEnter() {
		this.editor.setCursor({ type: 'cross', rotation: 0 })
	}

	// [b]
	override onPointerDown() {
		const currentPagePoint = this.editor.inputs.getCurrentPagePoint()
		this.editor.createShape({
			type: 'text',
			x: currentPagePoint.x - OFFSET,
			y: currentPagePoint.y - OFFSET,
			props: { richText: toRichText('❤️') },
		})
	}
}

// [2]
const customTools = [StickerTool]
export default function CustomToolExample() {
	return (
		<div className="tldraw__editor">
			<Tldraw
				// Pass in the array of custom tool classes
				tools={customTools}
				// Set the initial state to the sticker tool
				initialState="sticker"
				// hide the ui
				hideUi
				// Put some helpful text on the canvas
				onMount={(editor) => {
					editor.createShape({
						type: 'text',
						x: 100,
						y: 100,
						props: { richText: toRichText('Click anywhere to add a sticker') },
					})
				}}
			/>
		</div>
	)
}

/*
Introduction:

Tools are nodes in tldraw's state machine. They are responsible for handling user input.
You can create custom tools by extending the `StateNode` class and overriding its methods.
In this example we make a very simple sticker tool that adds a heart emoji to the canvas
when you click.

[1]
We extend the `StateNode` class to create a new tool called `StickerTool`. We set its id
to "sticker". We are not implementing any child states in this example, so we don't need
to set an initial state or define any children states. To see an example of a custom tool
with child states, check out the screenshot tool example.

	[a] The onEnter method is called when the tool is activated. We use it to set the cursor
		to a crosshair.

	[b] The onPointerDown method is called when the user presses on the canvas. We create a
		text shape at the pointer's page-space position, read from `editor.inputs`, offset so
		the emoji is centered under the cursor.

[2]
We pass our custom tool to the Tldraw component using the `tools` prop. We also set the
initial state to our custom tool. We hide the ui and add some helpful text to the canvas
using the `onMount` prop. This is not necessary for the tool to work but it helps make the
example more visually clear.
*/

Tools are nodes in tldraw's state chart and are responsible for handling user input. A custom tool extends StateNode, sets a static id, and overrides event handlers such as onEnter and onPointerDown. Pass the class to <Tldraw> through the tools prop.

The sticker tool here sets a crosshair cursor when it becomes active and creates a text shape at editor.inputs.getCurrentPagePoint() on every pointer down. The example hides the UI and starts with the tool selected via initialState, so just click anywhere on the canvas. For a tool with child states and drag interactions, see the screenshot tool example; for adding a tool to the toolbar, see the custom shape and tool example.

Is this page helpful?
Prev
Custom shape
Next
Custom tool (screenshot)