Skip to main content

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:

vue
<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)

LODConditionWhat renders
fullSelected, or >= 96px on screenFull custom renderer.
simple6–96pxLightweight placeholder that preserves node color and group shape.
hidden< 6pxNothing (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:

ts
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.