REACT ARCH / DOCUMENTATION

Custom renderers

Render regular React content inside nodes and custom SVG for edges while keeping documents serializable.

Node type registry

ServiceNode.tsx
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.

PacketEdge.tsx
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.

shape-geometry.tsx
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.

diagram.css
.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.

Minimap.tsx
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.

node-animation.ts
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"));