toolgarden.xyz
中文
browser tool developmenttldrawReact whiteboardNext.jsfrontend engineering

How to Wrap tldraw into a Production React Whiteboard

A practical tldraw integration for Next.js covering client-only loading, self-hosted assets, persistence, toolbar layout, page scrolling, fullscreen, and cleanup.

ToolGarden tools prioritize browser-local processing, so files and text do not need to be uploaded to a server.

Published July 22, 202612 min readBy ToolGarden

tldraw gives React applications a capable infinite canvas, but dropping <Tldraw /> into a Next.js page is only the first five percent of the work. Production integration starts where the demo ends: loading, assets, persistence, scroll ownership, responsive controls, fullscreen, and deployment licensing.

This guide follows a real browser-local wrapper. The result keeps the document in the user’s browser, self-hosts editor assets, adapts Chinese and English locales, places the main toolbar vertically on wide canvases, and allows the page to scroll normally until the user enters fullscreen.

Define the wrapper boundary before adding features

Treat tldraw as an editor runtime inside your product shell. The shell owns page layout, loading and retry states, privacy copy, fullscreen, analytics boundaries, and navigation. tldraw owns shapes, selection, camera, undo history, pages, and its native export UI.

This boundary prevents a common failure mode: rebuilding editor features that the library already handles while neglecting the product behavior around the canvas.

ConcernOwner
Shapes, tools, selection, pagestldraw
Route, header, help text, error stateApplication shell
Document persistencetldraw persistenceKey or a custom store
Collaboration and authenticationYour backend and sync architecture
Asset hosting and CSPApplication deployment

Load tldraw only in the browser

Canvas editors depend on browser APIs and add substantial JavaScript. Put the wrapper in a client component, import tldraw/tldraw.css in the route layout, and dynamically import the runtime after hydration. Store the component function with setState(() => Component); passing it directly to setState can make React treat it as an updater.

Track whether the effect is still active so a slow import does not update state after unmount. A retry counter can re-run the import after a failed chunk request.

// Client component: load the editor only after hydration.
useEffect(() => {
  let active = true;

  import('tldraw').then(({ Tldraw, DefaultToolbar }) => {
    if (!active) return;
    setEditor(() => Tldraw);
    setToolbar(() => DefaultToolbar);
  });

  return () => { active = false; };
}, []);

return Editor ? (
  <Editor
    assetUrls={assetUrls}
    locale={locale === 'zh' ? 'zh-cn' : 'en'}
    persistenceKey="my-whiteboard"
    cameraOptions={{ wheelBehavior: fullscreen ? 'pan' : 'none' }}
  />
) : <LoadingState />;

Self-host every asset the editor requests

A whiteboard can render correctly in development and then fail under a strict Content Security Policy or offline deployment because fonts, translations, icons, or embed thumbnails still point to a remote host. Use assetUrls to map the editor’s runtime assets to a versioned directory under the same origin.

Keep the mapping in one utility module. When tldraw is upgraded, compare its asset manifest with your copied files and make missing assets a build-time check instead of waiting for a broken production toolbar.

const base = '/tldraw-assets';

export const assetUrls = {
  icons: { 'align-left': `${base}/icons/align-left.svg` },
  fonts: { tldraw_sans: `${base}/fonts/IBMPlexSans-Medium.woff2` },
  translations: { 'zh-cn': `${base}/translations/zh-cn.json` },
  embedIcons: { youtube: `${base}/embed-icons/youtube.png` },
};
  • Copy the icon sprite or required individual icons.
  • Include all font weights and styles exposed by text tools.
  • Provide the locale JSON files that your locale switcher can select.
  • Self-host embed icons if embeds are enabled.

Use persistenceKey for a local-first single-user board

A stable persistenceKey gives the editor a browser-local place to restore the board after refresh. It is ideal for a single-user utility because no scene bytes need to be sent to an application API. Use a product-specific key so two boards on the same origin do not overwrite each other.

