Add API links to all docs pages (#1661)

This PR adds links to the API reference from within the docs pages.
I think we should continue with this as we move forwards with more
content.

We could tweak the threshold of what we link or don't link. I tried to
strike a good balance (maybe linked too much though). We'd want lots of
stuff to be clickable, so that users can dive into the API. But we don't
want the screen to be a splattering of blue, distracting away from more
important stuff.

### Change Type

- [x] `documentation` — Changes to the documentation only[^2]

[^1]: publishes a `patch` release, for devDependencies use `internal`
[^2]: will not publish a new version

### Test Plan

1. Try out some of the newly added links on the docs site.

- [ ] Unit Tests
- [ ] End to end tests

### Release Notes

- Documentation: Added links to API reference in guides.
This commit is contained in:
Lu Wilson 2023-07-06 15:01:11 +01:00 committed by GitHub
parent 80595232d2
commit a316fd2ab4
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
6 changed files with 44 additions and 44 deletions

View file

@ -6,13 +6,13 @@ date: 6/9/2023
order: 9 order: 9
--- ---
In order to use the `<Tldraw/>` component, the app must be able to find certain assets. These are contained in the `embed-icons`, `fonts`, `icons`, and `translations` folders. We offer a few different ways of making these assets available to your app. In order to use the [`<Tldraw/>`](/gen/tldraw/Tldraw) component, the app must be able to find certain assets. These are contained in the `embed-icons`, `fonts`, `icons`, and `translations` folders. We offer a few different ways of making these assets available to your app.
### 1. Using a public CDN ### 1. Using a public CDN
By default we serve these assets from a [public CDN called unpkg](https://unpkg.com/browse/@tldraw/assets@2.0.0-alpha.12/), so everything should work out of the box and is a good way to get started. By default we serve these assets from a [public CDN called unpkg](https://unpkg.com/browse/@tldraw/assets@2.0.0-alpha.12/), so everything should work out of the box and is a good way to get started.
If you would like to customize some of the assets you can pass the customizations to our `<Tldraw />` component. For example, to use a custom icon for the `hand` tool you can do the following: If you would like to customize some of the assets you can pass the customizations to our [`<Tldraw/>`](/gen/tldraw/Tldraw) component. For example, to use a custom icon for the `hand` tool you can do the following:
```javascript ```javascript
const assetUrls = { const assetUrls = {

View file

@ -22,19 +22,19 @@ keywords:
## Introduction ## Introduction
The `Editor` class is the main way of controlling tldraw's editor. You can use it to manage the editor's internal state, make changes to the document, or respond to changes that have occurred. The [`Editor`](/gen/editor/Editor) class is the main way of controlling tldraw's editor. You can use it to manage the editor's internal state, make changes to the document, or respond to changes that have occurred.
By design, the `Editor`'s surface area is [very large](/gen/editor/Editor). Almost everything is available through it. Need to create some shapes? Use `editor.createShapes()`. Need to delete them? Use `editor.deleteShapes()`. Need a sorted array of every shape on the current page? Use `editor.sortedShapesArray`. By design, the [`Editor`](/gen/editor/Editor)'s surface area is very large. Almost everything is available through it. Need to create some shapes? Use [`editor.createShapes()`](/gen/editor/Editor#createShapes). Need to delete them? Use [`editor.deleteShapes()`](/gen/editor/Editor#deleteShapes). Need a sorted array of every shape on the current page? Use [`editor.sortedShapesArray`](/gen/editor/Editor#sortedShapesArray).
This page gives a broad idea of how the `Editor` class is organized and some of the architectural concepts involved. The full reference is available in the [Editor API](/gen/editor/Editor). This page gives a broad idea of how the [`Editor`](/gen/editor/Editor) class is organized and some of the architectural concepts involved. The full reference is available in the [Editor API](/gen/editor/Editor).
## State ## State
The editor holds the raw state of the document in its `store` property. Data is kept here as a table of JSON serializable records. The editor holds the raw state of the document in its [`store`](/gen/editor/Editor#store) property. Data is kept here as a table of JSON serializable records.
For example, the store contains a `page` record for each page in the current document, as well as an `instancePageState` record for each page that stores information about the editor's state for that page, and a single `instanceState` for each editor instance which stores the id of the user's current page. For example, the store contains a `page` record for each page in the current document, as well as an `instancePageState` record for each page that stores information about the editor's state for that page, and a single `instanceState` for each editor instance which stores the id of the user's current page.
The editor also exposes many _computed_ values which are derived from other records in the store. For example, `editor.selectedIds` is a computed property that will return the editor's current selected shape ids for its current page. The editor also exposes many _computed_ values which are derived from other records in the store. For example, [`editor.selectedIds`](/gen/editor/Editor#selectedIds) is a computed property that will return the editor's current selected shape ids for its current page.
You can use these properties directly or you can use them in signals. You can use these properties directly or you can use them in signals.
@ -52,7 +52,7 @@ export const SelectedIdsCount = track(() => {
### Changing the state ### Changing the state
The `Editor` class has many methods for updating its state. For example, you can change the current page's selection using `editor.setSelectedIds`. You can also use other convenience methods, such as `editor.select`, `editor.deselect`, `editor.selectAll`, or `editor.selectNone`. The [`Editor`](/gen/editor/Editor) class has many methods for updating its state. For example, you can change the current page's selection using [`editor.setSelectedIds`](/gen/editor/Editor#setSelectedIds). You can also use other convenience methods, such as [`editor.select`](/gen/editor/Editor#), [`editor.deselect`](/gen/editor/Editor#deselect), [`editor.selectAll`](/gen/editor/Editor#selectAll), or [`editor.selectNone`](/gen/editor/Editor#selectNone).
```ts ```ts
editor.selectNone() editor.selectNone()
@ -60,11 +60,11 @@ editor.select(myShapeId, myOtherShapeId)
editor.selectedIds // [myShapeId, myOtherShapeId] editor.selectedIds // [myShapeId, myOtherShapeId]
``` ```
Each change to the state happens within a transaction. You can batch changes into a single transaction using the `editor.batch` method. It's a good idea to batch wherever possible, as this reduces the overhead for persisting or distributing those changes. Each change to the state happens within a transaction. You can batch changes into a single transaction using the [`editor.batch`](/gen/editor/Editor#batch) method. It's a good idea to batch wherever possible, as this reduces the overhead for persisting or distributing those changes.
### Listening for changes ### Listening for changes
You can subscribe to changes using `editor.store.listen`. Each time a transaction completes, the editor will call the callback with a history entry. This entry contains information about the records that were added, changed, or deleted, as well as whether the change was caused by the user or from a remote change. You can subscribe to changes using [`editor.store.listen`](/gen/store/Store#listen). Each time a transaction completes, the editor will call the callback with a history entry. This entry contains information about the records that were added, changed, or deleted, as well as whether the change was caused by the user or from a remote change.
```ts ```ts
editor.store.listen(entry => { editor.store.listen(entry => {
@ -74,17 +74,17 @@ editor.store.listen(entry => {
### Remote changes ### Remote changes
By default, changes to the editor's store are assumed to have come from the editor itself. You can use `editor.store.mergeRemoteChanges` to make changes in the store that will be emitted via `store.listen` with the `source` property as `'remote'`. By default, changes to the editor's store are assumed to have come from the editor itself. You can use [`editor.store.mergeRemoteChanges`](/gen/store/Store#mergeRemoteChanges) to make changes in the store that will be emitted via [`store.listen`](/gen/store/Store#listen) with the `source` property as `'remote'`.
If you're setting up some kind of multiplayer backend, you would want to send only the `'user'` changes to the server and merge the changes from the server using `editor.store.mergeRemoteChanges`. (We'll have more information about this soon.) If you're setting up some kind of multiplayer backend, you would want to send only the `'user'` changes to the server and merge the changes from the server using [`editor.store.mergeRemoteChanges`](/gen/store/Store#mergeRemoteChanges).
### Undo and redo ### Undo and redo
The history stack in tldraw contains two types of data: "marks" and "commands". Commands have their own `undo` and `redo` methods that describe how the state should change when the command is undone or redone. The history stack in tldraw contains two types of data: "marks" and "commands". Commands have their own `undo` and `redo` methods that describe how the state should change when the command is undone or redone.
You can call `editor.mark(id)` to add a mark to the history stack with the given `id`. You can call [`editor.mark(id)`](/gen/editor/Editor#mark) to add a mark to the history stack with the given `id`.
When you call `editor.undo()`, the editor will undo each command until it finds either a mark or the start of the stack. When you call `editor.redo()`, the editor will redo each command until it finds either a mark or the end of the stack. When you call [`editor.undo()`](/gen/editor/Editor#undo), the editor will undo each command until it finds either a mark or the start of the stack. When you call [`editor.redo()`](/gen/editor/Editor#redo), the editor will redo each command until it finds either a mark or the end of the stack.
```ts ```ts
// A // A
@ -97,7 +97,7 @@ editor.undo() // will return to A
editor.redo() // will return to B editor.redo() // will return to B
``` ```
You can call `editor.bail()` to undo and delete all commands in the stack until the first mark. You can call [`editor.bail()`](/gen/editor/Editor#bail) to undo and delete all commands in the stack until the first mark.
```ts ```ts
// A // A
@ -110,7 +110,7 @@ editor.bail() // will return to A
editor.redo() // will do nothing editor.redo() // will do nothing
``` ```
You can use `editor.bailToMark(id)` to undo and delete all commands and marks until you reach a mark with the given `id`. You can use [`editor.bailToMark(id)`](/gen/editor/Editor#bailToMark) to undo and delete all commands and marks until you reach a mark with the given `id`.
```ts ```ts
// A // A
@ -126,17 +126,17 @@ editor.bailToMark("first") // will to A
## Events and Tools ## Events and Tools
The `Editor` class receives events from the user interface via its `dispatch` method. When the `Editor` receives an event, it is first handled internally to update `editor.inputs` and other state before, and then sent into to the editor's state chart. The [`Editor`](/gen/editor/Editor) class receives events from the user interface via its [`dispatch`](/gen/editor/Editor#dispatch) method. When the [`Editor`](/gen/editor/Editor) receives an event, it is first handled internally to update [`editor.inputs`](/gen/editor/Editor#inputs) and other state before, and then sent into to the editor's state chart.
You shouldn't need to use the `dispatch` method directly, however you may write code in the state chart that responds to these events. You shouldn't need to use the [`dispatch`](/gen/editor/Editor#dispatch) method directly, however you may write code in the state chart that responds to these events.
### State Chart ### State Chart
The `Editor` class has a "state chart", or a tree of `StateNode` instances, that contain the logic for the editor's tools such as the select tool or the draw tool. User interactions such as moving the cursor will produce different changes to the state depending on which nodes are active. The [`Editor`](/gen/editor/Editor) class has a "state chart", or a tree of [`StateNode`](/gen/editor/StateNode) instances, that contain the logic for the editor's tools such as the select tool or the draw tool. User interactions such as moving the cursor will produce different changes to the state depending on which nodes are active.
Each node be active or inactive. Each state node may also have zero or more children. When a state is active, and if the state has children, one (and only one) of its children must also be active. When a state node receives an event from its parent, it has the opportunity to handle the event before passing the event to its active child. The node can handle an event in any way: it can ignore the event, update records in the store, or run a _transition_ that changes which states nodes are active. Each node be active or inactive. Each state node may also have zero or more children. When a state is active, and if the state has children, one (and only one) of its children must also be active. When a state node receives an event from its parent, it has the opportunity to handle the event before passing the event to its active child. The node can handle an event in any way: it can ignore the event, update records in the store, or run a _transition_ that changes which states nodes are active.
When a user interaction is sent to the editor via its `dispatch` method, this event is sent to the editor's root state node (`editor.root`) and passed then down through the chart's active states until either it reaches a leaf node or until one of those nodes produces a transaction. When a user interaction is sent to the editor via its [`dispatch`](/gen/editor/Editor#dispatch) method, this event is sent to the editor's root state node (`editor.root`) and passed then down through the chart's active states until either it reaches a leaf node or until one of those nodes produces a transaction.
<Image title="Events" src="/images/api/events.png" alt="A diagram showing an event being sent to the editor and handled in the state chart." title="The editor passes an event into the state start where it is handled by each active state in order."/> <Image title="Events" src="/images/api/events.png" alt="A diagram showing an event being sent to the editor and handled in the state chart." title="The editor passes an event into the state start where it is handled by each active state in order."/>
@ -144,7 +144,7 @@ When a user interaction is sent to the editor via its `dispatch` method, this ev
You can get the editor's current "path" of active states via `editor.root.path`. In the above example, the value would be `"root.select.idle"`. You can get the editor's current "path" of active states via `editor.root.path`. In the above example, the value would be `"root.select.idle"`.
You can check whether a path is active via `editor.isIn`, or else check whether multiple paths are active via `editor.isInAny`. You can check whether a path is active via [`editor.isIn`](/gen/editor/Editor#isIn), or else check whether multiple paths are active via [`editor.isInAny`](/gen/editor/Editor#isInAny).
```ts ```ts
editor.store.path // 'root.select.idle' editor.store.path // 'root.select.idle'
@ -155,9 +155,9 @@ editor.isIn('root.select.pointing_shape') // false
editor.isInAny('editor.select.idle', 'editor.select.pointing_shape') // true editor.isInAny('editor.select.idle', 'editor.select.pointing_shape') // true
``` ```
Note that the paths you pass to `isIn` or `isInAny` can be the full path or a partial of the start of the path. For example, if the full path is `root.select.idle`, then `isIn` would return true for the paths `root`, `root.select`, or `root.select.idle`. Note that the paths you pass to [`isIn`](/gen/editor/Editor#isIn) or [`isInAny`](/gen/editor/Editor#isInAny) can be the full path or a partial of the start of the path. For example, if the full path is `root.select.idle`, then [`isIn`](/gen/editor/isIn) would return true for the paths `root`, `root.select`, or `root.select.idle`.
> If all you're interested in is the state below `root`, there is a convenience property, `editor.currentToolId`, that can help with the editor's currently selected tool. > If all you're interested in is the state below `root`, there is a convenience property, [`editor.currentToolId`](/gen/editor/Editor#currentToolId), that can help with the editor's currently selected tool.
```tsx ```tsx
import { track, useEditor } from "@tldraw/tldraw" import { track, useEditor } from "@tldraw/tldraw"
@ -177,9 +177,9 @@ export const CreatingBubbleToolUi = track(() => {
## Inputs ## Inputs
The editor's `inputs` object holds information about the user's current input state, including their cursor position (in page space _and_ screen space), which keys are pressed, what their multi-click state is, and whether they are dragging, pointing, pinching, and so on. The editor's [`inputs`](/gen/editor/Editor#inputs) object holds information about the user's current input state, including their cursor position (in page space _and_ screen space), which keys are pressed, what their multi-click state is, and whether they are dragging, pointing, pinching, and so on.
Note that the modifier keys include a short delay after being released in order to prevent certain errors when modeling interactions. For example, when a user releases the "Shift" key, `editor.inputs.shiftKey` will remain `true` for another 100 milliseconds or so. Note that the modifier keys include a short delay after being released in order to prevent certain errors when modeling interactions. For example, when a user releases the "Shift" key, [`editor.inputs.shiftKey`](/gen/editor/Editor#inputs) will remain `true` for another 100 milliseconds or so.
This property is stored as regular data. It is not reactive. This property is stored as regular data. It is not reactive.

View file

@ -30,13 +30,13 @@ type CardShape = TLBaseShape<
> >
``` ```
With the `TLBaseShape` helper, we define the shape's `type` property (`card`) and the shape's `props` property (`{ w: number, h: number }`). The type can be any string but the props must be a regular [JSON-serializable](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/JSON/stringify#description) JavaScript object. With the [`TLBaseShape`](http://localhost:3000/gen/tlschema/TLBaseShape) helper, we define the shape's `type` property (`card`) and the shape's `props` property (`{ w: number, h: number }`). The type can be any string but the props must be a regular [JSON-serializable](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/JSON/stringify#description) JavaScript object.
The `TLBaseShape` helper adds the other default properties of a shape, such as `parentId`, `x`, `y`, and `rotation`. The [`TLBaseShape`](http://localhost:3000/gen/tlschema/TLBaseShape) helper adds the other default properties of a shape, such as `parentId`, `x`, `y`, and `rotation`.
### Shape Util ### Shape Util
While tldraw's shapes themselves are simple JSON objects, we use `ShapeUtil` classes to answer questions about shapes. For example, when the editor needs to know the bounding box of our card shape, it will find a `ShapeUtil` for the `card` type and call that util's `bounds` method, passing in the `CardShape` object as an argument. While tldraw's shapes themselves are simple JSON objects, we use [`ShapeUtil`](/gen/editor/ShapeUtil) classes to answer questions about shapes. For example, when the editor needs to know the bounding box of our card shape, it will find a [`ShapeUtil`](/gen/editor/ShapeUtil) for the `card` type and call that util's `bounds` method, passing in the `CardShape` object as an argument.
Let's create a `ShapeUtil` class for the shape. Let's create a `ShapeUtil` class for the shape.
@ -71,13 +71,13 @@ class CardShapeUtil extends ShapeUtil<CardShape> {
} }
``` ```
This is a minimal `ShapeUtil`. We've given it a static property `type` that matches the type of our shape, we've provided implementations for the abstract methods `getDefaultProps`, `getBounds`, `component`, and `indicator`. This is a minimal [`ShapeUtil`](/gen/editor/ShapeUtil). We've given it a static property `type` that matches the type of our shape, we've provided implementations for the abstract methods [`getDefaultProps`](/gen/editor/ShapeUtil#getDefaultProps), [`getBounds`](/gen/editor/ShapeUtil#getBounds), [`component`](/gen/editor/ShapeUtil#component), and [`indicator`](/gen/editor/ShapeUtil#indicator).
We still have work to do on the `CardShapeUtil` class, but we'll come back to it later. For now, let's put the shape onto the canvas by passing it to the `<Tldraw>` component. We still have work to do on the `CardShapeUtil` class, but we'll come back to it later. For now, let's put the shape onto the canvas by passing it to the [`<Tldraw>`](/gen/tldraw/Tldraw) component.
### Defining the shape ### Defining the shape
Before we pass the shape down, we need to package it up in a way using the `defineShape` function. We can then create an array of our defined shapes and pass them into the `<Tldraw>` component's `shapes` prop. Before we pass the shape down, we need to package it up in a way using the [`defineShape`](/gen/editor/defineShape) function. We can then create an array of our defined shapes and pass them into the [`<Tldraw>`](/gen/tldraw/Tldraw) component's `shapes` prop.
```tsx ```tsx
import { Tldraw } from '@tldraw/tldraw' import { Tldraw } from '@tldraw/tldraw'
@ -95,7 +95,7 @@ export default function () {
} }
``` ```
The `defineShape` function can also be used to include a tool that we can use to create this type of shape. For now, let's create it using the `Editor` API. The [`defineShape`](/gen/editor/defineShape) function can also be used to include a tool that we can use to create this type of shape. For now, let's create it using the [`Editor`](/gen/editor/Editor) API.
```tsx ```tsx
export default function () { export default function () {
@ -113,13 +113,13 @@ Once the page refreshes, we should now have our custom shape on the canvas.
## Using starter shapes ## Using starter shapes
You can use "starter" shape utils like `BaseBoxShapeUtil` to get regular rectangular shape behavior. You can use "starter" shape utils like [`BaseBoxShapeUtil`](/gen/editor/BaseBoxShapeUtil) to get regular rectangular shape behavior.
> todo > todo
## Flags ## Flags
You can use flags like `hideRotateHandle` to hide different parts of the UI when the shape is selected, or else to control different behaviors of the shape. You can use flags like [`hideRotateHandle`](/gen/editor/ShapeUtil#hideRotateHandle) to hide different parts of the UI when the shape is selected, or else to control different behaviors of the shape.
> todo > todo

View file

@ -17,7 +17,7 @@ keywords:
## Events ## Events
The `<Tldraw>` component has a prop, `onUiEvent`, that the user interface will call when certain events occur. The [`<Tldraw>`](/gen/tldraw/Tldraw) component has a prop, `onUiEvent`, that the user interface will call when certain events occur.
```tsx ```tsx
function Example() { function Example() {
@ -33,6 +33,6 @@ function Example() {
The `onUiEvent` callback is called with the name of the event as a string and an object with information about the event's source (e.g. `menu` or `context-menu`) and possibly other data specific to each event, such as the direction in an `align-shapes` event. The `onUiEvent` callback is called with the name of the event as a string and an object with information about the event's source (e.g. `menu` or `context-menu`) and possibly other data specific to each event, such as the direction in an `align-shapes` event.
Note that `onUiEvent` is only called when interacting with the user interface. It is not called when running commands manually against the app, e.g. `editor.alignShapes()` will not call `onUiEvent`. Note that `onUiEvent` is only called when interacting with the user interface. It is not called when running commands manually against the app, e.g. [`editor.alignShapes()`](/gen/editor/Editor#alignShapes) will not call `onUiEvent`.
See the [tldraw repository](https://github.com/tldraw/tldraw/tree/main/apps/examples) for an example of how to customize tldraw's user interface. See the [tldraw repository](https://github.com/tldraw/tldraw/tree/main/apps/examples) for an example of how to customize tldraw's user interface.

View file

@ -23,11 +23,11 @@ export default function () {
} }
``` ```
You can use the `<Tldraw>` React component to build on top of the default tldraw experience. It's easy to use and easy to extend with your own [custom shapes](/docs/shapes), [custom tools](/docs/tools), and [user interface](/docs/user-interface) overrides. You can use the [`<Tldraw>`](/gen/tldraw/Tldraw) React component to build on top of the default tldraw experience. It's easy to use and easy to extend with your own [custom shapes](/docs/shapes), [custom tools](/docs/tools), and [user interface](/docs/user-interface) overrides.
You can also use the [Editor API](/docs/editor) to create, update, and delete shapes, control the camera, or do just about anything else. You can also use the [Editor API](/docs/editor) to create, update, and delete shapes, control the camera, or do just about anything else.
If you want to go even deeper, you can use the `<TldrawEditor>` component as a more minimal engine without the default tldraw shapes or user interface. If you want to go even deeper, you can use the [`<TldrawEditor>`](/gen/editor/TldrawEditor) component as a more minimal engine without the default tldraw shapes or user interface.
Best of all, you can easily plug tldraw into the [collaboration backend](/docs/collaboration) of your choice. Best of all, you can easily plug tldraw into the [collaboration backend](/docs/collaboration) of your choice.

View file

@ -6,7 +6,7 @@ date: 3/22/2023
order: 2 order: 2
--- ---
The `<Tldraw>` component is the easiest way to get started. To use it, create a file like this one: The [`<Tldraw>`](/gen/tldraw/Tldraw) component is the easiest way to get started. To use it, create a file like this one:
```tsx ```tsx
import { Tldraw } from '@tldraw/tldraw' import { Tldraw } from '@tldraw/tldraw'
@ -23,26 +23,26 @@ export default function () {
### CSS ### CSS
In order to use the `<Tldraw>` component, you should also import the `tldraw.css` file from the `@tldraw/tldraw` package. You can alternatively import this file inside of another CSS file using the `@import` syntax. In order to use the [`<Tldraw>`](/gen/tldraw/Tldraw) component, you should also import the `tldraw.css` file from the `@tldraw/tldraw` package. You can alternatively import this file inside of another CSS file using the `@import` syntax.
You can overwrite these files with other CSS or copy the contents into a different file and import that instead. You can overwrite these files with other CSS or copy the contents into a different file and import that instead.
### HTML ### HTML
If you're using the `<Tldraw>` component in a full-screen app, you probably also want to update your `index.html`'s meta viewport element as shown below. If you're using the [`<Tldraw>`](/gen/tldraw/Tldraw) component in a full-screen app, you probably also want to update your `index.html`'s meta viewport element as shown below.
```html ```html
<meta name="viewport" content="width=device-width, initial-scale=1, viewport-fit=cover" /> <meta name="viewport" content="width=device-width, initial-scale=1, viewport-fit=cover" />
``` ```
This may not be critical to `<Tldraw>` performing correctly, however some features (such as safe area positioning) will only work correctly if these viewport options are set. This may not be critical to [`<Tldraw>`](/gen/tldraw/Tldraw) performing correctly, however some features (such as safe area positioning) will only work correctly if these viewport options are set.
## Using in Next.js, Create React App, Vite, etc. ## Using in Next.js, Create React App, Vite, etc.
Check the [examples repository](https://github.com/tldraw/examples) to see examples of tldraw being used in various frameworks. Check the [examples repository](https://github.com/tldraw/examples) to see examples of tldraw being used in various frameworks.
By the way, the `<Tldraw>` component can't be server-rendered. If you're using the component in a server-rendered framework (such as Next.js) then you need to import it dynamically. By the way, the [`<Tldraw>`](/gen/tldraw/Tldraw) component can't be server-rendered. If you're using the component in a server-rendered framework (such as Next.js) then you need to import it dynamically.
## Going deeper ## Going deeper
The `<Tldraw>` component combines two lower-level components: `<TldrawEditor>` and `<TldrawUi>`. If you want to have more granular control, you can use those lower-level components directly. See [this example](https://github.com/tldraw/tldraw/blob/main/apps/examples/src/5-exploded/ExplodedExample.tsx) for reference. The [`<Tldraw>`](/gen/tldraw/Tldraw) component combines two lower-level components: [`<TldrawEditor>`](/gen/editor/TldrawEditor) and [`<TldrawUi>`](gen/ui/TldrawUi). If you want to have more granular control, you can use those lower-level components directly. See [this example](https://github.com/tldraw/tldraw/blob/main/apps/examples/src/5-exploded/ExplodedExample.tsx) for reference.