Custom React Surfaces
Compose your own panes and layers while React owns the canvas and engine lifecycle.
Navigate linked panes
Pan either pane or zoom with the wheel. Both share a Y window; their X windows stay independent.
const unlink = linkAxes([cameraA, cameraB], 'y')
// Call unlink() when the linked group is removed.Use a custom surface for well tracks, linked panes or your own layer composition. React owns the canvas; your callbacks supply content.
Create the Surface
'use client'
import { Camera, PointsLayer } from '@bs/outkrop-core'
import { OutkropSurface } from '@bs/outkrop-react'
export function CustomPlot() {
return (
<OutkropSurface
style={{ height: 480 }}
setup={(engine) => {
const pane = engine.addPane({
id: 'plot',
rect: { x: 0, y: 0, width: 1, height: 1 },
camera: new Camera({ x: [0, 4], y: [0, 10], flipY: false }),
})
pane.add(new PointsLayer({ x: [1, 2, 3], y: [2, 8, 5], size: 10 }))
}}
layout={({ width, height }, engine) => {
engine.pane('plot')?.setRect({ x: 0, y: 0, width, height })
}}
controls={{ mode: 'pan' }}
fallback={<p>This browser cannot create a WebGL2 view.</p>}
/>
)
}
layout replaces the initial rectangle before the first frame and runs after resizing. Sizes are CSS pixels. Reserve room for axes; give the surface a height.
Know What Runs When
sequenceDiagram
accTitle: React surface lifecycle
accDescr: On client mount the surface creates an engine and calls setup. Layout runs before drawing and on resize. On unmount the surface calls the setup cleanup and disposes the engine.
participant React
participant Surface as OutkropSurface
participant App as Your callbacks
React->>Surface: Mount on client
Surface->>App: setup(engine)
Surface->>App: layout(size, engine)
Note over Surface: Draw when state or data changes
Surface->>App: layout after resize
React->>Surface: Unmount
Surface->>App: Run setup cleanup
Note over Surface: Dispose controls and engine
| Change | Behavior |
|---|---|
| Mount | Create engine, run synchronous setup, then layout |
| Resize | Run layout |
| Callback prop update | Does not rerun setup; update layers through an effect |
New key or pixelRatio |
Rebuild the engine and rerun setup |
| Unmount | Run setup cleanup, dispose engine/controls/canvas |
Setup must not be async. Load metadata first, or start cancellable work inside setup and return cleanup. Your app owns sources/workers and listener subscriptions it creates. StrictMode can replay effects.
Controls and Export
controls={{ mode: 'pan', wheel: 'y' }}pans and restricts wheel zoom to Y. Use'zoom'for box selection orfalseto disable controls.backgrounduses premultiplied RGBA in[0, 1]; transparent is the default.OutkropSurfaceHandleexposesengine,controlsandexportPng(). Handles are not ready before mount; catch export failures.- HTML
childrensit above the canvas and are not part of its PNG.
Imports are SSR-safe; browser resources start after hydration. Next.js callback/hook components need a client boundary. See themes and hooks.