<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: JointJS</title>
    <description>The latest articles on DEV Community by JointJS (jointjs).</description>
    <link>https://dev.to/jointjs</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Forganization%2Fprofile_image%2F15350%2F98158a50-d552-4ead-a49c-debcd752ac78.png</url>
      <title>DEV Community: JointJS</title>
      <link>https://dev.to/jointjs</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/jointjs"/>
    <language>en</language>
    <item>
      <title>How to embed a visual Mermaid editor in your React app</title>
      <dc:creator>Zoran Jambor</dc:creator>
      <pubDate>Wed, 04 Nov 2026 12:50:26 +0000</pubDate>
      <link>https://dev.to/jointjs/how-to-embed-a-visual-mermaid-editor-in-your-react-app-318</link>
      <guid>https://dev.to/jointjs/how-to-embed-a-visual-mermaid-editor-in-your-react-app-318</guid>
      <description>&lt;p&gt;Mermaid has effectively become the standard for diagrams as code. You write a flowchart like &lt;code&gt;A --&amp;gt; B&lt;/code&gt; in Markdown, GitHub renders it, and it lives in version control next to your code. Mermaid diagrams are in READMEs, docs sites, and wikis, and increasingly the output across almost all major AI platforms for generating text-based diagrams. All of this makes Mermaid the obvious format for any product that displays diagrams, and the next step for those products is a visual Mermaid editor that lets users change a diagram by clicking on it instead of learning the syntax.&lt;/p&gt;

&lt;p&gt;  &lt;iframe src="https://www.youtube.com/embed/NyQq2KX3O6U" width="710" height="399"&gt;
  &lt;/iframe&gt;
&lt;/p&gt;

&lt;p&gt;But Mermaid diagrams integrated into products can quickly show their limits. The output is a static SVG; users can’t click a node to rename it or restyle an arrow without learning the syntax. Just as well, a typical visual editor built on top keeps its own copy of the diagram, separate from the Mermaid text. When you edit the diagram, the text becomes out of date. If you edit the text, the diagram is no longer accurate. Teams building docs platforms, wikis, low-code tools, and AI assistants often realize that Mermaid is great, but the editing experience isn't.&lt;/p&gt;

&lt;p&gt;To close that gap, we built the Mermaid Editor app template with JointJS, a diagramming library for building visual editors in JavaScript and React. It reads Mermaid with Mermaid’s own parser, renders it as a live, editable diagram, and writes every canvas change back into the text.&lt;/p&gt;

&lt;p&gt;In this article, you’ll see what the JointJS Mermaid Editor does, the patterns behind it, and how to embed it in your own React app.&lt;/p&gt;

&lt;h2&gt;
  
  
  About the JointJS Mermaid Editor demo
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://jointjs.com/demos/mermaid-editor" rel="noopener noreferrer"&gt;The Mermaid Editor&lt;/a&gt; is a split-screen editor for Mermaid flowcharts. You write Mermaid on the left, and on the right you get a real, interactive diagram rendered by JointJS+ for React. As you type, the source is reparsed, and the diagram is laid out as you go. When you edit the diagram, the text changes.&lt;/p&gt;

&lt;p&gt;&lt;iframe src="https://player.vimeo.com/video/1242448332" width="710" height="399"&gt;
&lt;/iframe&gt;
&lt;/p&gt;

&lt;p&gt;Here’s what that looks like in practice:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;It works with standard Mermaid syntax.&lt;/strong&gt; All classic delimiter shapes (&lt;code&gt;[ ]&lt;/code&gt;, &lt;code&gt;( )&lt;/code&gt;, &lt;code&gt;{ }&lt;/code&gt;, &lt;code&gt;[( )]&lt;/code&gt;, and the rest), the extended v11 &lt;code&gt;@{ shape: … }&lt;/code&gt; catalog, &lt;code&gt;@{ img: … }&lt;/code&gt; image nodes, nested &lt;code&gt;subgraph&lt;/code&gt; blocks, &lt;code&gt;style&lt;/code&gt;, &lt;code&gt;classDef&lt;/code&gt;, click links, &lt;code&gt;linkStyle&lt;/code&gt;, edge labels, curves, and even &lt;code&gt;@{ animate: true }&lt;/code&gt; dash-animated links.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Every node is a real SVG shape that sizes itself to its label.&lt;/strong&gt; No fixed-width boxes with overflowing text.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The source and the canvas point at each other.&lt;/strong&gt; Click a node, and every mention of it lights up in the source. Move the caret in the editor, and the diagram selects and scrolls to the node you're on.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The canvas is an editor, not a viewer.&lt;/strong&gt; Double-click to rename a node in place. Select a node to see a toolbar with shape, fill, text color, bold and italic, border, hyperlink, image, and an "add a step" action. Select a link to change its line pattern, arrowheads, color, curve, and animation. Shift-click to select two nodes, and the editor offers to connect them.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Every edit is written into the Mermaid text.&lt;/strong&gt; The source stays the single source of truth, so what you export is still a plain &lt;code&gt;.mmd&lt;/code&gt; file that works in GitHub, GitLab, Notion, or anywhere else Mermaid does.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;It's a proper canvas.&lt;/strong&gt; Pan and zoom on an infinite PaperScroller, switch layout direction and link routing (curved or orthogonal), toggle light and dark themes, and export the diagram to SVG.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The source editor is Monaco, the same editor that powers VS Code, with Mermaid syntax highlighting, autocompletion, find and replace, multiple cursors, and full undo history.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Ff4fk76q3k4944arat4tn.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Ff4fk76q3k4944arat4tn.png" alt="A detail from the Mermaid Editor, a node toolbar." width="799" height="461"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;You can find the full source code on GitHub: &lt;a href="https://github.com/clientIO/joint-demos/tree/main/mermaid-editor" rel="noopener noreferrer"&gt;clientIO/joint-demos/mermaid-editor&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  How the editor keeps the Mermaid text and the diagram in sync
&lt;/h2&gt;

&lt;p&gt;The key aspect of the Mermaid Editor, precisely as in our &lt;a href="https://www.jointjs.com/demos/data-modeling" rel="noopener noreferrer"&gt;Data Modeling App Template&lt;/a&gt;, is that there is a single source of truth, the Mermaid text.&lt;/p&gt;

&lt;p&gt;The visual diagram is never edited directly, but rather always a rendering of the current source. When you type, the source changes and the diagram is rendered from it. When you change something on the canvas, the app doesn’t touch the diagram, but computes the new Mermaid text and derives the visual from that.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fhlo3sa5dwopok81w5vh2.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fhlo3sa5dwopok81w5vh2.png" alt="Every first edit updates the Mermaid text, and then renders the diagram from it." width="799" height="305"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Everything flows clockwise. The text will be parsed by Mermaid, mapped to JointJS cells, and rendered. Canvas actions become text edits, which start the loop again. There’s no second model to keep in sync, so the diagram and the text can't fall out of sync.&lt;/p&gt;

&lt;p&gt;An automatic layout engine calculates node positions on every render, just as Mermaid does. The editor supports the two layout engines Mermaid offers for flowcharts. Dagre, a layered layout with smooth, curved links, which is the default, and ELK, which routes links at right angles and is used when the source sets &lt;code&gt;layout: elk&lt;/code&gt;. You can easily switch between them by editing Mermaid text.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fofiq8pojjxa32fa4pwat.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fofiq8pojjxa32fa4pwat.png" alt="A look at the layout: elk option in Mermaid config." width="800" height="422"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  From Mermaid to JointJS cells
&lt;/h2&gt;

&lt;p&gt;Mermaid’s syntax is large, evolving, and full of edge cases, so the demo uses Mermaid’s own parser and reads the result from its diagram database:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// src/mermaid/parse.ts&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;mermaid&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;loadMermaid&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

&lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;diagram&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;diagram&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;mermaid&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;mermaidAPI&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getDiagramFromText&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;text&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;catch &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;MermaidParseError&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;toMessage&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;// … (rejects diagram types other than flowchart)&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;db&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;diagram&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;db&lt;/span&gt; &lt;span class="kd"&gt;as &lt;/span&gt;&lt;span class="nx"&gt;unknown&lt;/span&gt; &lt;span class="kd"&gt;as &lt;/span&gt;&lt;span class="nx"&gt;FlowDbLike&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;nodes&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;edges&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;db&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getData&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;When Mermaid is parsed, it figures out what each shape should be, breaks two-way arrows into separate one-way arrows, and keeps track of which nodes are inside each subgraph.&lt;/p&gt;

&lt;p&gt;The parsed graph is then mapped onto declarative JointJS cell records with a single pure function:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// src/mermaid/to-cells.ts&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;toCells&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;flow&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;FlowGraph&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="nx"&gt;MermaidCell&lt;/span&gt;&lt;span class="p"&gt;[]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That’s the same pattern the &lt;a href="https://www.jointjs.com/blog/how-to-build-production-grade-data-modeling-apps-in-react-a-jointjs-architecture-walkthrough" rel="noopener noreferrer"&gt;Data Modeling app&lt;/a&gt; uses with &lt;code&gt;schemaToCells&lt;/code&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Nodes grow to fit their labels
&lt;/h3&gt;

&lt;p&gt;Each node on the diagram is a React component that renders its Mermaid shape as SVG, and &lt;code&gt;useMeasureElement&lt;/code&gt; measures the label and provides its exact sizing:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;&lt;span class="c1"&gt;// src/components/render-node.tsx&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;width&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;height&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useMeasureElement&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;textRef&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;options&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That’s why a diamond with a long label grows bigger instead of clipping its text, and why a label can span as many lines as it needs to. Shapes are defined once in &lt;code&gt;shapes.ts&lt;/code&gt;, and the same geometry draws both the node on the canvas and its icon in the toolbar’s shape picker, so the two can never drift apart.&lt;/p&gt;

&lt;h3&gt;
  
  
  Subgraphs become real containers
&lt;/h3&gt;

&lt;p&gt;A Mermaid &lt;code&gt;subgraph&lt;/code&gt; turns into a JointJS container element. Its nodes are embedded inside it, and after layout, the container is resized to fit perfectly. Because the nodes really belong to the container, rather than sitting on a box drawn behind them, subgraphs can be nested too.&lt;/p&gt;

