ts-graphviz
    Preparing search index...

    Module @ts-graphviz/react - v0.11.2

    Main CodeQL License: MIT All Contributors

    OpenSSF Best Practices OpenSSF Scorecard Tidelift

    npm version node version deno version npm

    @ts-graphviz/react

    React Components and Renderer for ts-graphviz 🔗

    GitHub npm Reference Ask DeepWiki

    Sponsor OpenCollective

    format: Biome test: Vitest build: Vite


    It is part of the ts-graphviz library, which is split into modular packages to improve maintainability, flexibility, and ease of use.

    The module can then be installed using npm:

    NPM

    # yarn
    $ yarn add @ts-graphviz/react react@^19
    # or npm
    $ npm install -S @ts-graphviz/react react@^19
    # or pnpm
    $ pnpm add @ts-graphviz/react react@^19

    Important: Install React 19+ as a peerDependency. React 18 and earlier versions are not supported. Note that react-dom is no longer required as this package now uses a custom HTML rendering implementation.

    The package provides React components that map directly to Graphviz DOT language constructs:

    • <Digraph> - Creates a directed graph (arrows point from source to target)
    • <Graph> - Creates an undirected graph (no arrow direction)
    • <Subgraph> - Creates a subgraph cluster for grouping related nodes
    • <Node> - Creates a node/vertex with customizable attributes
    • <Edge> - Creates an edge/connection between nodes with optional styling

    All components accept Graphviz DOT attributes as props with full TypeScript support:

    // Node with styling attributes
    <Node
    id="server1"
    label="Web Server"
    shape="box"
    color="blue"
    style="filled"
    fillcolor="lightblue"
    />

    // Edge with custom styling
    <Edge
    targets={["server1", "database1"]}
    label="API calls"
    color="red"
    style="dashed"
    weight={2}
    />

    // Digraph with global attributes
    <Digraph
    rankdir="LR"
    bgcolor="white"
    node={{ shape: "ellipse", color: "gray" }}
    edge={{ color: "black", arrowhead: "vee" }}
    >
    {/* nodes and edges */}
    </Digraph>

    Components can be freely nested to create complex graph structures:

    // Define reusable components
    const ServerNode = ({ id, name, type }) => (
    <Node
    id={id}
    label={
    <dot:table border="1" cellborder="0" cellspacing="0">
    <dot:tr>
    <dot:td bgcolor="lightgray">
    <dot:b>{name}</dot:b>
    </dot:td>
    </dot:tr>
    <dot:tr>
    <dot:td>{type}</dot:td>
    </dot:tr>
    </dot:table>
    }
    shape="record"
    />
    );

    const ServiceCluster = ({ id, label, children }) => (
    <Subgraph id={id} label={label} style="filled" fillcolor="lightblue">
    {children}
    </Subgraph>
    );

    // Compose the architecture
    const SystemArchitecture = () => (
    <Digraph rankdir="TB">
    <ServiceCluster id="cluster_frontend" label="Frontend Layer">
    <ServerNode id="web1" name="Web Server" type="Nginx" />
    <ServerNode id="app1" name="App Server" type="React" />
    </ServiceCluster>

    <ServiceCluster id="cluster_backend" label="Backend Layer">
    <ServerNode id="api1" name="API Gateway" type="REST" />
    <ServerNode id="auth1" name="Auth Service" type="OAuth" />
    </ServiceCluster>

    <ServiceCluster id="cluster_data" label="Data Layer">
    <ServerNode id="db1" name="Database" type="PostgreSQL" />
    <ServerNode id="cache1" name="Cache" type="Redis" />
    </ServiceCluster>

    <Edge targets={["web1", "app1"]} label="serves" />
    <Edge targets={["app1", "api1"]} label="API calls" />
    <Edge targets={["api1", "auth1"]} label="validates" />
    <Edge targets={["api1", "db1"]} label="queries" />
    <Edge targets={["api1", "cache1"]} label="caches" />
    </Digraph>
    );

    Create rich, formatted labels using Graphviz's HTML-like label syntax with natural JSX:

    // Reusable table component for data records
    const DataRecord = ({ title, fields }) => (
    <dot:table border="1" cellborder="0" cellspacing="0">
    <dot:tr>
    <dot:td bgcolor="lightblue" colspan="2">
    <dot:b>{title}</dot:b>
    </dot:td>
    </dot:tr>
    {fields.map(([key, value]) => (
    <dot:tr key={key}>
    <dot:td port={key}>{key}</dot:td>
    <dot:td>{value}</dot:td>
    </dot:tr>
    ))}
    </dot:table>
    );

    // Status indicator component
    const StatusIndicator = ({ status, message }) => (
    <>
    <dot:b>Status:</dot:b><dot:br/>
    <dot:font color={status === 'active' ? 'green' : 'red'}>
    <dot:b>{status.toUpperCase()}</dot:b>
    </dot:font><dot:br/>
    <dot:i>{message}</dot:i>
    </>
    );

    // Usage in graph
    const DatabaseDiagram = () => (
    <Digraph>
    <Node
    id="user-table"
    label={<DataRecord
    title="Users"
    fields={[['id', '1001'], ['name', 'John Doe'], ['email', 'john@example.com']]}
    />}
    shape="record"
    />

    <Node
    id="server-status"
    label={<StatusIndicator status="active" message="All systems operational" />}
    shape="box"
    style="rounded,filled"
    fillcolor="lightyellow"
    />

    <Edge targets={["user-table", "server-status"]} label="monitors" />
    </Digraph>
    );
    • <dot:table>, <dot:tr>, <dot:td> - Table structures
    • <dot:b>, <dot:i>, <dot:u> - Text formatting (bold, italic, underline)
    • <dot:font> - Font styling with color, face, point-size
    • <dot:br> - Line breaks
    • <dot:hr>, <dot:vr> - Horizontal/vertical rules
    • <dot:img> - Images
    • <dot:s>, <dot:sub>, <dot:sup>, <dot:o> - Advanced text formatting

    Full type safety with IntelliSense support for all Graphviz attributes:

    import type { NodeProps, EdgeProps, DigraphProps } from '@ts-graphviz/react';

    // Typed component props
    const MyNode: React.FC<NodeProps> = (props) => (
    <Node
    shape="box" // ✅ TypeScript knows valid shapes
    color="blue" // ✅ String colors supported
    style="filled" // ✅ Valid style options
    {...props}
    />
    );

    // Edge with typed targets
    <Edge
    targets={["node1", "node2"]} // ✅ Tuple type enforced
    arrowhead="diamond" // ✅ Valid arrowhead styles
    />

    The package provides sophisticated TypeScript support with automatic type inference and runtime type filtering:

    // ✅ Automatic type inference - no casting needed
    const root = createRoot();
    await root.render(
    <Digraph id="myGraph" rankdir="LR">
    <Node id="A" shape="box" />
    <Node id="B" shape="circle" />
    <Edge targets={["A", "B"]} />
    </Digraph>
    );

    // ✅ Type-safe model access
    const models = root.getTopLevelModels();
    // models is automatically typed as DotObjectModel[]

    // ✅ Runtime type filtering with built-in type guards
    import { isNodeModel, isEdgeModel, isRootGraphModel } from '@ts-graphviz/common';

    // Filter by model type with automatic type narrowing
    const nodes = root.getTopLevelModels(isNodeModel);
    nodes.forEach(node => console.log(node.id)); // TypeScript knows this is NodeModel

    const edges = root.getTopLevelModels(isEdgeModel);
    edges.forEach(edge => console.log(edge.targets)); // TypeScript knows this is EdgeModel

    const graphs = root.getTopLevelModels(isRootGraphModel);
    graphs.forEach(graph => console.log(graph.directed)); // TypeScript knows this is RootGraphModel

    // ✅ Direct type casting (trusted user assertion)
    // When you know the exact types, you can cast directly without runtime validation
    const trustedNodes = root.getTopLevelModels<NodeModel>();
    trustedNodes.forEach(node => console.log(node.id)); // TypeScript trusts your assertion

    const trustedEdges = root.getTopLevelModels<EdgeModel>();
    trustedEdges.forEach(edge => console.log(edge.targets)); // No runtime type checking

    // ✅ Advanced model type checking
    const allModels = root.getTopLevelModels();
    for (const model of allModels) {
    if (isNodeModel(model)) {
    console.log(`Node: ${model.id}`);
    } else if (isEdgeModel(model)) {
    console.log(`Edge: ${model.targets.map(t => t.id).join(' -> ')}`);
    } else if (isRootGraphModel(model)) {
    console.log(`Graph: ${model.id} (directed: ${model.directed})`);
    }
    }

    When using container mode, you get access to all rendered models with full type safety:

    import { digraph } from 'ts-graphviz';
    import { isNodeModel, isEdgeModel, isSubgraphModel } from '@ts-graphviz/react';

    const container = digraph('myContainer');
    const root = createRoot({ container });

    await root.render(
    <>
    <Node id="node1" />
    <Node id="node2" />
    <Edge targets={['node1', 'node2']} />
    <Subgraph id="cluster1">
    <Node id="node3" />
    </Subgraph>
    </>
    );

    // Container mode: access all non-container models with type safety

    // Runtime type filtering (safe, validates at runtime)
    const allNodes = root.getTopLevelModels(isNodeModel); // NodeModel[]
    const allEdges = root.getTopLevelModels(isEdgeModel); // EdgeModel[]
    const subgraphs = root.getTopLevelModels(isSubgraphModel); // SubgraphModel[]

    // Direct type casting (user knows the types, no runtime validation)
    const trustedNodes = root.getTopLevelModels<NodeModel>(); // All models cast as NodeModel[]
    const trustedEdges = root.getTopLevelModels<EdgeModel>(); // All models cast as EdgeModel[]

    // Type-safe operations
    allNodes.forEach(node => {
    node.attributes.set('color', 'blue'); // TypeScript knows node attributes
    });

    allEdges.forEach(edge => {
    console.log(`Edge from ${edge.targets[0]} to ${edge.targets[1]}`);
    });

    The package provides clean async rendering APIs:

    • createRoot() - Creates a rendering root following React 19's createRoot pattern
    • renderToDot() - Primary async function for converting React components to DOT language strings
    • renderHTMLLike() - Renders HTML-like label structures for use in node or edge labels

    All rendering functions are async-only and provide a clean, consistent API surface. The new createRoot() API follows React 19's modern patterns for better performance and error handling.

    import { Digraph, Node, Edge, createRoot, renderToDot } from "@ts-graphviz/react";

    // Define a reusable process component
    const ProcessNode = ({ id, label, color = "lightblue" }) => (
    <Node
    id={id}
    label={
    <dot:table border="0" cellborder="1" cellspacing="0">
    <dot:tr>
    <dot:td bgcolor={color}>
    <dot:b>{label}</dot:b>
    </dot:td>
    </dot:tr>
    </dot:table>
    }
    shape="record"
    />
    );

    // Create a workflow diagram
    const WorkflowDiagram = () => (
    <Digraph rankdir="LR">
    <ProcessNode id="start" label="Start" color="lightgreen" />
    <ProcessNode id="process" label="Process Data" />
    <ProcessNode id="validate" label="Validate" />
    <ProcessNode id="end" label="End" color="lightcoral" />

    <Edge targets={["start", "process"]} />
    <Edge targets={["process", "validate"]} />
    <Edge targets={["validate", "end"]} />
    </Digraph>
    );

    // Create root and render to graph models
    const root = createRoot();
    await root.render(<WorkflowDiagram />);
    const models = root.getTopLevelModels();

    // Convert to DOT string
    const dotString = await renderToDot(<WorkflowDiagram />);
    import { Digraph, Node, Edge, renderToDot } from "@ts-graphviz/react";

    // Reusable card component with HTML-like labels
    const InfoCard = ({ id, title, items }) => (
    <Node
    id={id}
    label={
    <dot:table border="1" cellborder="0" cellspacing="0">
    <dot:tr>
    <dot:td bgcolor="navy">
    <dot:font color="white">
    <dot:b>{title}</dot:b>
    </dot:font>
    </dot:td>
    </dot:tr>
    {items.map((item, index) => (
    <dot:tr key={index}>
    <dot:td align="left">{item}</dot:td>
    </dot:tr>
    ))}
    </dot:table>
    }
    shape="record"
    />
    );

    // Usage in graph
    const ProjectDiagram = () => (
    <Digraph>
    <InfoCard
    id="requirements"
    title="Requirements"
    items={["User login", "Data processing", "Reporting"]}
    />
    <InfoCard
    id="implementation"
    title="Implementation"
    items={["React frontend", "Node.js API", "PostgreSQL DB"]}
    />

    <Edge targets={["requirements", "implementation"]} label="leads to" />
    </Digraph>
    );

    const dotString = await renderToDot(<ProjectDiagram />);
    import { renderHTMLLike } from "@ts-graphviz/react";

    const htmlLabel = renderHTMLLike(
    <dot:table>
    <dot:tr>
    <dot:td>left</dot:td>
    <dot:td>right</dot:td>
    </dot:tr>
    </dot:table>
    );
    import { Digraph, Node, Edge, createRoot } from "@ts-graphviz/react";

    // Basic usage
    const root = createRoot();
    await root.render(
    <Digraph>
    <Node id="A" />
    <Node id="B" />
    <Edge targets={["A", "B"]} />
    </Digraph>
    );

    // Container mode - render into existing graph
    import { digraph } from 'ts-graphviz';
    const container = digraph('MyGraph');
    const containerRoot = createRoot({ container });
    await containerRoot.render(
    <>
    <Node id="A" />
    <Node id="B" />
    <Edge targets={["A", "B"]} />
    </>
    );

    // Error handling options
    const rootWithErrorHandling = createRoot({
    onUncaughtError: (error, errorInfo) => {
    console.error('Rendering error:', error);
    console.log('Component stack:', errorInfo.componentStack);
    },
    onCaughtError: (error, errorInfo) => {
    console.error('Caught error:', error);
    }
    });

    await rootWithErrorHandling.render(<MyComplexGraph />);

    The package provides robust error handling capabilities for rendering errors:

    import { createRoot, renderToDot } from "@ts-graphviz/react";

    // Error handling with createRoot
    const root = createRoot({
    onUncaughtError: (error, errorInfo) => {
    console.error('Uncaught rendering error:', error.message);
    console.log('Component stack:', errorInfo.componentStack);
    // Send to error tracking service
    errorTracker.captureException(error, { extra: errorInfo });
    },
    onCaughtError: (error, errorInfo) => {
    console.error('Caught by error boundary:', error.message);
    // Handle recoverable errors
    }
    });

    await root.render(<MyGraph />);

    // renderToDot also supports error handling
    try {
    const dotString = await renderToDot(<ComplexGraph />);
    } catch (error) {
    if (error.message.includes('Multiple top-level graphs')) {
    console.error('Invalid graph structure');
    }
    }

    The package supports using ref to access and manipulate graph models directly, allowing for dynamic updates and interactions:

    import { useRef } from 'react';
    import { Digraph, Graph, Node, Edge, createRoot } from "@ts-graphviz/react";
    import type { NodeModel, EdgeModel, GraphBaseModel } from 'ts-graphviz';

    function MyGraphComponent() {
    const nodeRef = useRef<NodeModel>(null);
    const edgeRef = useRef<EdgeModel>(null);
    const digraphRef = useRef<GraphBaseModel>(null);
    const graphRef = useRef<GraphBaseModel>(null);

    const handleRender = async () => {
    // Example with Digraph component
    const digraphRoot = createRoot();
    await digraphRoot.render(
    <Digraph id="mygraph" ref={digraphRef}>
    <Node id="A" ref={nodeRef} label="Node A" />
    <Node id="B" label="Node B" />
    <Edge targets={['A', 'B']} ref={edgeRef} label="A to B" />
    </Digraph>
    );

    // Example with Graph component (undirected)
    const graphRoot = createRoot();
    await graphRoot.render(
    <Graph id="undirected-graph" ref={graphRef}>
    <Node id="X" label="Node X" />
    <Node id="Y" label="Node Y" />
    <Edge targets={['X', 'Y']} label="X -- Y" />
    </Graph>
    );

    // Access and manipulate the models directly
    if (nodeRef.current) {
    nodeRef.current.attributes.set('color', 'red');
    nodeRef.current.comment = 'Modified via ref';
    }

    if (edgeRef.current) {
    edgeRef.current.attributes.set('style', 'dashed');
    }

    console.log('Digraph nodes:', digraphRef.current?.nodes.length);
    console.log('Digraph edges:', digraphRef.current?.edges.length);
    console.log('Graph nodes:', graphRef.current?.nodes.length);
    console.log('Graph edges:', graphRef.current?.edges.length);
    };

    return (
    <button onClick={handleRender}>
    Render Graph
    </button>
    );
    }

    Thanks goes to these wonderful people (emoji key):

    Yuki Yamazaki
    Yuki Yamazaki

    💻 ⚠️ 📖 🤔
    LaySent
    LaySent

    🐛 ⚠️
    elasticdotventures
    elasticdotventures

    📖
    Christian Murphy
    Christian Murphy

    💻 🤔 📖
    Artem
    Artem

    🐛
    fredericohpandolfo
    fredericohpandolfo

    🐛
    diegoquinteiro
    diegoquinteiro

    🐛
    robross0606
    robross0606

    🤔
    Blake Regalia
    Blake Regalia

    🐛
    bigbug
    bigbug

    💬
    mrwk
    mrwk

    💬
    svdvonde
    svdvonde

    💬
    Adam
    Adam

    💬
    Trevor Scheer
    Trevor Scheer

    ️️️️♿️
    Prem Pillai
    Prem Pillai

    🐛
    nagasawaryoya
    nagasawaryoya

    💻 ⚠️
    YukiSasaki
    YukiSasaki

    💻 ⚠️
    Madd0g
    Madd0g

    🐛
    j4k0xb
    j4k0xb

    🐛
    HKrogstie
    HKrogstie

    🐛
    Nils K
    Nils K

    🐛
    hao2013
    hao2013

    🚧 👀
    Walter Rafelsberger
    Walter Rafelsberger

    💬
    grsjst
    grsjst

    🐛
    Steve
    Steve

    🐛

    This project follows the all-contributors specification. Contributions of any kind welcome!

    This software is released under the MIT License, see LICENSE.

    Namespaces

    Digraph
    Edge
    Graph
    GraphPortal
    Node
    Subgraph

    Interfaces

    CreateRootOptions
    EdgeOptions
    EdgeProps
    ErrorInfo
    GraphBaseAttributesProps
    GraphPortalProps
    GraphvizRoot
    NodeOptions
    NodeProps
    RenderToDotOptions
    RootGraphOptions
    RootGraphProps
    SubgraphOptions
    SubgraphProps

    Type Aliases

    AnyGraphContainer
    ComponentProps
    ComponentType
    FilterableModel
    ReactNode
    TopLevelModel

    Variables

    Digraph
    Edge
    Graph
    GraphPortal
    Node
    Subgraph

    Functions

    createRoot
    isAttributeListModel
    isEdgeModel
    isNodeModel
    isRootGraphModel
    isSubgraphModel
    renderHTMLLike
    renderToDot
    useCurrentGraph
    useDigraph
    useEdge
    useGraph
    useGraphContainer
    useNode
    useSubgraph