Your First Diagram
Time to render something. This is a complete, runnable diagram: three elements joined by links. You should see three cards you can drag, with the links staying attached. These are the first steps of the support-ticket workflow you'll grow across the rest of the guide, all the way to the editor on the landing page.
- JointJS
- JointJS+
import { GraphProvider, Paper, HTMLBox } from '@joint/react'; import '@joint/react/styles.css'; import './example.css'; // 1. Describe the diagram as data. const linkStyle = { targetMarker: 'arrow' } as const; const initialCells = [ { id: 'a', type: 'element', position: { x: 140, y: 110 }, data: { label: 'New Ticket' } }, { id: 'b', type: 'element', position: { x: 350, y: 110 }, data: { label: 'Classify' } }, { id: 'c', type: 'element', position: { x: 560, y: 110 }, data: { label: 'Send Reply' } }, // Links connect two elements by id — covered in "Connecting Elements". { id: 'a→b', type: 'link', source: { id: 'a' }, target: { id: 'b' }, style: linkStyle }, { id: 'b→c', type: 'link', source: { id: 'b' }, target: { id: 'c' }, style: linkStyle }, ]; // 2. Render one element. It receives that element's `data` as props. function Element({ label }: { label: string }) { return <HTMLBox>{label}</HTMLBox>; } // 3. Wire it together. Paper is sized with CSS (the `.canvas` class). export default function App() { return ( <GraphProvider initialCells={initialCells}> <Paper renderElement={Element} className="canvas" /> </GraphProvider> ); }
Press Edit to open the code and change a label. The canvas updates live.
How it works​
This is the same three-part shape as the starter in Installation (data, a renderer, and the provider plus canvas), now with a full walkthrough. Three steps, top to bottom.
1. Describe the diagram as data​
A diagram is an array of cells. Each cell is a plain object with a type of either 'element' (a node) or 'link' (a connection).
const initialCells = [
{ id: 'a', type: 'element', position: { x: 40, y: 110 }, data: { label: 'New Ticket' } },
{ id: 'b', type: 'element', position: { x: 250, y: 110 }, data: { label: 'Classify' } },
// ...
];
id: a unique string. Links reference elements by this id.position: where the element sits, in canvas coordinates. The origin point of the element is its top-left corner.data: anything you want. This is your payload, and it is what your component receives.
There is no size here: when an element renders as HTML, @joint/react measures it and sizes the element for you.
JointJS calls a box an element and a connection a link. That is the vocabulary in the API (type: 'element', renderElement) and across these docs. You will see "node" used informally elsewhere for the same idea.
2. Render an element​
renderElement turns a cell's data into React. It receives that data as props, so you destructure the fields you need. Here it reads the label and wraps it in HTMLHost:
function Element({ label }: { label: string }) {
return <HTMLHost className="node">{label}</HTMLHost>;
}
dataas props.@joint/reactrenders your component as<Element {...data} />, passing only the element'sdataslice. It re-runs only when its owndatachanges; dragging and resizing never re-run your renderer. When you need more thandata(the element's id, position, or size), reach foruseCell(...)inside the renderer, covered in Accessing State.HTMLHostlets you render any HTML or React markup as the element, and measures it to auto-size the element. That is why the cell above needs nowidth/height. JointJS draws in SVG, so under the hoodHTMLHostwraps your<div>in an SVG<foreignObject>. Without it, you'd have to write that<foreignObject>and set the element's size yourself, or draw the node as raw SVG. More in Custom Elements.
JointJS provides a default styled element called HTMLBox that wraps your content in a div with styles applied. You can use it instead of HTMLHost if you want a quick start.
function Element({ label }: { label: string }) {
return <HTMLBox>{label}</HTMLBox>;
}
3. Wire it together​
<GraphProvider initialCells={initialCells}>
<Paper renderElement={Element} className="canvas" />
</GraphProvider>
GraphProviderowns the data.initialCellsseeds it (uncontrolled mode, so the graph manages its own state from here).Paperdraws the canvas and is told how to render an element. It is sized by the.canvasclass (height: 100%); remember, Paper takes its size from CSS.
When using JointJS+ React, <Diagram> is a drop-in upgrade of <GraphProvider> that also owns the diagram-level models the Plus features read. Enable each with a prop:
<Diagram initialCells={initialCells} history clipboard>
…
</Diagram>
Recap​
The core loop: data in initialCells → renderElement → an interactive canvas. Everything else is making elements richer and the canvas smarter.
Next, learn to read and change that data while the app runs.