&lt;h3&gt;
  
  
  Links keep their Mermaid styling
&lt;/h3&gt;

&lt;p&gt;Each Mermaid link (the parser returns them as edges) becomes a JointJS link that carries everything Mermaid knows about it, from solid, dotted, or thick lines, to point, circle, or cross arrowheads at either end, labels, &lt;code&gt;linkStyle&lt;/code&gt; colors and widths, curved routing for Mermaid’s curve families, and a CSS marching-dash animation for &lt;code&gt;animate: true&lt;/code&gt; links.&lt;/p&gt;

&lt;h3&gt;
  
  
  Layout runs when every node is measured
&lt;/h3&gt;

&lt;p&gt;Once every node is measured, &lt;code&gt;DirectedGraph.layout&lt;/code&gt; from &lt;a href="https://docs.jointjs.com/learn/features/automatic-layouts/directed-graph/" rel="noopener noreferrer"&gt;@joint/layout-directed-graph&lt;/a&gt; arranges the graph in whatever direction the source declares (&lt;code&gt;TB&lt;/code&gt;, &lt;code&gt;BT&lt;/code&gt;, &lt;code&gt;LR&lt;/code&gt;, or &lt;code&gt;RL&lt;/code&gt;). The &lt;code&gt;DirectedGraph.layout&lt;/code&gt; is the default, the same dagre-style layout Mermaid uses, with curved links. When you set &lt;code&gt;layout: elk&lt;/code&gt; option, the editor switches to &lt;a href="https://docs.jointjs.com/learn/features/automatic-layouts/elk" rel="noopener noreferrer"&gt;@joint/layout-elk&lt;/a&gt; with orthogonal links.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;&lt;span class="nf"&gt;useOnElementsMeasured&lt;/span&gt;&lt;span class="p"&gt;(({&lt;/span&gt; &lt;span class="na"&gt;graph&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;measuredGraph&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nf"&gt;settle&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;measuredGraph&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;onLayoutSettled&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;useOnElementsMeasured&lt;/code&gt; hook repaints the paper right after the callback, so there’s never a frame where diagram nodes are visible at their pre-layout position.&lt;/p&gt;

&lt;h2&gt;
  
  
  From the diagram canvas back to Mermaid
&lt;/h2&gt;

&lt;p&gt;Rendering Mermaid is the easier half. The hard part, and the reason so many Mermaid tools stop at a preview, is writing visual edits back into Mermaid format without breaking it.&lt;/p&gt;

&lt;p&gt;The obvious approach is to regenerate the whole source from the parsed graph after every edit. It’s also the wrong one. Mermaid’s parsed output is a normalization, not a faithful syntax tree, so it has no comments, no formatting, no &lt;code&gt;classDef&lt;/code&gt; names, and shapes come back as IDs rather than the delimiters the author typed. With this approach, every click on the canvas would reformat the author’s entire document, delete their comments, and produce a massive diff.&lt;/p&gt;

&lt;p&gt;In this app, the source is never fully recreated. Every canvas action is a targeted edit that finds the exact span of text responsible for the change made and splices in the new value.&lt;/p&gt;

&lt;p&gt;For example, here’s how renaming a node works:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// src/mermaid/edit-source.ts&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;setNodeLabel&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;source&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;CellId&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;label&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
   &lt;span class="c1"&gt;// … (a v11 `id@{ label: … }` block is edited in place first)&lt;/span&gt;

   &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;parsed&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;parse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;source&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
   &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;declaration&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;findDeclaration&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;parsed&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
   &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;declaration&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
   &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;text&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;quoteLabel&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;encodeBreaks&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;label&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;trim&lt;/span&gt;&lt;span class="p"&gt;()));&lt;/span&gt;

   &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;declaration&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;labelFrom&lt;/span&gt; &lt;span class="o"&gt;!==&lt;/span&gt; &lt;span class="kc"&gt;undefined&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nx"&gt;declaration&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;labelTo&lt;/span&gt; &lt;span class="o"&gt;!==&lt;/span&gt; &lt;span class="kc"&gt;undefined&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
       &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;splice&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;source&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;declaration&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;labelFrom&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;declaration&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;labelTo&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;text&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
   &lt;span class="p"&gt;}&lt;/span&gt;
   &lt;span class="c1"&gt;// A bare `a --&amp;gt; b` has no shape to hold a label; give it the default one.&lt;/span&gt;
   &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;declaration&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;shapeFrom&lt;/span&gt; &lt;span class="o"&gt;!==&lt;/span&gt; &lt;span class="kc"&gt;undefined&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nx"&gt;declaration&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;shapeTo&lt;/span&gt; &lt;span class="o"&gt;!==&lt;/span&gt; &lt;span class="kc"&gt;undefined&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
       &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;splice&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;source&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;declaration&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;shapeFrom&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;declaration&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;shapeTo&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;`[&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;text&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;]`&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
   &lt;span class="p"&gt;}&lt;/span&gt;
   &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;splice&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;source&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;declaration&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;idTo&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;declaration&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;idTo&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;`[&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;text&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;]`&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Ideally, Mermaid's own parser would say where each piece of the diagram sits in the text. Mermaid's newer diagram types are parsed with Langium, which returns a typed syntax tree whose nodes record their exact offset and length in the source. Flowcharts, though, haven't been migrated yet. They still use Mermaid's older Jison grammar, which Mermaid now marks as deprecated, and Jison hands back the finished diagram data with no record of where anything came from in the text. To edit the source precisely, you need a second parser that keeps positions.&lt;/p&gt;

&lt;p&gt;So each edit parses the current source using a separate Lezer grammar for Mermaid from the &lt;a href="https://github.com/inspirnathan/codemirror-lang-mermaid" rel="noopener noreferrer"&gt;codemirror-lang-mermaid&lt;/a&gt; package, but it doesn’t interpret the diagram. It only records where each node ID, shape bracket, and label sits in the text. The edit then replaces just that piece, so the rest of the source, including comments and formatting, stays exactly as the author wrote it.&lt;/p&gt;

&lt;p&gt;There's one of these functions per action, and each one deals with corner cases of Mermaid syntax so the UI doesn't have to.&lt;/p&gt;

&lt;p&gt;The React side is equally small. Every handler on the node toolbar runs one of these functions on the current source and hands the result back to the same state the editor writes to:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;&lt;span class="c1"&gt;// src/app.tsx&lt;/span&gt;

&lt;span class="nx"&gt;onLabelChange&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;label&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;apply&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;setNodeLabel&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;sourceRef&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;current&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;label&lt;/span&gt;&lt;span class="p"&gt;)),&lt;/span&gt;
&lt;span class="nx"&gt;onShapeChange&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;shape&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;apply&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;setNodeShape&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;sourceRef&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;current&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;shape&lt;/span&gt;&lt;span class="p"&gt;)),&lt;/span&gt;
&lt;span class="nx"&gt;onFillChange&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;fill&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;apply&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;setNodeFill&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;sourceRef&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;current&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;fill&lt;/span&gt;&lt;span class="p"&gt;)),&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Any change, whether typed or made on the canvas, is handled in the same way. There are no separate processes for canvas and editor edits, so they always stay in sync. Since every edit only updates the text, it’s easy to test these changes, and the repo includes unit tests for edits like removing elements or updating labels and links.&lt;/p&gt;

&lt;h2&gt;
  
  
  Details that make it feel like an editor
&lt;/h2&gt;

&lt;p&gt;A two-way binding is easy to describe and surprisingly easy to get subtly wrong. Most of the remaining code in the demo is about making it feel instant and predictable.&lt;/p&gt;

&lt;h3&gt;
  
  
  Selection without feedback loops
&lt;/h3&gt;

&lt;p&gt;The text and diagram don’t directly control each other. Instead, the app manages selection in one central place. Whenever you interact with either side, that action is reported up, so the app then knows what to display in both the text and the diagram.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;&lt;span class="c1"&gt;// src/app.tsx&lt;/span&gt;

&lt;span class="kr"&gt;interface&lt;/span&gt; &lt;span class="nx"&gt;Selection&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
   &lt;span class="k"&gt;readonly&lt;/span&gt; &lt;span class="nx"&gt;ids&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;readonly&lt;/span&gt; &lt;span class="nx"&gt;CellId&lt;/span&gt;&lt;span class="p"&gt;[];&lt;/span&gt;
   &lt;span class="k"&gt;readonly&lt;/span&gt; &lt;span class="nx"&gt;origin&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;canvas&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;editor&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For example:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Clicking a node in the diagram highlights all its matches in the text and scrolls the first one into view, which is helpful when you want to find the code for a shape you see.&lt;/li&gt;
&lt;li&gt;Moving the caret in the text editor selects and centers the matching node in the diagram.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;By letting the app handle selection, changes don’t bounce back and forth between the text and the diagram. This keeps everything in sync without causing endless update loops.&lt;/p&gt;

&lt;h3&gt;
  
  
  Optimized for production-grade use cases
&lt;/h3&gt;

&lt;p&gt;The demo, as it’s always the case with JointJS application templates, is optimized for performance, from the big picture to the smallest details.&lt;/p&gt;

&lt;p&gt;Typing is debounced by 300 ms, so the editor is not parsing text on every keystroke. But actions like picking a shape, loading an example, or hitting undo happen right away without any delay. This avoids lag and keeps the app feeling quick and responsive.&lt;/p&gt;

&lt;p&gt;Two heavy dependencies, Mermaid and Monaco, are loaded with dynamic imports, so each comes in its own chunk instead of the app’s main bundle. And since JointJS for React works with declarative cell records, an edit that only changes a label or a color updates the existing graph instead of rebuilding it.&lt;/p&gt;

&lt;h3&gt;
  
  
  Accessible by default
&lt;/h3&gt;

&lt;p&gt;As with any JointJS app template, we paid special consideration to accessibility.&lt;/p&gt;

&lt;p&gt;&lt;iframe src="https://player.vimeo.com/video/1242772585" width="710" height="399"&gt;
&lt;/iframe&gt;
&lt;/p&gt;

&lt;p&gt;The app works with screen readers, everything is accessible with keyboard, text not only has sufficient contrast but also looks great in both themes, and reduced-motion preferences are respected, enabling users to disable unnecessary animations (&lt;code&gt;@media (prefers-reduced-motion: reduce) {}&lt;/code&gt;).&lt;/p&gt;

