Performance
Viewport culling, level of detail, batching, and rendering habits for large boards.
Nuxt Board keeps large boards usable with viewport culling, level of detail, requestAnimationFrame pointer updates, and granular subscriptions. Your code still needs to batch bulk operations and avoid deep-reactive engine wrappers.
Viewport culling
BoardRoot only renders nodes within the viewport plus a configurable margin. Nodes outside are completely unmounted from the DOM:
<BoardRoot :engine="engine" :cull-margin="200" />Increase cull-margin if nodes appear too late during fast panning. Decrease it when DOM count matters more than early rendering.
Level of detail (LOD)
| LOD | Condition | What renders |
|---|---|---|
full | Selected, or >= 96px on screen | Full custom renderer. |
simple | 6–96px | Lightweight placeholder that preserves node color and group shape. |
hidden | < 6px | Nothing (unmounted). |
Selected nodes always render at full detail. Zoomed-out boards keep colored borders and fills so users can still scan structure without mounting every custom renderer.
Batching
Use engine.batch() when creating many nodes at once:
engine.batch(() => {
for (let i = 0; i < 100; i++) {
engine.createNode({
type: 'text',
x: i * 260,
y: 0,
text: `Node ${i}`,
})
}
// One notification instead of 100
})Best practices
Do not wrap the engine in reactive(). The engine manages its own reactivity through subscribables. Use shallowRef if you need to store the engine in Vue state.
Prefer composables such as useBoardCamera() and useBoardNodes() over manual subscriptions. They keep component dependencies narrow.
Keep node updates immutable. Pass a fresh patch to engine.updateNode() instead of mutating objects returned from snapshots.
Batch bulk operations with engine.batch() so subscribers receive one coherent update.
Implementation detail
RAF throttling
BoardRoot throttles pointer projection updates to one internal adapter call per animation frame. Applications do not drive the pointer state machine directly.
Implementation detail
Subscribable granularity
The five subscribables ($camera, $nodes, $selection, $interaction, $snapGuides) fire independently. A camera change does not trigger a $nodes notification. This prevents cascading re-renders in Vue's reactivity system.