Embed shape

The embed shape displays interactive content from external services within an iframe. When you paste a URL from a supported service onto the canvas, tldraw automatically converts it to an embed with the appropriate dimensions and settings.

Creating embeds

Paste a supported URL onto the canvas, or create an embed shape programmatically:

editor.createShape({
	type: 'embed',
	x: 100,
	y: 100,
	props: {
		url: 'https://www.youtube.com/watch?v=dQw4w9WgXcQ',
		w: 560,
		h: 315,
	},
})

The embed system recognizes URLs from supported services and converts them to their embeddable equivalents. A YouTube watch URL becomes an embed URL automatically.

Supported services

ServiceHostnamesResizableAspect ratio locked
tldrawtldraw.com, beta.tldraw.comYesNo
Figmafigma.comYesNo
YouTubeyoutube.com, youtu.beYesYes
Google Mapsgoogle.com/mapsYesNo
Google Calendarcalendar.google.comYesNo
Google Slidesdocs.google.com/presentationYesNo
CodeSandboxcodesandbox.ioYesNo
CodePencodepen.ioYesNo
Scratchscratch.mit.eduNoNo
Val Townval.townYesNo
GitHub Gistgist.github.comYesNo
Replitreplit.comYesNo
Feltfelt.comYesNo
Spotifyopen.spotify.comYesNo
Vimeovimeo.com, player.vimeo.comYesYes
Observableobservablehq.comYesNo
Desmosdesmos.comYesNo

Each service has default dimensions appropriate for its content type. YouTube embeds default to 800×450 (16:9), while Spotify defaults to 720×500.

Interacting with embeds

Embed shapes behave differently from other shapes because they contain live interactive content.

Locked embeds: When an embed shape is locked, you can interact with the content inside—play videos, scroll through code, use the embedded application—without accidentally moving the shape. This is the recommended way to use embeds for viewing content.

Unlocked embeds: When an embed is unlocked, clicking on it selects the shape rather than interacting with the content. To interact with an unlocked embed, double-click to enter editing mode or hold Shift while clicking to interact directly.

Editing mode: In editing mode, pointer events pass through to the iframe. You can scroll, click buttons, and interact with the embedded content. Click outside the shape or press Escape to exit editing mode.

URL transformation

The embed system converts user-facing URLs to embed URLs automatically. When you paste https://www.youtube.com/watch?v=dQw4w9WgXcQ, the embed shape stores the original URL and renders https://www.youtube.com/embed/dQw4w9WgXcQ.

Each embed definition includes two transformation functions:

  • toEmbedUrl: Converts a shareable URL to an embeddable URL
  • fromEmbedUrl: Converts an embed URL back to the original URL

This bidirectional transformation means the shape can display the original URL to users while rendering the embed version internally.

Fallback to bookmarks

When you paste a URL that isn't recognized as embeddable, the embed shape falls back to rendering as a bookmark. The shape's geometry changes to match the bookmark dimensions, and a link card replaces the iframe.

You can check whether a URL is embeddable before creating a shape:

import { getEmbedInfo, DEFAULT_EMBED_DEFINITIONS } from 'tldraw'

const embedInfo = getEmbedInfo(DEFAULT_EMBED_DEFINITIONS, 'https://youtube.com/watch?v=abc123')

if (embedInfo) {
	// URL is embeddable
	console.log(embedInfo.definition.title) // "YouTube"
	console.log(embedInfo.embedUrl) // "https://www.youtube.com/embed/abc123"
} else {
	// URL is not embeddable, will render as bookmark
}

Iframe security

Embeds run in sandboxed iframes with restricted permissions. The default sandbox settings are:

PermissionDefaultDescription
allow-scriptsYesAllow JavaScript execution
allow-same-originYesAllow access to same-origin storage and APIs
allow-formsYesAllow form submission
allow-popupsYesAllow opening new windows (for linking to source)
allow-downloadsNoBlock file downloads
allow-modalsNoBlock modal dialogs like window.prompt()
allow-pointer-lockNoBlock pointer lock API
allow-top-navigationNoBlock navigating away from tldraw
allow-storage-access-by-user-activationNoBlock access to parent storage

Individual embed definitions can override these defaults. YouTube embeds allow allow-presentation for fullscreen video. Tldraw embeds allow allow-top-navigation so users can open rooms in new tabs.

GitHub Gist embeds receive special handling: they use srcDoc instead of src to load the gist script, with an additional security check that restricts gist IDs to hexadecimal characters only. This prevents JSONP callback attacks.

Custom embed definitions

Replace or extend the default embed definitions using EmbedShapeUtil.setEmbedDefinitions():

import { Tldraw, EmbedShapeUtil, DEFAULT_EMBED_DEFINITIONS } from 'tldraw'
import 'tldraw/tldraw.css'

// Add a custom embed definition
const myEmbedDefinitions = [
	...DEFAULT_EMBED_DEFINITIONS,
	{
		type: 'myservice',
		title: 'My Service',
		hostnames: ['myservice.com'],
		width: 600,
		height: 400,
		doesResize: true,
		toEmbedUrl: (url) => {
			const match = url.match(/myservice\.com\/item\/(\w+)/)
			if (match) {
				return `https://myservice.com/embed/${match[1]}`
			}
			return undefined
		},
		fromEmbedUrl: (url) => {
			const match = url.match(/myservice\.com\/embed\/(\w+)/)
			if (match) {
				return `https://myservice.com/item/${match[1]}`
			}
			return undefined
		},
		embedOnPaste: true,
	},
]

// Set before mounting the editor
EmbedShapeUtil.setEmbedDefinitions(myEmbedDefinitions)

export default function App() {
	return (
		<div style={{ position: 'fixed', inset: 0 }}>
			<Tldraw />
		</div>
	)
}

Embed definition properties

PropertyTypeRequiredDescription
typestringYesUnique identifier for this embed type
titlestringYesDisplay name shown in the UI
hostnamesstring[]YesURL hostnames to match (supports glob patterns like *.youtube.com)
widthnumberYesDefault width in pixels
heightnumberYesDefault height in pixels
doesResizebooleanYesWhether the shape can be resized
toEmbedUrl(url: string) => string | undefinedYesConvert shareable URL to embed URL
fromEmbedUrl(url: string) => string | undefinedYesConvert embed URL to shareable URL
minWidthnumberNoMinimum width when resizing
minHeightnumberNoMinimum height when resizing
isAspectRatioLockedbooleanNoLock aspect ratio when resizing
canEditWhileLockedbooleanNoAllow interaction when shape is locked (default: true)
overridePermissionsTLEmbedShapePermissionsNoCustom iframe sandbox permissions
backgroundColorstringNoBackground color for the embed container
overrideOutlineRadiusnumberNoCustom border radius (Spotify uses 12px)
embedOnPastebooleanNoWhen true, URLs are auto-converted to embeds on paste
instructionLinkstringNoHelp URL for services requiring setup (like Google Calendar public links)

Embed-on-paste behavior

By default, pasting a URL from a supported service creates an embed shape. Set embedOnPaste: false in the embed definition to create a bookmark instead.

The tldraw embed definition uses embedOnPaste: false because nested tldraw canvases are blocked when the current page is already inside an iframe (to prevent infinite nesting).

Shape properties

PropertyTypeDescription
urlstringThe original URL (converted to embed URL internally)
wnumberWidth of the embed container
hnumberHeight of the embed container

SVG export

Embed shapes render as blank rectangles in SVG exports. The iframe content can't be captured directly, so exports show a placeholder with the embed's background color and border radius.

Prev
Editor
Next
Environment detection