# How Grafloria works

Grafloria is one headless diagram model with framework bindings around it: the binding mounts the diagram, the instance connects its rendered canvas to its data and engine, and the renderer turns model intent into pixels.

## One headless model, every framework

Use the binding that fits your application. Each example mounts the same two-node flow; drag a node or connect nodes in the resulting canvas, regardless of framework.

### React

For a runnable React flow and its initial-data props, see the [React quick start](https://bench-grafloria-6l.atloria.app/p/bench-grafloria-6l-dqcsEdUbfS/developer/react-quick-start). The framework component mounts the shared model rather than a React-specific diagram model.

### Vue

For a runnable Vue flow and its initial-data props, see the [Vue quick start](https://bench-grafloria-6l.atloria.app/p/bench-grafloria-6l-dqcsEdUbfS/developer/vue-quick-start). Vue also mounts the shared model; the binding changes how you express the component, not the diagram's underlying model.

### Qwik

Qwik renders the component on the server and resumes it in the browser; follow the [Qwik quick start](https://bench-grafloria-6l.atloria.app/p/bench-grafloria-6l-dqcsEdUbfS/developer/qwik-quick-start) for a runnable component setup.

### Angular

Angular uses [`DiagramCanvasComponent`](https://bench-grafloria-6l.atloria.app/p/bench-grafloria-6l-dqcsEdUbfS/developer/grafloria-angular-components-diagramcanvascomponent#diagramcanvascomponent) with two-way node and edge bindings. The canvas displays the same flow, and user edits update the bound arrays.

```ts
import { Component } from '@angular/core';
import { DiagramCanvasComponent } from '@grafloria/angular';
import type { EdgeSpec, NodeSpec } from '@grafloria/renderer';

@Component({
  selector: 'app-root',
  imports: [DiagramCanvasComponent],
  template: `
    <grafloria-diagram-canvas
      [(nodes)]="nodes"
      [(edges)]="edges"
      style="display: block; height: 400px" />
  `,
})
export class AppComponent {
  nodes: NodeSpec[] = [
    { id: 'ingest', position: { x: 60, y: 80 }, size: { width: 180, height: 80 }, label: 'Ingest' },
    { id: 'publish', position: { x: 380, y: 80 }, size: { width: 180, height: 80 }, label: 'Publish' },
  ];
  edges: EdgeSpec[] = [{ id: 'flow', source: 'ingest', target: 'publish' }];
}
```

Each binding translates its convenient spec inputs into the same live model. The model is headless: it describes the diagram without depending on React, Vue, Qwik, Angular, or a DOM. That is why the same data and engine behavior make sense in each binding.

## Two layers, one truth

The diagram owns data; the engine owns behavior. A [`DiagramInstance`](https://bench-grafloria-6l.atloria.app/p/bench-grafloria-6l-dqcsEdUbfS/developer/grafloria-renderer-instance-diagraminstance#diagraminstance) is the renderer-level facade between them and the canvas.

```mermaid
flowchart LR
  Specs["Framework specs"] --> Model["DiagramModel: nodes, links, groups, viewport"]
  Engine["DiagramEngine: commands, history, layout, validation"] --> Model
  Model --> Instance["DiagramInstance: canvas and model facade"]
  Instance --> Pixels["Renderer: visible diagram"]
```

For plain JavaScript, [`render`](https://bench-grafloria-6l.atloria.app/p/bench-grafloria-6l-dqcsEdUbfS/developer/grafloria-element-core#render) mounts a spec into a real, sized host and returns that same kind of instance. This complete browser example renders two boxes, fits them into view, and reads the live model's document:

```ts
import { render } from '@grafloria/element';
import type { EdgeSpec, NodeSpec } from '@grafloria/renderer';

const nodes: NodeSpec[] = [
  { id: 'ingest', position: { x: 60, y: 80 }, size: { width: 180, height: 80 }, label: 'Ingest' },
  { id: 'publish', position: { x: 380, y: 80 }, size: { width: 180, height: 80 }, label: 'Publish' },
];
const edges: EdgeSpec[] = [{ id: 'flow', source: 'ingest', target: 'publish' }];

const container = document.createElement('div');
container.style.height = '400px';
document.body.append(container);

const instance = render({ nodes, edges }, container);
instance.fitView();

const diagram = instance.getModel();
const engine = instance.getEngine();
const activeDiagram = engine.getDiagram();
if (activeDiagram === null) {
  throw new Error('The engine has no active diagram.');
}
const savedDocument = activeDiagram.serialize();
const savedJson = JSON.stringify(savedDocument);
```

The canvas shows the two labeled boxes and their edge. `getModel()` returns the live [`DiagramModel`](https://bench-grafloria-6l.atloria.app/p/bench-grafloria-6l-dqcsEdUbfS/developer/grafloria-engine-models-diagrammodel#diagrammodel), where diagram data lives; `getEngine()` returns the [`DiagramEngine`](https://bench-grafloria-6l.atloria.app/p/bench-grafloria-6l-dqcsEdUbfS/developer/grafloria-engine-engine#diagramengine), where behavior lives. `serialize()` produces the document object, and `JSON.stringify()` turns it into JSON text. Use the instance for canvas-facing work, the model for data queries, and the engine for behavior.

## The document is the API

The model serializes the diagram as one JSON document: it includes the schema version, diagram metadata and name, nodes, links, groups, and viewport. The same document shape is used for persistence and restoration, so data does not need a separate format for each framework. `savedDocument` in the example above is that document object; `savedJson` is its JSON representation.

## User edits are commands on one history stack

When a user drags, connects, deletes, or groups an item in the mounted canvas, Grafloria turns the gesture into a command on the engine's shared history stack. Try dragging either node in one of the mounted examples and pressing Ctrl+Z (or ⌘Z on macOS): the move is undone. The framework binding reflects the changed model back into application state. By contrast, adding initial specs or loading a document is setup, not a user edit to undo.

See [Command history and edits](https://bench-grafloria-6l.atloria.app/p/bench-grafloria-6l-dqcsEdUbfS/developer/command-history) for commands and programmatic edits.

## The model stores intent, not pixels

Node specs provide identity, label, and geometry; an edge spec identifies its source and target. The renderer decides how to draw that intent. For example, an edge without pinned handles can use the side that faces its partner, so the connection adapts as nodes move instead of preserving a pixel path.

In the examples, `position` locates each node and `source`/`target` describe the edge. Grafloria computes and draws the connection on the canvas; the spec does not contain the rendered pixels. See [Diagram intent and rendering](https://bench-grafloria-6l.atloria.app/p/bench-grafloria-6l-dqcsEdUbfS/developer/diagram-intent-and-rendering) for geometry and routing.

## Where next

- [The graph model and document](https://bench-grafloria-6l.atloria.app/p/bench-grafloria-6l-dqcsEdUbfS/developer/graph-model-and-document) — nodes, links, groups, and serialized data.
- [Instance and data flow](https://bench-grafloria-6l.atloria.app/p/bench-grafloria-6l-dqcsEdUbfS/developer/instance-and-data-flow) — how specs and model changes move through the instance.
- [Command history and edits](https://bench-grafloria-6l.atloria.app/p/bench-grafloria-6l-dqcsEdUbfS/developer/command-history) — how gestures and programmatic edits enter history.
- [Diagram intent and rendering](https://bench-grafloria-6l.atloria.app/p/bench-grafloria-6l-dqcsEdUbfS/developer/diagram-intent-and-rendering) — how the renderer turns model geometry and connections into a visible diagram.
- [Live interaction demo](https://grafloria.com/demos/#interaction) — drag a node and try undo.