&lt;h2&gt;
  
  
  Embedding the Mermaid editor in your React app
&lt;/h2&gt;

&lt;p&gt;The demo is a complete app, built from two pieces you can embed into your own product. The Mermaid logic is in &lt;code&gt;src/mermaid/&lt;/code&gt; and the React components in &lt;code&gt;src/components/&lt;/code&gt;. The app shell in &lt;code&gt;app.tsx&lt;/code&gt; holds the raw text as state and connects the two:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;&lt;span class="c1"&gt;// src/app.tsx (simplified)&lt;/span&gt;

&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;EditorPanel&lt;/span&gt;
   &lt;span class="na"&gt;source&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;source&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
   &lt;span class="na"&gt;onSourceChange&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;handleSourceChange&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
   &lt;span class="na"&gt;onCursorNodeChange&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;selectFromEditor&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
   &lt;span class="na"&gt;highlightedIds&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;selection&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;origin&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;canvas&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="nx"&gt;selection&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;ids&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;NO_IDS&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;/&amp;gt;&lt;/span&gt;

&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;MermaidDiagram&lt;/span&gt;
   &lt;span class="na"&gt;direction&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;rendered&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;direction&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
   &lt;span class="na"&gt;layout&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;rendered&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;layout&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
   &lt;span class="na"&gt;cells&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;rendered&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;cells&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
   &lt;span class="na"&gt;selectedIds&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;selection&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;ids&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
   &lt;span class="na"&gt;onSelect&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;selectFromCanvas&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
   &lt;span class="na"&gt;edit&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;edit&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
   &lt;span class="na"&gt;linkEdit&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;linkEdit&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;/&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Notice that the diagram component, &lt;code&gt;MermaidDiagram&lt;/code&gt;, never sees the Mermaid text. It receives a list of shapes and links to draw, and when the user changes something, it reports the change. The app shell turns that info into updated Mermaid text and passes the new shapes back down. Because the component only draws and reports, you can plug it into any app that stores Mermaid text:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;The source can live anywhere.&lt;/strong&gt; Load your own Mermaid text from anywhere, such as a Markdown file, a database field, or an LLM response, and let your users edit it visually, then save the Mermaid text back. There's no extra diagram format to store.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The code editor is optional.&lt;/strong&gt; &lt;code&gt;EditorPanel&lt;/code&gt; and &lt;code&gt;MermaidDiagram&lt;/code&gt; are completely separate components. Developers probably want both side by side, but for non-technical users, you can show only the canvas and its toolbars, and keep Mermaid behind the scenes.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The Mermaid logic is plain TypeScript.&lt;/strong&gt; Parsing Mermaid flowchart source is done in &lt;code&gt;parse.ts&lt;/code&gt;, which contains no React or canvas code, so you can easily extend it for the parts of Mermaid your users rely on.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Theming is done with CSS Custom Properties.&lt;/strong&gt; Color themes (both light and dark) are set entirely in CSS variables (&lt;code&gt;index.css&lt;/code&gt;), so you can easily tweak them to match your own brand and product style.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Use the Mermaid Editor in your projects, from docs platforms to AI assistants
&lt;/h2&gt;

&lt;p&gt;If you read our &lt;a href="https://www.jointjs.com/blog/how-to-build-production-grade-data-modeling-apps-in-react-a-jointjs-architecture-walkthrough" rel="noopener noreferrer"&gt;Data Modeling App Template walkthrough&lt;/a&gt;, the shape of this app will look familiar. In Data Modeling Demo, SQL DDL serves as the text format, and the graph carries the schema. In Mermaid Editor Demo, Mermaid functions as the text format, and the source carries the diagram. In both cases:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;One thing is the source of truth, and everything else is derived from it.&lt;/li&gt;
&lt;li&gt;The domain logic (SQL generation and parsing in Data Modeling, and Mermaid parsing and source edits here) doesn’t depend on the canvas, so it’s testable on its own.&lt;/li&gt;
&lt;li&gt;A single function maps the domain model to cells, and JointJS for React renders those cells declaratively.&lt;/li&gt;
&lt;li&gt;Nodes are React components, and the interaction layer (selection, scrolling, zooming, layout, export) comes from the library instead of being rebuilt by hand.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;That makes the demo a useful starting point well beyond flowcharts. A few places it fits naturally:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Docs and knowledge-base platforms&lt;/strong&gt; that already store Mermaid in Markdown, so you can easily give non-technical users a visual way to edit it, without changing the storage format.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;AI assistants and agents&lt;/strong&gt; that generate flowcharts as Mermaid. LLMs are good at writing Mermaid and not great at getting a diagram exactly right on the first try. A visual editor on top lets users fix the last 10% by clicking instead of re-prompting.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Internal tools and wikis&lt;/strong&gt; where diagrams live in Git and need to stay reviewable as text diffs.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Other text-based diagram formats.&lt;/strong&gt; Swap &lt;code&gt;parse.ts&lt;/code&gt; and &lt;code&gt;edit-source.ts&lt;/code&gt; for another grammar (another Mermaid diagram type, PlantUML, D2, Graphviz DOT, or your own DSL), and most of the rendering and interaction layer stays as it is.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The current demo covers Mermaid flowcharts. Other Mermaid types like sequence or state diagrams would need their own mapping in &lt;code&gt;toCells&lt;/code&gt; and their own source edits, but the architecture doesn’t change.&lt;/p&gt;

&lt;h2&gt;
  
  
  Getting started
&lt;/h2&gt;

&lt;p&gt;The quickest way to see what this does is to &lt;a href="https://jointjs.com/demos/mermaid-editor" rel="noopener noreferrer"&gt;open the live Mermaid Editor demo&lt;/a&gt; and paste in a flowchart of your own. Pick something from a real README, or ask your favorite LLM to generate one. Then click around, rename a node, change an arrow, switch the layout direction, and watch the source follow along.&lt;/p&gt;

&lt;p&gt;When you’re ready to embed it in your own app, download the app template locally:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npx @joint/cli download mermaid-editor/react
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You’ll need access to the JointJS+ for React package, through a license or a &lt;a href="https://www.jointjs.com/free-trial" rel="noopener noreferrer"&gt;free trial&lt;/a&gt;. The full source is on GitHub: &lt;a href="https://github.com/clientIO/joint-demos/tree/main/mermaid-editor" rel="noopener noreferrer"&gt;clientIO/joint-demos/mermaid-editor&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Like our other demos, the codebase is heavily commented with the reasoning behind each decision, which makes it a good fit for an AI coding agent. Point your agent at it, describe your use case, and let it adapt the demo. For best results, set up the &lt;a href="https://docs.jointjs.com/learn/help-center/mcp-server/" rel="noopener noreferrer"&gt;JointJS MCP Server&lt;/a&gt;, which gives your agent direct access to up-to-date JointJS docs and demo code.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://jointjs.com/demos/mermaid-editor" rel="noopener noreferrer"&gt;Try the Mermaid Editor demo&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/clientIO/joint-demos/tree/main/mermaid-editor" rel="noopener noreferrer"&gt;Browse the source on GitHub&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://docs.jointjs.com/react/" rel="noopener noreferrer"&gt;Read the JointJS for React docs&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.jointjs.com/free-trial" rel="noopener noreferrer"&gt;Start your free JointJS+ trial&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Happy diagramming!&lt;/p&gt;

&lt;p&gt;--&lt;/p&gt;

&lt;h2&gt;
  
  
  FAQ
&lt;/h2&gt;

&lt;p&gt;&lt;/p&gt;
  &lt;strong&gt;What is the JointJS Mermaid Editor?&lt;/strong&gt;
  &lt;p&gt;It’s an app template built with JointJS+ for React that turns Mermaid flowchart source into an interactive, editable diagram. Changes made on the canvas are written back into the Mermaid text, so the source always stays the single source of truth. &lt;/p&gt;

&lt;p&gt;&lt;/p&gt;

&lt;p&gt;&lt;/p&gt;
  &lt;strong&gt;How is this different from the Mermaid Live Editor?&lt;/strong&gt;
  &lt;p&gt;The Mermaid Live Editor renders a static preview of your text. The JointJS Mermaid Editor renders a live JointJS graph you can edit directly, from renaming nodes and changing shapes and styles to adding and connecting steps, restyling links, and switching the layout direction, with every change synced back to the source. &lt;/p&gt;

&lt;p&gt;&lt;/p&gt;

&lt;p&gt;&lt;/p&gt;
  &lt;strong&gt;How is this different from Mermaid’s Advanced Editor?&lt;/strong&gt;
  &lt;p&gt;The Advanced Editor is the editor in mermaid.ai, Mermaid’s subscription-based platform. It bundles AI generation, saved diagrams and collaboration with a Visual Editor for canvas editing. The JointJS editor offers the same kind of two-way editing for flowcharts, but as a template you embed in your own React app. &lt;/p&gt;

&lt;p&gt;&lt;/p&gt;

&lt;p&gt;&lt;/p&gt;
  &lt;strong&gt;Can I easily embed the Mermaid Editor in an existing React app?&lt;/strong&gt;
  &lt;p&gt;Yes. The editor uses React components to handle diagram updates, while your app manages the Mermaid text. You can keep the source anywhere you like, from a Markdown file, to a database, or from an LLM response, and add the code editor panel only if your users want to see or edit the text. &lt;/p&gt;

&lt;p&gt;&lt;/p&gt;

&lt;p&gt;&lt;/p&gt;
  &lt;strong&gt;Does it use a custom Mermaid parser?&lt;/strong&gt;
  &lt;p&gt;No. It uses Mermaid’s own parser to read the diagram, so syntax support follows Mermaid itself. A separate Lezer grammar locates exact text spans so canvas edits can be applied without reformatting the rest of the source. &lt;/p&gt;

&lt;p&gt;&lt;/p&gt;

&lt;p&gt;&lt;/p&gt;
  &lt;strong&gt;Which Mermaid features are supported?&lt;/strong&gt;
  &lt;p&gt;Flowcharts, including all classic node shapes, the extended v11 &lt;code&gt;@{ shape: … }&lt;/code&gt; catalog, image nodes, nested subgraphs, &lt;code&gt;style&lt;/code&gt;, &lt;code&gt;classDef&lt;/code&gt;, &lt;code&gt;click&lt;/code&gt;, &lt;code&gt;linkStyle&lt;/code&gt;, link labels, arrow types, curves, animated links and Mermaid's &lt;code&gt;layout: elk&lt;/code&gt; option. &lt;/p&gt;

