Server Components and Next.js
Every component pans, zooms, hovers and draws to a canvas, so they are all client components.
react-bio-viz is marked "use client", so you can render them straight from a React Server
Component, such as a page in the Next.js App Router. They are server-rendered to HTML like any
client component, then become interactive in the browser.
// app/tree/page.tsx — a Server Componentimport { PhyloTree } from "react-bio-viz";import { parseNewick, midpointRoot } from "react-bio-viz/utils";
export default async function Page() { const tree = parseNewick(await fetchNewick()); const { rerootedAt, rerootPosition } = midpointRoot(tree); return ( <PhyloTree tree={tree} width={800} height={600} defaultSelection={{ rerootedAt, rerootPosition, collapsed: [], order: {} }} /> );}Helpers on the server: react-bio-viz/utils
Section titled “Helpers on the server: react-bio-viz/utils”Everything exported from a "use client" module becomes a client reference inside a Server
Component. You can render it or pass it along, but you can’t call it. So parseNewick imported
from react-bio-viz can’t run in a Server Component. The pure helpers are therefore also exported
from react-bio-viz/utils, which has no "use client" and doesn’t import React, so they run on
the server:
| Trees | parseNewick, toNewick, midpointRoot, ladderizeOrder, rotateOrder, orderForLeafNames, collapseBySupport, applyTreeSelection, leafOrder, rerootAbove, REROOT_ID |
| Alignments | alignmentSequences, analyseColumns, computeColumnStats, computeConsensus, computeConservationScores, COLOR_STYLES, COLOR_STYLE_GROUPS |
| Other data | blastHitsFromResult, distanceColor, resolveRowOrder, moveItem |
| Viewports | fitToExtent, clampToExtent, panBy, zoomAt, zoomBy |
Use them to prepare a component’s data or its initial state (defaultSelection,
defaultViewport, defaultRowOrder, …) where the data is loaded. In client code, importing them
from either entry gives the same functions.
What can cross the boundary
Section titled “What can cross the boundary”Props passed from a Server Component to a client component must be serializable. Data and default*
props are plain objects and arrays, so they can be passed directly. Functions can’t be passed, so
these props need a client component of your own:
- change callbacks (
onViewportChange,onSelectionChange, …) and events (onNodeClick, …), - function props such as
colorFunction,leafTextComponentortrackRenderers, - external stores (
selectionStore, …).
Move the part of the page that uses them into a component marked "use client", and give it the
server-loaded data as props:
"use client";import { useState } from "react";import { PhyloTree, type Tree, type TreeSelection } from "react-bio-viz";
export function TreeView({ tree }: { tree: Tree }) { const [selection, setSelection] = useState<TreeSelection>(); return <PhyloTree tree={tree} width={800} height={600} selection={selection} onSelectionChange={setSelection} />;}import { parseNewick } from "react-bio-viz/utils";import { TreeView } from "./TreeView";
export default async function Page() { return <TreeView tree={parseNewick(await fetchNewick())} />;}