Deep links

Keep the camera position in the URL so links open at the same view.

import { Tldraw } from 'tldraw'
import 'tldraw/tldraw.css'

export default function DeepLinksExample() {
	return (
		<div className="tldraw__editor">
			<Tldraw persistenceKey="deep-links-example" options={{ deepLinks: true }} />
		</div>
	)
}

Deep links are URLs that point to a specific part of a document: a viewport, a page, or a set of shapes. Try it here: create a shape, then pan or zoom. The d search param in the URL updates as you go, and opening that URL in a new tab returns to the same view.

The simplest way to use deep links is the deepLinks key of the Tldraw component's options prop. It keeps window.location in sync with a search param that tldraw can navigate to, e.g. https://my-app.com/document-name?d=v1234.-234.3.21.

Setting deepLinks: true enables the default behavior:

  1. When the editor initializes, before the initial render, it checks window.location for a search param called d. If found, it parses the value as a deep link and navigates there.
  2. 500 milliseconds after each camera or page change, it updates window.location with the latest d param.

You can customize this behavior by passing an options object instead:

<Tldraw
	options={{
		deepLinks: {
			// change the param name to `page`
			param: 'page',
			// only link to the current page
			getTarget(editor) {
				return { type: 'page', pageId: editor.getCurrentPageId() }
			},
			// log the new search params to the console instead of updating `window.location`
			onChange(url) {
				console.log('the new search params are', url.searchParams)
			},
			// set the debounce interval to 100ms instead of 500ms
			debounceMs: 100,
		},
	}}
/>

For full options see the TLDeepLinkOptions API reference.

The same functionality is exposed as methods and utilities, which gives you more control if, for example, you prefer not to use search params in the URL.

You can create an isolated deep link string using the createDeepLinkString helper which takes a TLDeepLink descriptor object.

createDeepLinkString({ type: 'page', pageId: 'page:abc123' })
// => 'pabc123'
createDeepLinkString({ type: 'shapes', shapeIds: ['shape:foo', 'shape:bar'] })
// => 'sfoo.bar'
createDeepLinkString({
	type: 'viewport',
	pageId: 'page:abc123',
	bounds: {
		x: 0,
		y: 0,
		w: 1024,
		h: 768,
	},
})
// => 'v0.0.1024.768.abc123'

If you do prefer to put this in a URL as a query param, you can use the Editor.createDeepLink method.

editor.createDeepLink({ to: { type: 'page', pageId: 'page:abc123' } })
// => 'https://my-app.com/document-name?d=pabc123'

You can parse a deep link string with parseDeepLinkString which returns a TLDeepLink descriptor object.

You can then call Editor.navigateToDeepLink with this descriptor to navigate to the part of the document described by the deep link.

Editor#navigateToDeepLink also can take a plain URL if the deep link is encoded as a query param.

editor.navigateToDeepLink(parseDeepLinkString('pabc123'))
// or pass in a url
editor.navigateToDeepLink({ url: 'https://my-app.com/document-name?d=pabc123' })
// or call without options to use the current `window.location`
editor.navigateToDeepLink()

You can listen for deep link changes with the Editor.registerDeepLinkListener method, which takes the same options as the deepLinks option.

useEffect(() => {
	const unlisten = editor.registerDeepLinkListener({
		param: 'page',
		getTarget(editor) {
			return { type: 'page', pageId: editor.getCurrentPageId() }
		},
		onChange(url) {
			console.log('the new search params are', url.searchParams)
		},
		debounceMs: 100,
	})
	return () => {
		unlisten()
	}
}, [editor])
Is this page helpful?
Prev
Embed permissions
Next
Frame colors