&lt;p&gt;&lt;/p&gt;

&lt;p&gt;&lt;/p&gt;
  &lt;strong&gt;Can I export the result?&lt;/strong&gt;
  &lt;p&gt;Yes. You can export the diagram as SVG or download the Mermaid source as a .mmd file that works anywhere Mermaid is supported. &lt;/p&gt;

&lt;p&gt;&lt;/p&gt;

&lt;p&gt;&lt;/p&gt;
  &lt;strong&gt;Can I adapt it for other diagram formats?&lt;/strong&gt;
  &lt;p&gt;Yes. Parsing and source editing are separated from rendering, so you can use different formats, such as another Mermaid diagram type or your own DSL, and keep most of the canvas and interaction code. &lt;/p&gt;

&lt;p&gt;&lt;/p&gt;

</description>
      <category>javascript</category>
      <category>react</category>
      <category>tutorial</category>
      <category>mermaid</category>
    </item>
    <item>
      <title>How to get started with JointJS for React</title>
      <dc:creator>Zoran Jambor</dc:creator>
      <pubDate>Thu, 22 Oct 2026 14:31:28 +0000</pubDate>
      <link>https://dev.to/jointjs/how-to-get-started-with-jointjs-for-react-26pf</link>
      <guid>https://dev.to/jointjs/how-to-get-started-with-jointjs-for-react-26pf</guid>
      <description>&lt;p&gt;Whether you’re experimenting, building a proof of concept, or ready for a production-grade app, this roadmap will help you find the right starting point.&lt;/p&gt;

&lt;p&gt;We just released &lt;a href="https://docs.jointjs.com/react" rel="noopener noreferrer"&gt;JointJS for React&lt;/a&gt; and &lt;a href="https://www.jointjs.com/blog/introducing-jointjs-for-react" rel="noopener noreferrer"&gt;shared a bit about who it is for, why it matters, and why you should start using it&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;In this article, you’ll get a practical, hands-on guide on getting started with JointJS for React. We won’t go into nitty-gritty details here, but instead we’ll give you a clear outline of the best ways you can jump into it without much effort.&lt;/p&gt;

&lt;p&gt;We’ll briefly cover:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;How to start from a ready-made demo (&lt;a href="https://www.jointjs.com/demos/ai-workflow-builder" rel="noopener noreferrer"&gt;AI Workflow Builder&lt;/a&gt;) and customize it for your project&lt;/li&gt;
&lt;li&gt;Guidance on building your first app step-by-step using the documentation and quickstart guide&lt;/li&gt;
&lt;li&gt;How to use AI Coding Agents to accelerate development, along with quick installation instructions to get you up and running&lt;/li&gt;
&lt;li&gt;A conclusion with tips, next steps, and further resources&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Start with the AI Workflow Builder demo
&lt;/h2&gt;

&lt;p&gt;Instead of building your app from scratch, the best way to start is to use one of the existing JointJS for React demos that’s close to your actual needs, such as AI Workflow Builder, to build your own proof of concept JointJS for React app.&lt;/p&gt;

&lt;p&gt;&lt;iframe src="https://player.vimeo.com/video/1208077455" width="710" height="399"&gt;
&lt;/iframe&gt;
&lt;/p&gt;

&lt;p&gt;First, we’ll set up an AI Workflow Builder demo project in a new folder. You can download it using the JointJS CLI tool:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npx @joint/cli download ai-workflow-editor/react
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Alternatively, you can simply clone the &lt;a href="https://github.com/clientIO/joint-demos/tree/main/ai-workflow-builder" rel="noopener noreferrer"&gt;AI Workflow Builder demo GitHub repository&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Once the demo is downloaded, run &lt;code&gt;npm install&lt;/code&gt; to set up all project dependencies. Note that this demo is built in JointJS+ for React, the commercial extension of the open-source, community-driven library, which installs from the private JointJS npm registry, so you’ll need an access token in the &lt;code&gt;.npmrc&lt;/code&gt; file to install it.&lt;/p&gt;

