Graphviz-dot Renderer for React.
npm install @ts-graphviz/react







!node version


React Components and Renderer for ts-graphviz
🔗









---
> 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:

``bash`yarn
$ yarn add @ts-graphviz/react react@^19or npm
$ npm install -S @ts-graphviz/react react@^19or 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:
#### Graph Containers
-
-
-
#### Graph Elements
-
-
All components accept Graphviz DOT attributes as props with full TypeScript support:
`tsx
// Node with styling attributes
label="Web Server"
shape="box"
color="blue"
style="filled"
fillcolor="lightblue"
/>
// Edge with custom styling
label="API calls"
color="red"
style="dashed"
weight={2}
/>
// Digraph with global attributes
bgcolor="white"
node={{ shape: "ellipse", color: "gray" }}
edge={{ color: "black", arrowhead: "vee" }}
>
{/ nodes and edges /}
`
Components can be freely nested to create complex graph structures:
`tsx
// Define reusable components
const ServerNode = ({ id, name, type }) => (
label={
}
shape="record"
/>
);
const ServiceCluster = ({ id, label, children }) => (
{children}
);
// Compose the architecture
const SystemArchitecture = () => (
);
`
Create rich, formatted labels using Graphviz's HTML-like label syntax with natural JSX:
`tsx
// Reusable table component for data records
const DataRecord = ({ title, fields }) => (
{fields.map(([key, value]) => (
))}
);
// Status indicator component
const StatusIndicator = ({ status, message }) => (
<>
>
);
// Usage in graph
const DatabaseDiagram = () => (
label={
fields={[['id', '1001'], ['name', 'John Doe'], ['email', 'john@example.com']]}
/>}
shape="record"
/>
label={
shape="box"
style="rounded,filled"
fillcolor="lightyellow"
/>
);
`
- , , - Table structures
- , , - Text formatting (bold, italic, underline)
- - Font styling with color, face, point-size
- - Line breaks
- , - Horizontal/vertical rules
- - Images
- , , , - Advanced text formatting
Full type safety with IntelliSense support for all Graphviz attributes:
`tsx
import type { NodeProps, EdgeProps, DigraphProps } from '@ts-graphviz/react';
// Typed component props
const MyNode: React.FC
color="blue" // ✅ String colors supported
style="filled" // ✅ Valid style options
{...props}
/>
);
// Edge with typed targets
arrowhead="diamond" // ✅ Valid arrowhead styles
/>
`
#### JSX Type Definitions for HTML-Like Elements
TypeScript type definitions for all dot:* HTML-like elements are automatically available when you import @ts-graphviz/react. No additional configuration or setup is required.
`tsx
import { Digraph, Node, Edge } from '@ts-graphviz/react';
// Full IntelliSense and type checking for dot:* elements works out of the box
label={
}
shape="box"
/>
`
The DotJSXElements type is also exported for advanced use cases where you need to reference the type definitions directly.
#### Enhanced Type Safety
The package provides sophisticated TypeScript support with automatic type inference and runtime type filtering:
`tsx
// ✅ Automatic type inference - no casting needed
const root = createRoot();
await root.render(
);
// ✅ 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
trustedNodes.forEach(node => console.log(node.id)); // TypeScript trusts your assertion
const trustedEdges = root.getTopLevelModels
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});Edge: ${model.targets.map(t => t.id).join(' -> ')}
} else if (isEdgeModel(model)) {
console.log();Graph: ${model.id} (directed: ${model.directed})
} else if (isRootGraphModel(model)) {
console.log();`
}
}
#### Container Mode Type Safety
When using container mode, you get access to all rendered models with full type safety:
`tsx
import { digraph } from 'ts-graphviz';
import { isNodeModel, isEdgeModel, isSubgraphModel } from '@ts-graphviz/react';
const container = digraph('myContainer');
const root = createRoot({ container });
await root.render(
<>
>
);
// 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
const trustedEdges = root.getTopLevelModels
// 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]});`
});
- createRoot() - Creates a rendering root following React 19's createRoot patternrenderToDot()
- - Primary async function for converting React components to DOT language stringsrenderHTMLLike()
- - 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.
`tsx
import { Digraph, Node, Edge, createRoot, renderToDot } from "@ts-graphviz/react";
// Define a reusable process component
const ProcessNode = ({ id, label, color = "lightblue" }) => (
label={
}
shape="record"
/>
);
// Create a workflow diagram
const WorkflowDiagram = () => (
);
// Create root and render to graph models
const root = createRoot();
await root.render(
const models = root.getTopLevelModels();
// Convert to DOT string
const dotString = await renderToDot(
`
`tsx
import { Digraph, Node, Edge, renderToDot } from "@ts-graphviz/react";
// Reusable card component with HTML-like labels
const InfoCard = ({ id, title, items }) => (
label={
{items.map((item, index) => (
))}
}
shape="record"
/>
);
// Usage in graph
const ProjectDiagram = () => (
title="Requirements"
items={["User login", "Data processing", "Reporting"]}
/>
title="Implementation"
items={["React frontend", "Node.js API", "PostgreSQL DB"]}
/>
);
const dotString = await renderToDot(
`
The renderHTMLLike function converts React elements with HTML-like labels into Graphviz-compatible markup. It includes built-in security protections against deeply nested structures and circular references.
`ts
import { renderHTMLLike } from "@ts-graphviz/react";
// Basic usage
const htmlLabel = renderHTMLLike(
);
// With custom security options
const deeplyNestedLabel = renderHTMLLike(
{/ ... deeply nested structure ... /}
{ maxDepth: 2000 } // Increase limit for complex structures
);
// Stricter validation for user-generated content
const userContent = renderHTMLLike(
userProvidedElement,
{ maxDepth: 100 } // Lower limit for untrusted input
);
`
#### Security Model: DOT Language vs Browser HTML
Important: The HTML-like labels generated by this library are not browser HTML. They are:
- Part of the GraphViz DOT language specification
- Processed by the GraphViz engine (not browsers)
- Converted to SVG/PNG/etc. by GraphViz tools
XSS Risk Context:
- This library generates DOT strings, not web content
- XSS risks occur when rendering GraphViz output (SVG/HTML) in browsers
- See @ts-graphviz/adapter documentation for handling user-provided DOT files safely
#### Security Options
The renderHTMLLike function provides security protections against stack overflow attacks:
- maxDepth (default: 1000): Maximum recursion depth for nested elementsmaxDepth: 5000
- Prevents stack overflow from deeply nested or circular structures
- Adjust based on your use case:
- Increase for complex, trusted structures (e.g., )maxDepth: 100
- Decrease for user-generated content (e.g., )'<>'
- When the limit is exceeded, the function returns a safe fallback value ()
`ts
import { renderHTMLLike, type RenderHTMLLikeOptions } from "@ts-graphviz/react";
// Type-safe options
const options: RenderHTMLLikeOptions = {
maxDepth: 1500
};
const result = renderHTMLLike(
`
`ts
import { Digraph, Node, Edge, createRoot } from "@ts-graphviz/react";
// Basic usage
const root = createRoot();
await root.render(
);
// Container mode - render into existing graph
import { digraph } from 'ts-graphviz';
const container = digraph('MyGraph');
const containerRoot = createRoot({ container });
await containerRoot.render(
<>
>
);
// 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(
`
The package provides robust error handling capabilities for rendering errors:
`ts
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(
// renderToDot also supports error handling
try {
const dotString = await renderToDot(
} 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:
`ts
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
const edgeRef = useRef
const digraphRef = useRef
const graphRef = useRef
const handleRender = async () => {
// Example with Digraph component
const digraphRoot = createRoot();
await digraphRoot.render(
);
// Example with Graph component (undirected)
const graphRoot = createRoot();
await graphRoot.render(
);
// 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 (
);
}
``
Thanks goes to these wonderful people (emoji key):
This project follows the all-contributors
specification. Contributions of any kind welcome!
This software is released under the MIT License, see LICENSE.