Custom renderers
Render regular React content inside nodes and custom SVG for edges while keeping documents serializable.
Node type registry
type ServiceData = { runtime: string };
function ServiceNode({ node, selected }: NodeRendererProps<ServiceData>) {
return (
<article className="service" data-selected={selected}>
<small>{node.data?.runtime}</small>
<strong>{node.label}</strong>
</article>
);
}
const nodeTypes = { service: ServiceNode };
<Diagram
nodes={nodes}
edges={edges}
nodeTypes={nodeTypes}
/>;The document stores type: "service" and JSON-safe data. The consuming application maps that type to executable React code.
Custom edge renderer
An edge renderer receives the full routed path as edge.path and a marker-safe renderPath trimmed clear of node borders. Use renderPath for visible marker paths and edge.path for the wider pointer target.
function PacketEdge({ edge, renderPath, selected, markerId, onClick }: EdgeRendererProps) {
return (
<g>
<path
d={renderPath}
stroke="transparent"
strokeWidth={16}
onClick={onClick}
aria-label={edge.ariaLabel ?? edge.id}
/>
<path
d={edge.path}
fill="none"
stroke={selected ? "#fff" : "#8aa0b8"}
markerEnd={`url(#${markerId})`}
/>
<circle r={4} fill="#fff">
<animateMotion dur="1.4s" repeatCount="indefinite" path={renderPath} />
</circle>
</g>
);
}
const edgeTypes = { packet: PacketEdge };Custom boundary geometry
Visuals and geometry are deliberately separate. If a custom node is diamond-shaped, register an intersection function under the same shape name.
const shapes = {
diamond: {
intersect(bounds, toward) {
return intersectDiamond(bounds, toward);
}
}
};
<Diagram shapes={shapes} nodeTypes={nodeTypes} {...document} />;Theme variables
Import the default CSS and override its variables inside your diagram class. Custom renderers can ignore the default node styles entirely.
.my-diagram {
--react-draw-background: #07111f;
--react-draw-node-background: #102238;
--react-draw-node-border: #526e8a;
--react-draw-accent: #f2f2f0;
--react-draw-edge: #91a8bf;
}Use layout without Diagram
useDiagramLayout is useful when your component owns the SVG markup but still needs package layout and routing.
function Minimap({ document }: { document: DiagramDocument }) {
const { result, loading, error } = useDiagramLayout(document);
if (loading) return null;
if (error) return <span>{error.message}</span>;
return (
<svg viewBox={`${result!.bounds.x} ${result!.bounds.y} ${result!.bounds.width} ${result!.bounds.height}`}>
{result!.edges.map((edge) => <path key={edge.id} d={edge.path} />)}
</svg>
);
}Custom animation
animateElement skips Web Animations when the browser reports reduced motion.
const animation = animateElement(
element,
[
{ opacity: 0, transform: "translateY(8px)" },
{ opacity: 1, transform: "translateY(0)" }
],
{ duration: 240, easing: "ease-out", fill: "both" }
);
animation?.finished.then(() => console.log("done"));