&lt;p&gt;You can get the access token in our &lt;a href="https://my.jointjs.com/login" rel="noopener noreferrer"&gt;customer portal&lt;/a&gt; or by starting your &lt;a href="https://www.jointjs.com/free-trial" rel="noopener noreferrer"&gt;free trial of JointJS+&lt;/a&gt;. You can find more &lt;a href="https://docs.jointjs.com/learn/help-center/npm-registry/" rel="noopener noreferrer"&gt;info on setting up the access token in the docs&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Once you’ve successfully installed the dependencies, run the npm run start command to start the development server. By default, it will start on localhost, port 5173 (&lt;a href="http://localhost:5173/" rel="noopener noreferrer"&gt;http://localhost:5173/&lt;/a&gt;).&lt;/p&gt;

&lt;p&gt;When you have the demo set up, you can either explore the codebase yourself or ask your AI Coding Agent to give you a detailed overview of the project:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Give me an overview of this JointJS+ for React 
codebase, focusing on JointJS and the key things
I need to know about the app.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;As expected, Claude Code, the AI Agent I’m using for this demo, examined the codebase in detail and provided a clear overview of key concepts and everything you need to dig into it yourself.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F8dldfo3k7kn3wk4njevr.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F8dldfo3k7kn3wk4njevr.png" title="An example output of a prompt requesting an overview of the JointJS for React codebase." alt="An example output of a prompt requesting an overview of the JointJS for React codebase."&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;You can now start tweaking the demo yourself, or better yet, follow up with your AI Coding agent to precisely tweak it to suit your needs.&lt;/p&gt;

&lt;p&gt;I recommend you use the &lt;a href="https://docs.jointjs.com/learn/help-center/mcp-server/" rel="noopener noreferrer"&gt;JointJS MCP Server&lt;/a&gt; to get direct access to the latest, up-to-date JointJS docs. Even without the MCP server, your AI coding agent will be able to figure out precisely what it needs to do by analyzing JointJS code, but using the MCP server is more efficient and will get you better results in the end.&lt;/p&gt;

&lt;p&gt;Also, if you’re using the JointJS MCP Server, you don’t have to manually clone the demo; you can just ask AI to tweak the JointJS for React AI Workflow Builder demo, and AI will set up everything for you.&lt;/p&gt;

&lt;h2&gt;
  
  
  Build your first app step by step by following our quickstart guide
&lt;/h2&gt;

&lt;p&gt;Using AI Coding agents to tweak one of our prebuilt demos is the simplest and most efficient way of starting if you’re building a proof of concept and plan to build a serious, production-grade app.&lt;/p&gt;

&lt;p&gt;But if you want to learn more about JointJS for React, if you want to understand key concepts to be able to review AI agent’s changes and to be able to direct your AI coding agent better, it’s best to get your hands dirty by following our &lt;a href="https://docs.jointjs.com/react/getting-started/" rel="noopener noreferrer"&gt;JointJS for React Quickstart Guide&lt;/a&gt; in the docs.&lt;/p&gt;

&lt;p&gt;The guide is structured into multiple chapters, and each chapter focuses on one key concept, so it’s easy and straightforward to follow:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Install &lt;code&gt;@joint/react&lt;/code&gt;: add the package, import the styles, and size the canvas.&lt;/li&gt;
&lt;li&gt;Build your first diagram: render elements from plain data.&lt;/li&gt;
&lt;li&gt;Add and update elements: read the graph and change it while the app runs.&lt;/li&gt;
&lt;li&gt;Create custom elements: rich, data-driven nodes with ports, styled your way.&lt;/li&gt;
&lt;li&gt;Connect elements with links: arrowheads, labels, and routing.&lt;/li&gt;
&lt;li&gt;Make the canvas interactive: dragging, clicks, and selection.&lt;/li&gt;
&lt;li&gt;Zoom and pan the canvas: an infinite, scrollable, zoomable viewport with JointJS+.&lt;/li&gt;
&lt;li&gt;Add an element palette: drag from a palette onto the canvas.&lt;/li&gt;
&lt;li&gt;Add a property editor: edit the selected node from a live inspector.&lt;/li&gt;
&lt;li&gt;Add an element overlay: React UI anchored to an element, such as a badge, toolbar, or popover.&lt;/li&gt;
&lt;li&gt;Add a minimap: a bird’s-eye view that tracks the canvas.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The quickstart guide is designed to be straightforward and approachable for everyone, clearly outlining all key concepts of the library as you encounter them.&lt;/p&gt;

&lt;p&gt;By the end of the guide, you’ll build a complete workflow editor that includes all the key features you’ll find in flowcharts and diagrams, from palette, canvas, minimap, to a live properties panel, giving you insight into JointJS open-source and JointJS+ commercial features.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fonbkpsfuco6s4bis7b8e.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fonbkpsfuco6s4bis7b8e.png" title="A final state of a diagramming app built by following the JointJS for React quickstart guide." alt="A final state of a diagramming app built by following the JointJS for React quickstart guide."&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Use AI Coding agents to set up your JointJS for React project from scratch
&lt;/h2&gt;

&lt;p&gt;If you want to start completely from scratch, or add JointJS for React to your existing project, install the library directly from NPM. The open-source version of the library is available at @joint/react, and the commercial extension, JointJS+ for React, is available on NPM at &lt;code&gt;@joint/react-plus&lt;/code&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Install JointJS for React through NPM
&lt;/h3&gt;

&lt;p&gt;For this demo, we’ll start from scratch and use the Vite build tool to set up a simple React project:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm create vite@latest jointjs-for-react-demo &lt;span class="nt"&gt;--&lt;/span&gt; &lt;span class="nt"&gt;--template&lt;/span&gt; react-ts
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Next, we need to install JointJS for React, the open-source version of the library. The package is available at &lt;a href="https://www.npmjs.com/package/@joint/react?activeTab=readme" rel="noopener noreferrer"&gt;@joint/react&lt;/a&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm &lt;span class="nb"&gt;install&lt;/span&gt; @joint/react
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This will work with any modern React setup, so you can easily drop it into Vite, Next.js, Remix, and so on. Just as well, you can use JointJS+ for React instead of the open-source version.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm &lt;span class="nb"&gt;install&lt;/span&gt; @joint/react-plus
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For this guide, we’ll use the open-source version of the library, but JointJS+ for React includes everything from JointJS for React and works exactly the same (the only difference being that &lt;code&gt;&amp;lt;GraphProvider /&amp;gt;&lt;/code&gt; in  JointJS+ becomes &lt;code&gt;&amp;lt;Diagram /&amp;gt;&lt;/code&gt;. And of course, it also gives you access to selection, history, clipboard, spatial index, and more), just with an expanded set of features.&lt;/p&gt;

&lt;p&gt;Once our React project is up and running and the JointJS for React package is installed, we'll set up a simple JointJS canvas. The easiest way to start is to use an AI Coding agent to set up the boilerplate project.&lt;/p&gt;

&lt;h3&gt;
  
  
  Set up a basic JointJS canvas with your AI Coding agent
&lt;/h3&gt;

&lt;p&gt;Most developers these days use AI Coding Agents extensively in development, just as they should. And this is precisely where JointJS for React shines. JointJS for React is strongly typed, so your AI Coding agents can easily parse the library and understand exactly how it works and how it can give you the result you want.&lt;/p&gt;

&lt;p&gt;You can feel confident using JointJS for React even if you don’t want to wait for the AI training sets to update and kick in with &lt;a href="https://docs.jointjs.com/react/" rel="noopener noreferrer"&gt;new JointJS for React docs&lt;/a&gt;. Better yet, you can install &lt;a href="https://docs.jointjs.com/learn/help-center/mcp-server/" rel="noopener noreferrer"&gt;JointJS MCP Server&lt;/a&gt;, which provides direct access to up-to-date JointJS docs, including React, to achieve higher-quality code overall.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight markdown"&gt;&lt;code&gt;Replace the React demo and build a @joint/react app
with a canvas graph, paper, and two rectangles I 
can drag. Add one input port on the left and one 
output port on the right to the rectangles, and 
draw a link from the output port of the first 
rectangle to the input port of the second rectangle.

Make the styling slick, modern, and clean with 
plenty of padding.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Note that I’m explicitly mentioning the &lt;em&gt;@joint/react&lt;/em&gt; package so the AI Coding Agent knows exactly what to use. I’m using Claude Code in this article, but this will work with any other setup as well.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fvkg8s1dr5ou5hrj42e37.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fvkg8s1dr5ou5hrj42e37.png" title="An example of a JointJS for React app built with an AI Coding Agent through a single prompt." alt="An example of a JointJS for React app built with an AI Coding Agent through a single prompt."&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;AI Agent set up a simple JointJS for React canvas and configured the app precisely as it needs to:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;GraphProvider&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;Paper&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;HTMLHost&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;linkRoutingSmooth&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kd"&gt;type&lt;/span&gt; &lt;span class="nx"&gt;ElementPort&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kd"&gt;type&lt;/span&gt; &lt;span class="nx"&gt;CellRecord&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@joint/react&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@joint/react/styles.css&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;./App.css&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;App&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;return &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt; &lt;span class="na"&gt;className&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"canvas-page"&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;header&lt;/span&gt; &lt;span class="na"&gt;className&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"canvas-header"&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;h1&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;JointJS + React&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;h1&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;p&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;Drag the rectangles around the canvas&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;p&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;header&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt; &lt;span class="na"&gt;className&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"canvas-frame"&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;GraphProvider&lt;/span&gt; &lt;span class="na"&gt;initialCells&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;initialCells&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
          &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Paper&lt;/span&gt;
            &lt;span class="na"&gt;renderElement&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;RectangleNode&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
            &lt;span class="na"&gt;linkRouting&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nf"&gt;linkRoutingSmooth&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
            &lt;span class="na"&gt;defaultLink&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;style&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;#7c3aed&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;width&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mf"&gt;2.5&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;targetMarker&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;arrow&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
            &lt;span class="na"&gt;gridSize&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="mi"&gt;20&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
            &lt;span class="na"&gt;drawGrid&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;dot&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;args&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;#d8d3e6&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;thickness&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mf"&gt;1.5&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
            &lt;span class="na"&gt;background&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;#faf9fc&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
            &lt;span class="na"&gt;className&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"canvas"&lt;/span&gt;
          &lt;span class="p"&gt;/&amp;gt;&lt;/span&gt;
        &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nc"&gt;GraphProvider&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="nx"&gt;App&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;RectangleNode&lt;/code&gt; function is responsible for turning the element’s data into React UI. It uses HTMLHost so the element can be plain HTML:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;RectangleNode&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;label&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;hint&lt;/span&gt; &lt;span class="p"&gt;}:&lt;/span&gt; &lt;span class="nx"&gt;NodeData&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;return &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;HTMLHost&lt;/span&gt; &lt;span class="na"&gt;useModelGeometry&lt;/span&gt; &lt;span class="na"&gt;className&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"node"&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;span&lt;/span&gt; &lt;span class="na"&gt;className&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"node__hint"&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;hint&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;span&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;span&lt;/span&gt; &lt;span class="na"&gt;className&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"node__label"&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;label&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;span&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nc"&gt;HTMLHost&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And the initial elements, ports, and links are configured in their own constants:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;&lt;span class="kd"&gt;type&lt;/span&gt; &lt;span class="nx"&gt;NodeData&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;label&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;
  &lt;span class="na"&gt;hint&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;ports&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;Record&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;ElementPort&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;in&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;cx&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;cy&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;50%&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;width&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;16&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;height&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;16&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;#ffffff&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;outline&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;#c4b5fd&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;outlineWidth&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;passive&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="na"&gt;out&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;cx&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;100%&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;cy&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;50%&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;width&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;16&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;height&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;16&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;#7c3aed&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;outline&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;#ffffff&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;outlineWidth&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;initialCells&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;CellRecord&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;NodeData&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;[]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
  &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;rect-a&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;element&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;position&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;x&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;120&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;y&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;160&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="na"&gt;size&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;width&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;220&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;height&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;120&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="na"&gt;portMap&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;ports&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;data&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;label&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Rectangle A&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;hint&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Output&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;rect-b&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;element&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;position&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;x&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;560&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;y&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;160&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="na"&gt;size&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;width&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;220&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;height&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;120&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="na"&gt;portMap&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;ports&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;data&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;label&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Rectangle B&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;hint&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Input&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;rect-a-rect-b&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;link&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;source&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;rect-a&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;port&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;out&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="na"&gt;target&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;rect-b&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;port&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;in&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="na"&gt;style&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;#7c3aed&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;width&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mf"&gt;2.5&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;targetMarker&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;arrow&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
&lt;span class="p"&gt;]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;From here, you can use your AI Coding Agent to tweak and extend it according to your precise project needs. Follow up in your initial prompt to get the result you’re looking for.&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;If you want to quickly build a proof of concept for your own use case with JointJS for React, start with the AI Workflow Builder demo and use AI coding agents to tweak it to suit your needs.&lt;/p&gt;

&lt;p&gt;If you want to understand a bit more about how JointJS for React works, follow our &lt;a href="https://docs.jointjs.com/react/getting-started/" rel="noopener noreferrer"&gt;Getting Started Guide&lt;/a&gt; in the docs, and in the few short lessons, you’ll learn everything you need to get confident in building production-grade diagramming and flowchart apps.&lt;/p&gt;

&lt;h3&gt;
  
  
  Ready to get started?
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://docs.jointjs.com/react" rel="noopener noreferrer"&gt;Explore the official JointJS for React documentation for detailed guides and API references&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.jointjs.com/demos/ai-workflow-builder" rel="noopener noreferrer"&gt;Try the AI Workflow Builder demo&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.jointjs.com/free-trial" rel="noopener noreferrer"&gt;Learn more about JointJS+ and start a free trial&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;These resources will help you take your next steps and get the most out of JointJS for React and JointJS+ for React.&lt;/p&gt;

&lt;p&gt;Happy diagramming!&lt;/p&gt;

&lt;p&gt;--&lt;/p&gt;

&lt;h2&gt;
  
  
  FAQ
&lt;/h2&gt;

&lt;p&gt;&lt;/p&gt;
  &lt;strong&gt;What is the recommended way to start building with JointJS for React?&lt;/strong&gt;
  &lt;p&gt;The fastest way is to use the &lt;a href="https://www.jointjs.com/demos/ai-workflow-builder" rel="noopener noreferrer"&gt;AI Workflow Builder demo&lt;/a&gt; as a starting point. Download or clone the demo, install dependencies (you’ll need a JointJS+ access token for the commercial version), and run it locally. This gives you a working proof of concept that you can customize to your needs. &lt;/p&gt;

&lt;p&gt;&lt;/p&gt;

&lt;p&gt;&lt;/p&gt;
  &lt;strong&gt;How do I install JointJS for React in a new or existing project?&lt;/strong&gt;
  &lt;p&gt;For open-source, run &lt;code&gt;npm install @joint/react&lt;/code&gt;. For the commercial extension, run &lt;code&gt;npm install @joint/react-plus&lt;/code&gt; (requires private npm access). Both can be integrated into any modern React setup like Vite, Next.js, or Remix. &lt;/p&gt;

&lt;p&gt;&lt;/p&gt;

&lt;p&gt;&lt;/p&gt;
  &lt;strong&gt;What’s the difference between JointJS for React and JointJS+ for React?&lt;/strong&gt;
  &lt;p&gt;JointJS for React is open-source and provides core diagramming tools. JointJS+ for React is a commercial extension with advanced features (selection, history, clipboard, minimap, spatial index, and more), and uses &lt;code&gt;&amp;lt;Diagram /&amp;gt;&lt;/code&gt; instead of &lt;code&gt;&amp;lt;GraphProvider /&amp;gt;&lt;/code&gt;. &lt;/p&gt;

 &lt;p&gt;&lt;/p&gt;