Local persistence is not cloud backup. Clearing site data, changing browser profiles, or using private browsing can remove the document. If users expect account sync, collaboration, history, or cross-device access, design those as explicit backend capabilities rather than implying that persistenceKey provides them.

  • Document what is stored locally and how users can clear it.
  • Do not put user IDs or secrets in a client-visible key.
  • Provide export for portable backups.
  • Version or migrate stored documents when library upgrades change schema expectations.

Resolve the canvas-versus-page scroll conflict

An infinite canvas naturally wants wheel events for panning and zooming. A whiteboard embedded halfway down a long tool page should not trap ordinary vertical scrolling. One workable policy is to set wheelBehavior to none while embedded, forward non-modified wheel movement to window.scrollBy, and restore canvas panning in fullscreen.

Preserve Ctrl or Command plus wheel for editor zoom gestures, and do not forward zero-delta events. Test trackpads as well as mouse wheels because horizontal and inertial deltas behave differently.

Customize the toolbar through editor components

The components prop lets the application replace selected editor UI regions without forking tldraw. For a tall work area, wrapping DefaultToolbar with a vertical orientation keeps frequently used tools visible and frees horizontal space. CSS can then position the vertical toolbar within the canvas frame.

Prefer composition over copying internal toolbar implementation. Default components inherit keyboard shortcuts, tool state, and accessibility behavior from the library; a copied toolbar can drift after upgrades.

  • Set minimum and maximum visible item counts for the real canvas height.
  • Keep tool labels or tooltips reachable on touch and keyboard.
  • Use the editor’s semantic components API before overriding internal class names.
  • Limit CSS overrides to positioning and product tokens where possible.

Implement fullscreen with a resilient fallback

Native requestFullscreen gives the best isolation, but it may reject when the browser, embedding context, or permissions policy disallows it. Catch that rejection and switch to a fixed inset-0 container as a CSS fallback. While the fallback is active, lock body scrolling and let Escape exit.

Both native and fallback transitions should dispatch a resize event after layout settles. Canvas editors cache viewport measurements, and a stale measurement can leave controls clipped or the camera centered on the old size.

Plan for errors, licensing, and upgrades

Show a real loading surface while the chunk is downloading, catch import failures, and offer retry. Verify the version’s licensing requirements and configure the client license key according to tldraw’s deployment rules. A browser-delivered key is not a secret; domain restrictions and the vendor’s license terms are the relevant controls.

Pin the package version, test persistence and exports before upgrades, and inspect the Network panel for unexpected asset hosts. If collaboration is added later, treat it as a separate architecture project with authorization, document access control, presence, conflict handling, and storage retention.

Key takeaways

A production tldraw wrapper is mostly integration engineering: client-only loading, deterministic assets, clear persistence semantics, deliberate wheel ownership, component-level customization, and reliable viewport changes. Keep those responsibilities in the product shell and leave drawing primitives to the editor runtime.

Frequently asked questions

Q.Can tldraw be rendered as a Next.js Server Component?

The surrounding route and metadata can be server-rendered, but the editor itself depends on browser APIs and should live in a client component. Dynamic import after hydration also keeps its large runtime out of the server-rendering path.

Q.Does persistenceKey sync a board across devices?

No. It provides browser-local persistence on the current origin and profile. Cross-device sync or multiplayer collaboration needs an authenticated storage and synchronization layer.

Q.Why self-host tldraw assets?

Self-hosting makes asset availability, CSP rules, offline behavior, caching, and version matching controllable by the application. It also prevents a remote asset outage from breaking the editor UI.

Q.Why does the page stop scrolling when the pointer is over the canvas?

The editor consumes wheel events for camera movement. Embedded integrations need an explicit policy: disable or intercept ordinary canvas wheel movement so the document page can scroll, then restore editor panning in fullscreen.

Q.Is tldraw persistence enough for important documents?

It is useful for local drafts, but it is not a managed backup. Important documents should also be exportable, and products promising account storage need a tested backend, retention policy, and recovery workflow.