Zoom, Pan and Export
ApexTree supports interactive zoom and pan, a built-in toolbar, and SVG export. All of it can be driven programmatically through the graph returned by render().
Interactive zoom and pan
enableZoomPan (on by default) enables Ctrl/⌘ + mouse-wheel (or trackpad pinch) zoom and drag-to-pan. A plain wheel without a modifier still scrolls the page, so the chart doesn't hijack scrolling inside a longer page:
const tree = new ApexTree(document.getElementById('chart'), {
enableZoomPan: true, // default
})
tree.render(data)
Set enableZoomPan: false to lock the viewport — useful when embedding the tree in a scrollable page where you don't want zoom interactions at all.
Built-in toolbar
enableToolbar shows a zoom/pan/export toolbar inside the container. It also hosts the search input when enableSearch is on:
const tree = new ApexTree(el, {
enableToolbar: true,
enableSearch: true,
})
tree.render(data)
Programmatic zoom
The graph returned by render() exposes zoom and fit methods:
| Method | Description |
|---|---|
zoom(zoomFactor) | Zoom by a numeric factor (e.g. 1.2 in, 0.8 out) |
fitScreen() | Fit the entire tree within the viewport |
centerOnNode(nodeId) | Center the camera on a node at the current zoom |
const graph = tree.render(data)
document.getElementById('zoom-in').onclick = () => graph.zoom(1.2)
document.getElementById('zoom-out').onclick = () => graph.zoom(0.8)
document.getElementById('fit').onclick = () => graph.fitScreen()
document.getElementById('focus-root').onclick = () => graph.centerOnNode(graph.getRootNodeId())
centerOnNode animates when enableAnimation is on and snaps otherwise.
zoom() re-bases on the live viewBox before it steps, so a step is always multiplicative on the real on-screen scale: zoom(1.2) is 20% in, whatever fit, focus or level-of-detail pan happened beforehand. Zoom-out bottoms out once the whole current layout fits with generous slack.
Wheel zoom and drag pan both release the camera spring first, so neither gesture is swallowed while a fit or focus animation is still in flight.
Capping the auto-fit
enableExpandCollapseZoom re-fits the camera after each expand and collapse. maxZoomNodeSpan caps how far in that fit can go:
| Option | Type | Default | Description |
|---|---|---|---|
maxZoomNodeSpan | number | 8 | Minimum number of node widths and heights the fitted view spans |
A collapsed view of two remaining nodes would otherwise balloon them to fill the canvas. The fitted view now always spans at least this many node widths, centred on the visible nodes; larger means less zoom-in.
new ApexTree(el, { maxZoomNodeSpan: 0 }) // fit tightly, pre-2.0 behavior
The cap never zooms out past the full tree, and never shrinks the chart below 1:1, so a chart narrower than maxZoomNodeSpan node widths (a small card, a dashboard tile) keeps its tight fit instead of being scaled down on every collapse. Focus mode keeps its own tighter framing and is unaffected. See Focus Mode and the Active Path.
Viewport padding
paddingX and paddingY control the breathing room between the outermost nodes and the viewBox edge, which also affects how fitScreen() frames the tree:
const tree = new ApexTree(el, {
paddingX: 120,
paddingY: 120,
})
Internal viewport
viewPortWidth and viewPortHeight set the internal SVG coordinate space (defaults 800 × 600). Adjust when a very large tree needs a different internal resolution before scaling to the canvas.
Export to SVG
exportToSvg() downloads the current tree as an SVG file. The built-in toolbar includes an export button when enableToolbar is on; call the method directly to wire your own trigger:
const tree = new ApexTree(el, { enableToolbar: false })
const graph = tree.render(data)
document.getElementById('export').addEventListener('click', () => {
graph.exportToSvg()
})
Complete example
const tree = new ApexTree(document.getElementById('chart'), {
enableZoomPan: true,
enableToolbar: true,
paddingX: 100,
paddingY: 100,
})
const graph = tree.render(data)
const controls = {
'zoom-in': () => graph.zoom(1.2),
'zoom-out': () => graph.zoom(0.8),
'fit': () => graph.fitScreen(),
'export': () => graph.exportToSvg(),
}
Object.entries(controls).forEach(([id, fn]) => {
document.getElementById(id).addEventListener('click', fn)
})
React example
import { useRef } from 'react'
import { ApexTreeChart } from 'react-apextree'
import type { ApexTreeRef } from 'react-apextree'
export default function ZoomableTree() {
const ref = useRef<ApexTreeRef>(null)
return (
<div>
<div style={{ display: 'flex', gap: 8, marginBottom: 12 }}>
<button onClick={() => ref.current?.getGraph()?.zoom(1.2)}>Zoom in</button>
<button onClick={() => ref.current?.getGraph()?.zoom(0.8)}>Zoom out</button>
<button onClick={() => ref.current?.fitScreen()}>Fit</button>
<button onClick={() => ref.current?.getGraph()?.exportToSvg()}>Export</button>
</div>
<ApexTreeChart
ref={ref}
data={data}
options={{ enableZoomPan: true }}
/>
</div>
)
}
fitScreen() is available directly on the ref handle; zoom() and exportToSvg() are reached via getGraph().