&lt;p&gt;&lt;/p&gt;
  &lt;strong&gt;Can I use AI Coding Agents with JointJS for React?&lt;/strong&gt;
  &lt;p&gt;Yes! JointJS for React is strongly typed and works well with AI Coding Agents (like Claude Code or GitHub Copilot). Agents can help you set up projects, tweak demos, and generate code. Using the &lt;a href="https://docs.jointjs.com/learn/help-center/mcp-server/" rel="noopener noreferrer"&gt;JointJS MCP Server&lt;/a&gt; gives them direct access to the latest docs for even better results. &lt;/p&gt;

&lt;p&gt;&lt;/p&gt;

&lt;p&gt;&lt;/p&gt;
  &lt;strong&gt;How can I learn the basics and key concepts of JointJS for React?&lt;/strong&gt;
  &lt;p&gt;Follow the &lt;a href="https://docs.jointjs.com/react/getting-started/" rel="noopener noreferrer"&gt;Quickstart Guide&lt;/a&gt; in the official docs. It covers installation, rendering diagrams, custom elements, interactivity, palettes, property editors, overlays, and minimaps—everything needed to build a production-grade workflow editor. &lt;/p&gt;

 &lt;p&gt;&lt;/p&gt;

&lt;p&gt;&lt;/p&gt;
  &lt;strong&gt;Do I need a commercial license to try JointJS for React?&lt;/strong&gt;
  &lt;p&gt;No, the open-source version is free to use. To try advanced features in JointJS+, &lt;a href="https://www.jointjs.com/free-trial" rel="noopener noreferrer"&gt;start a free trial for access to the commercial package and features&lt;/a&gt;. &lt;/p&gt;

&lt;p&gt;&lt;/p&gt;

&lt;p&gt;&lt;/p&gt;
  &lt;strong&gt;Where can I find more resources?&lt;/strong&gt;
  &lt;p&gt;Best ways to continue your journey with JointJS for React are:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://docs.jointjs.com/react" rel="noopener noreferrer"&gt;JointJS for React documentation&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.jointjs.com/demos/ai-workflow-builder" rel="noopener noreferrer"&gt;AI Workflow Builder demo&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://www.jointjs.com/free-trial" rel="noopener noreferrer"&gt;JointJS+ for React free trial&lt;/a&gt; 
&lt;/li&gt;
&lt;/ul&gt;

&lt;br&gt;
&lt;br&gt;
&lt;p&gt;&lt;/p&gt;

</description>
      <category>jointjs</category>
      <category>react</category>
      <category>diagramming</category>
      <category>webdev</category>
    </item>
    <item>
      <title>Introducing JointJS for React: Production-grade diagramming for React applications</title>
      <dc:creator>Zoran Jambor</dc:creator>
      <pubDate>Fri, 16 Oct 2026 12:46:58 +0000</pubDate>
      <link>https://dev.to/jointjs/introducing-jointjs-for-react-production-grade-diagramming-for-react-applications-3bpj</link>
      <guid>https://dev.to/jointjs/introducing-jointjs-for-react-production-grade-diagramming-for-react-applications-3bpj</guid>
      <description>&lt;p&gt;Most diagramming libraries seem promising at first. You can spin up a demo in ten minutes, and it feels like you’ve found the perfect fit. But after a few months, you’re building undo/redo from scratch, fighting to keep the diagram state in sync with your backend, running into roadblocks when customizing UX to your needs, and noticing performance lags as your data grows.&lt;/p&gt;

&lt;p&gt;  &lt;iframe src="https://www.youtube.com/embed/xZ_N3VRR41Q" width="710" height="399"&gt;
  &lt;/iframe&gt;
&lt;br&gt;
Most diagramming libraries take you just far enough to be committed, but fall short when it comes to building a polished, production-ready product, especially on a tight timeline.&lt;/p&gt;

&lt;p&gt;Behind the scenes, the hard part of building a diagramming application isn’t drawing boxes and lines, because every library already does that. The real challenge is supporting the sophisticated interactions users expect (UX/UI), from a modern, intuitive, accessible interface to scalability.&lt;/p&gt;

&lt;p&gt;As your &lt;a href="https://www.jointjs.com/blog/jointjs-performance-overview-testing-diagrams-with-100-000-nodes" rel="noopener noreferrer"&gt;diagrams grow from a few nodes to hundreds or thousands&lt;/a&gt;, performance has to keep up. And if you’re working in React, you want an integration that feels like React, not a constant struggle with side effects and useRefs.&lt;/p&gt;
&lt;h2&gt;
  
  
  What JointJS for React actually is
&lt;/h2&gt;

&lt;p&gt;JointJS has been powering diagramming applications used by millions of people for over a decade, from AI workflow builders and BPMN modelers to industrial automation dashboards, data modeling tools, and much more. Industry leaders worldwide trust it for diagrams that need to work reliably in production.&lt;/p&gt;

&lt;p&gt;The JointJS engine solves hard problems, including smart routing algorithms that avoid node overlap, consistent hit testing at any zoom level, DOM management, exceptional UX, outstanding performance, and memory management in production environments.&lt;/p&gt;

&lt;p&gt;JointJS for React brings that trusted engine directly to React. This isn’t just a wrapper or a clunky compatibility layer, but a truly idiomatic React API built on top of JointJS.&lt;/p&gt;

&lt;p&gt;Diagram state is React state, with a core wrapper component (&lt;code&gt;GraphProvider&lt;/code&gt; / &lt;code&gt;Diagram&lt;/code&gt;) that manages and provides it to child components, directly connecting the JointJS engine with React’s lifecycle. There’s no hidden state, no imperative API calls, and no synchronization headaches between React’s render cycle and the canvas. What’s in your state is exactly what’s on the screen.&lt;/p&gt;

&lt;p&gt;Here’s what a simple JointJS app in React looked like until now, and what it will look like going forward.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Before — JointJS 4.2&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="c1"&gt;// DO NOT USE - Old way of integrating JointJS with React&lt;/span&gt;

&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;useEffect&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;useRef&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;react&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;dia&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;shapes&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@joint/plus&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;JointJs&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;canvas&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;useRef&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;Element&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

   &lt;span class="nf"&gt;useEffect&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
       &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;graph&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;dia&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Graph&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

       &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;paper&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;dia&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Paper&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
           &lt;span class="na"&gt;model&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;graph&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
           &lt;span class="na"&gt;background&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
               &lt;span class="na"&gt;color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;#F8F9FA&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
           &lt;span class="p"&gt;},&lt;/span&gt;
           &lt;span class="na"&gt;frozen&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
           &lt;span class="na"&gt;async&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
           &lt;span class="na"&gt;cellViewNamespace&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;shapes&lt;/span&gt;
       &lt;span class="p"&gt;});&lt;/span&gt;

       &lt;span class="nx"&gt;canvas&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;current&lt;/span&gt;&lt;span class="p"&gt;?.&lt;/span&gt;&lt;span class="nf"&gt;appendChild&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;paper&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;el&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

       &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;rect&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;shapes&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;standard&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Rectangle&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
          &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;standard.Rectangle&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
          &lt;span class="na"&gt;position&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;x&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;y&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;100&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
          &lt;span class="na"&gt;size&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;width&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;height&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;50&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
          &lt;span class="na"&gt;attrs&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;label&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;text&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Hello World&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
       &lt;span class="p"&gt;});&lt;/span&gt;

       &lt;span class="nx"&gt;graph&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;addCell&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;rect&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
       &lt;span class="nx"&gt;paper&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;unfreeze&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

       &lt;span class="k"&gt;return &lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
           &lt;span class="nx"&gt;paper&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;remove&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
       &lt;span class="p"&gt;};&lt;/span&gt;

   &lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="p"&gt;[]);&lt;/span&gt;

   &lt;span class="k"&gt;return &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
       &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt; &lt;span class="na"&gt;className&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"canvas"&lt;/span&gt; &lt;span class="na"&gt;ref&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;canvas&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="p"&gt;/&amp;gt;&lt;/span&gt;
   &lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="nx"&gt;JointJs&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The old approach isn’t wrong, but it’s at odds with React’s model. Setup happens inside a &lt;code&gt;useEffect&lt;/code&gt;, you access Paper through &lt;code&gt;useRef&lt;/code&gt;, and you’re stuck with manual cleanup.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;After — JointJS for React&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="c1"&gt;// New way — JointJS for React&lt;/span&gt;

  &lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;GraphProvider&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;Paper&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@joint/react&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;initialCells&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;element&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;position&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;x&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;y&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;100&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
      &lt;span class="na"&gt;data&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;label&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Hello World&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="p"&gt;];&lt;/span&gt;

  &lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;App&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
      &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;GraphProvider&lt;/span&gt; &lt;span class="na"&gt;initialCells&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;initialCells&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Paper&lt;/span&gt; &lt;span class="na"&gt;className&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"canvas"&lt;/span&gt; &lt;span class="p"&gt;/&amp;gt;&lt;/span&gt;
      &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nc"&gt;GraphProvider&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;JointJS for React fits right in with the rest of your React codebase. You declare what you want, and the framework takes care of the rest. It’s not just more elegant, but actually simpler and more intuitive, too.&lt;/p&gt;

&lt;h2&gt;
  
  
  The difference between JointJS for React and JointJS+ for React
&lt;/h2&gt;

&lt;p&gt;In this article, we’ll focus on JointJS+ for React, so it’s worth noting what the actual differences from the open-source version of the library are. Both versions are based on the JointJS diagramming engine.&lt;/p&gt;

&lt;p&gt;JointJS for React is a free, community-driven diagramming library. It gives you the core SVG + HTML rendering engine, a solid API, and building blocks for flowcharts, UML, and other diagramming applications. (If you give the open-source version a try and find it helpful, &lt;a href="https://github.com/clientIO/joint/tree/master/packages/joint-react" rel="noopener noreferrer"&gt;consider leaving a star on GitHub&lt;/a&gt;. It’s a small thing, but it means a lot to the team – thanks!)) &lt;/p&gt;

&lt;p&gt;JointJS+ for React is a commercial extension built on the same core engine, adding numerous ready-to-use UI plugins for snapping, panning, zooming, tooltips, and context menus, plus advanced shapes/styling options on top of what's in the open-source version. It also adds production-focused components not in JointJS at all, like the CommandManager for unlimited undo/redo history tracking, multi-select, and Snaplines/measurement shapes for precision alignment.&lt;/p&gt;

