# React quick start

Mount a controlled canvas with two connected nodes, and keep its instance in a ref for a Fit view button.

Framework bindings are thin skins over one headless model: specs describe your intent, live models hold the data, and the engine owns behavior.

## Prerequisites

Use a browser-based React project with TypeScript and JSX configured, React and React DOM 17, 18 or 19 (18 or 19 for the mounting code below), and the binding's `@grafloria/element` `^0.5.0` peer; see [JavaScript quick start](https://bench-grafloria-61d.atloria.app/p/bench-grafloria-61d-mQCmWpDrxv/developer/javascript-quick-start#prerequisites) for the shared engine and renderer prerequisites.

## 1. Install the binding and its peers

Run this in your application's directory:

```bash
npm install @grafloria/react @grafloria/engine @grafloria/renderer @grafloria/element react react-dom
```

## 2. Create the controlled canvas

Render [`GrafloriaFlow`](https://bench-grafloria-61d.atloria.app/p/bench-grafloria-61d-mQCmWpDrxv/developer/grafloria-react#grafloriaflow) with typed [`NodeSpec`](https://bench-grafloria-61d.atloria.app/p/bench-grafloria-61d-mQCmWpDrxv/developer/grafloria-renderer-instance-nodespec#nodespec) and [`EdgeSpec`](https://bench-grafloria-61d.atloria.app/p/bench-grafloria-61d-mQCmWpDrxv/developer/grafloria-renderer-instance-edgespec#edgespec) data. The edge's `source` and `target` identify the two nodes.

[`useNodesState`](https://bench-grafloria-61d.atloria.app/p/bench-grafloria-61d-mQCmWpDrxv/developer/grafloria-react#usenodesstate) and [`useEdgesState`](https://bench-grafloria-61d.atloria.app/p/bench-grafloria-61d-mQCmWpDrxv/developer/grafloria-react#useedgesstate) each return a three-element tuple: the specs, a state setter and a canvas change handler. Pass the third element to the corresponding change prop to mirror canvas edits into React state.

Store the [`DiagramInstance`](https://bench-grafloria-61d.atloria.app/p/bench-grafloria-61d-mQCmWpDrxv/developer/grafloria-renderer-instance-diagraminstance#diagraminstance) received by `onInit` in a React ref. The button calls `fitView()` on that mounted instance to frame the content; the `fitView` prop also frames it at startup. `plugins` adds the shipped minimap, zoom/fit controls and background grid.

```tsx title="src/App.tsx"
import { useRef } from 'react';
import {
  GrafloriaFlow,
  useNodesState,
  useEdgesState,
} from '@grafloria/react';
import type {
  DiagramInstance,
  NodeSpec,
  EdgeSpec,
} from '@grafloria/react';

const initialNodes: NodeSpec[] = [
  {
    id: 'a',
    position: { x: 60, y: 80 },
    size: { width: 180, height: 80 },
    data: { label: 'Ingest' },
  },
  {
    id: 'b',
    position: { x: 380, y: 80 },
    size: { width: 180, height: 80 },
    data: { label: 'Publish' },
  },
];

const initialEdges: EdgeSpec[] = [
  { id: 'e1', source: 'a', target: 'b' },
];

export default function App() {
  const instanceRef = useRef<DiagramInstance | null>(null);
  const [nodes, , onNodesChange] = useNodesState(initialNodes);
  const [edges, , onEdgesChange] = useEdgesState(initialEdges);

  return (
    <main>
      <button
        type="button"
        onClick={() => instanceRef.current?.fitView()}
      >
        Fit view
      </button>
      <div style={{ width: '100%', height: 400 }}>
        <GrafloriaFlow
          nodes={nodes}
          edges={edges}
          onNodesChange={onNodesChange}
          onEdgesChange={onEdgesChange}
          onInit={(instance) => { instanceRef.current = instance; }}
          fitView
          plugins
        />
      </div>
    </main>
  );
}
```

![Ingest connects to Publish across the dotted canvas. Look for the Fit view button above it, zoom/fit controls at the lower left and the minimap at the lower right.](https://atloriaassets.blob.core.windows.net/assets/a438221c-c1af-407c-b9e6-a56c986db846/41e1adc1ca007ff858e4a059892bebbc.png)

The canvas occupies the 400-pixel-high wrapper. For other sizing arrangements, see [Theme a canvas](https://bench-grafloria-61d.atloria.app/p/bench-grafloria-61d-mQCmWpDrxv/developer/theme-a-canvas).

### Keep the controlled return path wired

Passing controlled `nodes` without `onNodesChange` lets stale React state overwrite user edits. Pass the hook's third element, not its `setNodes` setter: the callback receives live [`NodeModel`](https://bench-grafloria-61d.atloria.app/p/bench-grafloria-61d-mQCmWpDrxv/developer/grafloria-engine-models-nodemodel#nodemodel) objects, while the setter accepts `NodeSpec[]` or a state updater. The hook converts those live models back to specs. The second tuple element is omitted in this sample because it does not make application-originated edits.

`onInit` runs after the instance is created. The binding disposes that instance when the flow unmounts; do not dispose it after initialization.

## 3. Mount the app

For a React 18 or 19 client entry, render `App` into your HTML root element:

```tsx title="src/main.tsx"
import { createRoot } from 'react-dom/client';
import App from './App';

const container = document.getElementById('root');
if (!container) throw new Error('Missing #root element');

createRoot(container).render(<App />);
```

Use the HTML entry structure from [JavaScript quick start](https://bench-grafloria-61d.atloria.app/p/bench-grafloria-61d-mQCmWpDrxv/developer/javascript-quick-start#1-give-the-canvas-a-size), replacing its editor markup and script with the React root and TSX entry:

```html title="index.html"
<div id="root"></div>
<script type="module" src="/src/main.tsx"></script>
```

This HTML entry fits a Vite React TypeScript project. If your framework already mounts `App`, keep its entry point instead.

Start your application's development server and open it in the browser. You now have Ingest and Publish nodes joined by an edge, with a Fit view button above the canvas. Drag a node to move it; the change handler mirrors the edit into React state. Pan or zoom, then click Fit view to frame both nodes again.

## Where next

- Try the [live React starter](https://stackblitz.com/github/grafloria/grafloria/tree/main/starters/react?file=src/main.jsx), or explore the [React demo gallery](https://grafloria.com/demos-react/).
- Read [React: state and subscriptions](https://bench-grafloria-61d.atloria.app/p/bench-grafloria-61d-mQCmWpDrxv/developer/react-state-and-subscriptions) for application updates and subscriptions.
- Use [React: custom content](https://bench-grafloria-61d.atloria.app/p/bench-grafloria-61d-mQCmWpDrxv/developer/react-custom-content) to render your own node components.
- Read [Instance and lifecycle](https://bench-grafloria-61d.atloria.app/p/bench-grafloria-61d-mQCmWpDrxv/developer/instance-and-lifecycle) for instance ownership beyond this component.
