Introduction
Radial layouts for ego networks
An ego graph puts one node — the customer, account, or person the view is about — at the centre. ego-graph lays its neighbourhood around it, with branch folding and sensible treatment for relationships that should not control the hierarchy.
ego-graph is deliberately small:
- The core accepts plain
{ id, width, height }nodes and{ source, target }edges. - It is synchronous, renderer-agnostic, and returns top-left positions in a
Map. - The
react-flowentry point contains only pure adapters; it does not import React.
See it work
Section titled “See it work”This is the canonical executable sample at examples/polar-petal-playground.tsx; the page imports its source rather than maintaining a second copy. Edit the code and the preview reruns.
import { polarPetal } from 'ego-graph'; import './styles.css'; const nodes = [ { id: 'ada', label: 'Ada', width: 120, height: 44 }, { id: 'lin', label: 'Lin', width: 120, height: 44 }, { id: 'maria', label: 'Maria', width: 120, height: 44 }, { id: 'kofi', label: 'Kofi', width: 120, height: 44 }, { id: 'noor', label: 'Noor', width: 120, height: 44 }, ]; const edges = [ { source: 'ada', target: 'lin' }, { source: 'ada', target: 'maria' }, { source: 'ada', target: 'kofi' }, { source: 'maria', target: 'noor' }, ]; const positions = polarPetal({ nodes, edges, root: 'ada' }); const byId = new Map(nodes.map((node) => [node.id, node])); const center = (id: string) => { const position = positions.get(id)!; const node = byId.get(id)!; return { x: position.x + node.width / 2 + 280, y: position.y + node.height / 2 + 190 }; }; export default function App() { return ( <svg viewBox="0 0 560 1000" role="img" aria-label="An ego graph laid out with polarPetal"> <rect width="560" height="1000" fill="#f8fafc" /> {edges.map((edge) => { const from = center(edge.source); const to = center(edge.target); return <line key={`${edge.source}-${edge.target}`} x1={from.x} y1={from.y} x2={to.x} y2={to.y} stroke="#94a3b8" strokeWidth="2" />; })} {nodes.map((node) => { const position = positions.get(node.id)!; return ( <g key={node.id} transform={`translate(${position.x + 280}, ${position.y + 190})`}> <rect width={node.width} height={node.height} rx="10" fill={node.id === 'ada' ? '#3157b7' : 'white'} stroke="#3157b7" /> <text x={node.width / 2} y="28" textAnchor="middle" fill={node.id === 'ada' ? 'white' : '#172554'} fontSize="14">{node.label}</text> </g> ); })} </svg> ); }
Pick a layout
Section titled “Pick a layout”| Layout | Best for | Extra dependency |
|---|---|---|
polarPetal |
Wide, shallow graphs and the smallest bundle | None |
radialDagre |
Deep or uneven branches | @dagrejs/dagre |
sectoredDagre |
Very wide, shallow graphs | @dagrejs/dagre |
Start with polarPetal when a pure radial layout is enough. Use the Dagre variants when ranked subtrees need stricter internal structure.
LAYOUT_NAMES lists these as strings; LayoutName is the union type. Importing either does not load the implementations — see Layouts & options.