&lt;p&gt;JointJS+ includes everything in JointJS and builds on top of it, so it's really a question of whether you want to build the UI layer yourself or have the library do the heavy lifting for you. &lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Already a JointJS+ customer?&lt;/strong&gt;&lt;br&gt;
Download the latest package, including JointJS+ for React, &lt;a href="https://docs.jointjs.com/learn/help-center/npm-registry" rel="noopener noreferrer"&gt;using npm&lt;/a&gt; or from our &lt;a href="https://my.jointjs.com/" rel="noopener noreferrer"&gt;customer portal&lt;/a&gt;. If your update subscription expired, r&lt;a href="https://www.jointjs.com/contact-us" rel="noopener noreferrer"&gt;each out to us&lt;/a&gt; for re-activation.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;New to JointJS+?&lt;/strong&gt; &lt;br&gt;
Start a &lt;a href="https://www.jointjs.com/free-trial" rel="noopener noreferrer"&gt;30-day no-commitment trial&lt;/a&gt;, which now includes JointJS+ for React.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  AI Workflow Builder with JointJS+ for React
&lt;/h2&gt;

&lt;p&gt;Workflow editors like pipeline designers, approval flows, and data-processing graphs are among the most common diagramming apps teams build, and they’re a great benchmark for any library. They let you quickly see whether a tool handles both the basics and the edge cases.&lt;/p&gt;

&lt;p&gt;Rendering boxes and arrows is easy; what takes significant effort in development are the editing interactions users expect, connection rules that keep the graph semantically valid, configuration options, and performance that holds up as your diagram grows. This demo directly addresses those challenges.&lt;/p&gt;

&lt;p&gt;Live demo: &lt;a href="https://www.jointjs.com/demos/ai-workflow-builder" rel="noopener noreferrer"&gt;https://www.jointjs.com/demos/ai-workflow-builder&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;iframe src="https://player.vimeo.com/video/1208077455" width="710" height="399"&gt;
&lt;/iframe&gt;
&lt;/p&gt;

&lt;p&gt;The same engine powering this AI Workflow Builder also drives BPMN editors, industrial automation (SCADA/HMI), energy network visualization, circuit design, data modeling tools, and more. The domain might change, but the engine’s strengths remain the same. A workflow editor puts most of these capabilities to the test, making it the perfect starting point.&lt;/p&gt;

&lt;h3&gt;
  
  
  1. Diagram basics done right
&lt;/h3&gt;

&lt;p&gt;The diagram nodes in this demo (Input, AI Agent, Tool, Output) aren’t just rectangular boxes—they’re fully configurable UI components. Each node can render arbitrary HTML content, including headers, icons, text areas, select boxes, sliders, and other interactive controls. Combined with visual cues like colored icons and port legends, the graph remains easy to scan and understand at a glance.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;&lt;span class="c1"&gt;// src/components/nodes/render-node.tsx&lt;/span&gt;

&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;NodeBody&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;data&lt;/span&gt; &lt;span class="p"&gt;}:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nl"&gt;data&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;ConfigNodeData&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
 &lt;span class="k"&gt;switch &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;kind&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
   &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;input&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;TextInputBody&lt;/span&gt; &lt;span class="na"&gt;data&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt; &lt;span class="p"&gt;/&amp;gt;;&lt;/span&gt;
   &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;skill&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;SkillBody&lt;/span&gt; &lt;span class="na"&gt;data&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt; &lt;span class="p"&gt;/&amp;gt;;&lt;/span&gt;
   &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;agent&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;AgentBody&lt;/span&gt; &lt;span class="na"&gt;data&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt; &lt;span class="p"&gt;/&amp;gt;;&lt;/span&gt;
   &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;tool&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;   &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;ToolBody&lt;/span&gt; &lt;span class="na"&gt;data&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt; &lt;span class="p"&gt;/&amp;gt;;&lt;/span&gt;
   &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;output&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;OutputBody&lt;/span&gt; &lt;span class="na"&gt;data&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt; &lt;span class="p"&gt;/&amp;gt;;&lt;/span&gt;
 &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  2. Editing interactions
&lt;/h3&gt;

&lt;p&gt;Undo/redo, copy/paste, multi-select, keyboard shortcuts, snap-to-grid are all essential features users expect from serious apps. If you’ve ever tried building them from scratch, you know they’re far from easy to get right.&lt;/p&gt;

&lt;p&gt;In this demo, &lt;code&gt;history&lt;/code&gt; and &lt;code&gt;clipboard&lt;/code&gt; are single props on the root component:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;&lt;span class="c1"&gt;// src/app.tsx&lt;/span&gt;

&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Diagram&lt;/span&gt; &lt;span class="na"&gt;cells&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;cells&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt; &lt;span class="na"&gt;onCellsChange&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;setCells&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt; &lt;span class="na"&gt;history&lt;/span&gt; &lt;span class="na"&gt;clipboard&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The real value isn’t just that these features exist, but that they work together seamlessly. Undo correctly reverses a paste, multi-select moves as a group and lands on the undo stack as a single action, while &lt;a href="https://docs.jointjs.com/api/ui/Snaplines/" rel="noopener noreferrer"&gt;Snaplines&lt;/a&gt; (a JointJS feature for aligning elements) work no matter how a node was placed. Only a library that builds these features into the engine can guarantee they’ll play nicely together.&lt;/p&gt;

&lt;h3&gt;
  
  
  3. The data model
&lt;/h3&gt;

&lt;p&gt;This is what matters in serious, production-grade applications, and in JointJS+ for React, the diagram state is plain React state. Just as you’re used to working with React, Diagram can take data in either uncontrolled or controlled state. This demo showcases the controlled state.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;&lt;span class="c1"&gt;// src/app.tsx&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;cells&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;setCells&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;useState&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="k"&gt;readonly&lt;/span&gt; &lt;span class="nx"&gt;WorkflowCell&lt;/span&gt;&lt;span class="p"&gt;[]&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;INITIAL_CELLS&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Diagram&lt;/span&gt; &lt;span class="na"&gt;cells&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;cells&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt; &lt;span class="na"&gt;onCellsChange&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;setCells&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt; &lt;span class="na"&gt;history&lt;/span&gt; &lt;span class="na"&gt;clipboard&lt;/span&gt; &lt;span class="p"&gt;/&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The controlled cells array is the single source of truth. It’s rendered on the &lt;code&gt;&amp;lt;Diagram /&amp;gt;&lt;/code&gt;, while every user action fires &lt;code&gt;onCellsChange&lt;/code&gt; with an updated array. Because the graph is just a serializable data structure, saving, loading, and processing are trivial.&lt;/p&gt;

&lt;p&gt;Note that the state hooks in JointJS for React behave identically in JointJS and JointJS+, but in JointJS+, GraphProvider becomes Diagram, which also gives you access to selection, history, clipboard, spatial index, and more.&lt;/p&gt;

&lt;h3&gt;
  
  
  4. Layout and organization
&lt;/h3&gt;

&lt;p&gt;Diagrams that developers create by hand often drift toward visual noise. The auto-arrange option resolves this in a single action, in which a layout algorithm runs over the graph and repositions every node cleanly.&lt;/p&gt;

&lt;p&gt;At the canvas/paper level, there are two available modes: an infinite-scrollable canvas for open-ended building, and a fixed sheet mode that restricts the diagram's canvas to specific, page-like boundaries, which is ideal for exportable documents such as flowcharts, organization charts, or diagrams that must fit strict corporate reporting templates.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fv004q1fy0njuzlaitvi8.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fv004q1fy0njuzlaitvi8.png" alt="A screenshot of JointJS for React app canvas options." width="799" height="409"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The minimap in the bottom-right corner becomes vital as soon as the diagram grows beyond the viewport size. In JointJS+ for React, this is just one of the components you can plug into your application with a few lines of code.&lt;/p&gt;

&lt;h2&gt;
  
  
  he purpose of AI Workflow Builder demo
&lt;/h2&gt;

&lt;p&gt;This demo clearly demonstrates what a serious AI workflow editor looks like when it’s built on a complete diagramming engine.&lt;/p&gt;

&lt;p&gt;Every feature shown here, from typed connections, undo/redo, clipboard, multi-select, auto-layout, export, to minimap, is available on day one as a core feature of the JointJS library.&lt;/p&gt;

&lt;p&gt;JointJS handles the hard parts out of the box, so you can focus on building your application logic instead of spending months implementing core library features.&lt;/p&gt;

&lt;h2&gt;
  
  
  Key capabilities of JointJS+ for React — What you get out of the box
&lt;/h2&gt;

&lt;p&gt;JointJS+ for React provides a valuable set of features for production-ready diagrams, not just demos. These capabilities make JointJS+ for React a true foundation for serious diagramming applications, eliminating the need to reinvent critical features and assuring your product is ready for actual real-world demands.&lt;/p&gt;

&lt;h3&gt;
  
  
  SVG-based rendering with full custom shape support
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://www.jointjs.com/blog/svg-versus-canvas" rel="noopener noreferrer"&gt;JointJS uses a combination of SVG and HTML as its rendering engine&lt;/a&gt;, which means your diagrams will remain crisp at any zoom level, while the added benefit of SVG being accessible means your applications will meet necessary accessibility standards more easily.&lt;/p&gt;

&lt;p&gt;On top of that, in JointJS you’re never limited to built-in shapes. You can easily create any kinds of elements. Nodes can use SVG or HTML, with wrapped labels, interactive inputs, and live data. All JointJS elements work flawlessly with ports, links, and selection, so you’ll never have to resort to workarounds.&lt;/p&gt;

&lt;h3&gt;
  
  
  Built-in virtual rendering for large graph performance
&lt;/h3&gt;

&lt;p&gt;JointJS comes with extensive options that let you configure it for scalability and performance, but it ships with conservative defaults, primarily for backward compatibility. JointJS+ for React, on the other hand, doesn’t come with this baggage, so all advanced JointJS performance options like &lt;code&gt;async&lt;/code&gt;, &lt;code&gt;autoFreeze&lt;/code&gt;, and &lt;code&gt;viewManagement&lt;/code&gt; you’d want in a large-scale application are enabled by default.&lt;/p&gt;

&lt;p&gt;All of this means that instead of thinking about what you need to do to make your large apps performant, you can just start building with confidence, knowing that everything is already set up for you, even if you plan to build diagrams with thousands of nodes.&lt;/p&gt;

