Skip to main content
Version: 4.3

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.

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.

Element, not "node"

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>;
}
  • data as props. @joint/react renders your component as <Element {...data} />, passing only the element's data slice. It re-runs only when its own data changes; dragging and resizing never re-run your renderer. When you need more than data (the element's id, position, or size), reach for useCell(...) inside the renderer, covered in Accessing State.
  • HTMLHost lets 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 no width/height. JointJS draws in SVG, so under the hood HTMLHost wraps 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>
  • GraphProvider owns the data. initialCells seeds it (uncontrolled mode, so the graph manages its own state from here).
  • Paper draws the canvas and is told how to render an element. It is sized by the .canvas class (height: 100%); remember, Paper takes its size from CSS.
JointJS+

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 initialCellsrenderElement → 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.