&lt;h3&gt;
  
  
  Exceptional user experience (UX)
&lt;/h3&gt;

&lt;p&gt;JointJS+ for React is built to deliver a frustration-free diagramming experience for both end users and developers. The library provides intuitive drag-and-drop interactions, smooth editing, instant feedback, and advanced features such as minimap, smooth zoom controls, different routers, and more.&lt;/p&gt;

&lt;h3&gt;
  
  
  Domain-specific shape libraries
&lt;/h3&gt;

&lt;p&gt;Get fully featured, standards-compliant shapes for BPMN 2.0, value stream mapping, data modeling (records/tables), and charts.&lt;/p&gt;

&lt;p&gt;These shapes aren’t just icons, but they include the correct ports, labels, and connection rules for each domain, allowing you to build real-life applications without starting from scratch.&lt;/p&gt;

&lt;h3&gt;
  
  
  Comprehensive layout algorithms
&lt;/h3&gt;

&lt;p&gt;JointJS+ for React comes with built-in tree, force-directed, grid, and stack layouts, while also allowing seamless integration with advanced third-party layout engines such as ELK and Dagre. Easily auto-arrange and reorganize complex diagrams.&lt;/p&gt;

&lt;p&gt;These algorithms make it easy to auto-arrange nodes, keep diagrams readable, and adapt to model changes, saving you weeks of custom development.&lt;/p&gt;

&lt;h3&gt;
  
  
  Rich interaction model
&lt;/h3&gt;

&lt;p&gt;Undo/redo, copy/paste, multi-select, keyboard shortcuts, snap-to-grid, and alignment guides work together out of the box, intuitively.&lt;/p&gt;

&lt;p&gt;None of these are bolted on separately and hoped to cooperate: undo correctly reverses a paste, a multi-select move lands on the undo stack as a single action, and snap-to-grid/alignment guides apply no matter how the node got there, be it by dragging, pasting, or dropping it from the stencil.&lt;/p&gt;

&lt;h3&gt;
  
  
  Rich interaction model
&lt;/h3&gt;

&lt;p&gt;Undo/redo, copy/paste, multi-select, keyboard shortcuts, snap-to-grid, and alignment guides work together out of the box, intuitively.&lt;/p&gt;

&lt;p&gt;None of these are bolted on separately and hoped to cooperate: undo correctly reverses a paste, a multi-select move lands on the undo stack as a single action, and snap-to-grid/alignment guides apply no matter how the node got there, be it by dragging, pasting, or dropping it from the stencil.&lt;/p&gt;

&lt;h3&gt;
  
  
  Effortless export options
&lt;/h3&gt;

&lt;p&gt;In JointJS+ for React, you can easily export diagrams as SVG, PNG, or PDF. What users see on screen is exactly what they get in exports, so you won’t need to implement separate rendering logic. Canvas (paper) options allow you to visualize fixed sheets and page breaks, which is key if you need specific dimensions of diagrams and flowcharts for your documents and presentations.&lt;/p&gt;

&lt;h2&gt;
  
  
  Who is JointJS+ for React built for
&lt;/h2&gt;

&lt;p&gt;JointJS+ for React is designed for teams building workflow automation tools, BPMN modelers, data pipeline editors, network topology visualizers, circuit design tools, or any application where diagramming is a core product feature. It’s built for cases where users need robust editing, undo/redo, easy export, and all of this delivered with polished UX and production-grade scalability. When your requirements go beyond simple visuals, and you need a complete, production-ready foundation, JointJS+ for React delivers everything out of the box.&lt;/p&gt;

&lt;p&gt;Simpler libraries are fine for simple needs, but as soon as requirements get serious, whether in performance, intuitive and accessible UX, scalability, or configuration options, you quickly hit a ceiling. At that point, you either have to build the missing pieces yourself or start with the right foundation from day one. Not anymore, because this is precisely the gap that JointJS+ for React fills.&lt;/p&gt;

&lt;h2&gt;
  
  
  Getting started with JointJS+ for React
&lt;/h2&gt;

&lt;p&gt;The easiest way to get started with JointJS+ for React is to jump into our &lt;a href="https://www.jointjs.com/demos/ai-workflow-builder" rel="noopener noreferrer"&gt;AI Workflow Builder demo&lt;/a&gt;. The source is fully available on GitHub, so you can clone the repo, run &lt;code&gt;npm install&lt;/code&gt;, and you’re ready to tweak and customize it, either by hand, if you want to dig into the code yourself, or with your AI Coding Agent, be it Claude Code, Codex, Cursor, or any other model you might be using.&lt;/p&gt;

&lt;p&gt;All the principles that apply to &lt;a href="https://www.jointjs.com/blog/tutorial-customize-jointjs-demo-app-with-claude-code" rel="noopener noreferrer"&gt;AI Coding Agents and JointJS&lt;/a&gt; also apply to JointJS+ for React. That means you can use your AI Coding Agent and the AI Workflow Builder demo as a great starting point for your own proof of concept, or even project. The documentation is already up to date, so our JointJS MCP Server will help you get started right away, without having to wait for new AI training sets to kick in.&lt;/p&gt;

&lt;h3&gt;
  
  
  Learn more about JointJS+ for React.
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="http://docs.jointjs.com/react" rel="noopener noreferrer"&gt;Explore the docs →&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.jointjs.com/demos/ai-workflow-builder" rel="noopener noreferrer"&gt;Try the AI Workflow Builder demo →&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.jointjs.com/free-trial" rel="noopener noreferrer"&gt;Start your free JointJS+ trial&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Happy diagramming! 👋&lt;/p&gt;

&lt;p&gt;-&lt;/p&gt;

&lt;h2&gt;
  
  
  FAQ
&lt;/h2&gt;

&lt;p&gt;&lt;/p&gt;
  &lt;strong&gt;What is JointJS for React?&lt;/strong&gt;
  &lt;p&gt;JointJS for React is a production-grade diagramming library designed for React applications. It provides a truly idiomatic React API, connecting the trusted JointJS engine directly with React’s lifecycle without the need for wrappers or workarounds.&lt;/p&gt;

&lt;p&gt;&lt;/p&gt;

&lt;p&gt;&lt;/p&gt;
  &lt;strong&gt;How is JointJS+ for React different from other diagramming libraries?&lt;/strong&gt;
  &lt;p&gt;JointJS+ for React handles everything a serious diagramming library needs, from the essentials to advanced features like undo/redo, copy/paste, multi-select, snap-to-grid, and direct integration with React state.&lt;/p&gt;

&lt;p&gt;Performance as well as scalability are built in from day one, even for large diagrams. On top of this, the library provides superior UX features that will help you create intuitive and usable apps.&lt;/p&gt;

&lt;p&gt;&lt;/p&gt;

&lt;p&gt;&lt;/p&gt;
  &lt;strong&gt;What kinds of apps is JointJS+ for React best suited for?&lt;/strong&gt;
  &lt;p&gt;JointJS+ for React is ideal for AI workflow editors, BPMN modelers, industrial automation dashboards, network visualizations, circuit design tools, and any application where diagramming is a core feature and enterprise-level reliability is required. &lt;/p&gt;

&lt;p&gt;&lt;/p&gt;

&lt;p&gt;&lt;/p&gt;
  &lt;strong&gt;What are the key features included out of the box in JointJS+ for React?&lt;/strong&gt;
  &lt;p&gt;JointJS+ for React includes a number of advanced features:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;SVG-based rendering with full custom shape support&lt;/li&gt;
&lt;li&gt;High performance for large diagrams (virtual rendering, async updates)&lt;/li&gt;
&lt;li&gt;Comprehensive suite of out-of-the-box user experience (UX) and User Interface (UI) features&lt;/li&gt;
&lt;li&gt;Built-in rich interactions (undo/redo, clipboard, snap-to-grid, keyboard shortcuts)&lt;/li&gt;
&lt;li&gt;Comprehensive layout algorithms (tree, force-directed, grid, and stack layouts)&lt;/li&gt;
&lt;li&gt;Domain-specific shapes (BPMN, value stream, data modeling, and more)&lt;/li&gt;
&lt;li&gt;Effortless export (SVG, PNG, PDF)&lt;/li&gt;
&lt;li&gt;TypeScript-first API&lt;/li&gt;
&lt;/ul&gt;




&lt;p&gt;&lt;/p&gt;
&lt;p&gt;&lt;/p&gt;
  &lt;strong&gt;Is there a live demo or starter project?&lt;/strong&gt;
  &lt;p&gt;Yes! There is an &lt;a href="https://www.jointjs.com/demos/ai-workflow-builder" rel="noopener noreferrer"&gt;AI Workflow Builder application template&lt;/a&gt; with full source code available. You can explore, clone, and customize it as a starting point for your project. &lt;/p&gt;

&lt;p&gt;&lt;/p&gt;

&lt;p&gt;&lt;/p&gt;
  &lt;strong&gt;How do I get started?&lt;/strong&gt;
  &lt;p&gt;Check out the &lt;a href="https://docs.jointjs.com/react" rel="noopener noreferrer"&gt;documentation&lt;/a&gt; or try the &lt;a href="https://www.jointjs.com/demos/ai-workflow-builder" rel="noopener noreferrer"&gt;live demo&lt;/a&gt;. New to JointJS+? &lt;a href="https://www.jointjs.com/free-trial" rel="noopener noreferrer"&gt;Start a 30-day no-commitment trial&lt;/a&gt;, which now includes JointJS+ for React. If you're already a JointJS+ customer, download the latest package &lt;a href="https://docs.jointjs.com/learn/help-center/npm-registry" rel="noopener noreferrer"&gt;using npm&lt;/a&gt; or from our &lt;a href="https://my.jointjs.com/" rel="noopener noreferrer"&gt;customer portal&lt;/a&gt;. If your update subscription expired, &lt;a href="https://www.jointjs.com/contact-us" rel="noopener noreferrer"&gt;reach out to us&lt;/a&gt; for re-activation. &lt;/p&gt;

&lt;p&gt;&lt;/p&gt;

</description>
      <category>react</category>
      <category>typescript</category>
      <category>webdev</category>
      <category>softwaredevelopment</category>
    </item>
  </channel>
</rss>
