Learn / Groups

Groups

A group is not a rectangle drawn behind some nodes — it is a container the engine understands: members move together, collapse is reversible by construction, and nesting reflows parents around children.

Creating and membership

import { GroupModel } from '@grafloria/engine';

diagram.addGroup(new GroupModel({
  id: 'ingest-stage',
  position: { x: 0, y: 0 }, size: { width: 320, height: 180 },
}));

await engine.addToGroup('ingest-stage', 'a');    // (groupId, entityId) — group first
await engine.addToGroup('ingest-stage', 'b');
await engine.removeFromGroup('ingest-stage', 'b');

Members drag with the group, and interactive membership works too — drag a node into a group's frame and it joins; groups can contain groups. Query structure with diagram.getGroups(), diagram.getGroup(id), diagram.getAncestors(id) / getDescendants(id).

Collapse is a reversible snapshot

await engine.collapseGroup('ingest-stage');   // members hide, links re-route to the header
await engine.expandGroup('ingest-stage');     // everything returns exactly where it was

Collapse stores a snapshot on the group — member world-positions and the links removed at collapse time — so a collapsed diagram serializes, round-trips, and expands correctly later, even in a different session. Links from outside to a hidden member re-anchor to the collapsed header, and parallel edges merge into one until expand.

Nesting and fit

Containers reflow: a group can fit itself around its already-fitted children, and a resize cascades down into nested containers' own reflows. That is what makes swimlanes, BPMN pools and stage-based pipelines behave — moving a lane moves its contents; growing content grows the lane.

Zones — a frame of your own

A group declared in the render spec is a zone: a tinted region around some boxes with a small caption in a corner — no title band. bounds pins its frame; without it the frame is fitted around children with padding. The children become its members.

A zone is a container you can pick up. Press its caption or its empty tint and drag: the zone moves with every box in it, and their lines follow (empty canvas outside a zone still pans). Drag a box out of a zone and it leaves; drop one inside and it joins. Both are on by default; a host that draws frames purely as decoration turns them off:

api.getEngine().setInteractionConfig({
  enableGroupDrag: false,              // a press on a zone pans the canvas instead
  enableGroupMembershipOnDrop: false,  // dropping a box never changes its zone
});
const api = render({
  nodes, edges,
  groups: [{
    id: 'ours', label: 'OUR SIDE · MUST NEVER SEE A CARD', children: ['api', 'db'],
    bounds: { x: 380, y: 236, width: 566, height: 150 },
    labelPlacement: 'bottom-left',          // top-left (default), top, top-right, bottom-…
    style: { fill: '#f3f4f6', stroke: '#d7dbe0', strokeDasharray: '5 4',
             color: '#4b5563', fontWeight: '700', letterSpacing: 1 },
  }],
}, el);
api.setGroups([...]);                       // reconcile later, like setNodes

A Mermaid subgraph is the same zone: style <subgraph> … styles its frame, and a subgraph no directive pins is fitted around its nodes.

Frameless groups — layout without chrome

const g = diagram.getGroup('kpi-row');
g.setMetadata('frameChrome', 'none');    // no border, no header — pure layout container

A frameless group is the primitive under the dashboard kit's grid: cells that pack and drag without looking like "a group". Use it whenever you want container behavior without container pixels.

Groups participate in everything else on this site: group operations are commands (undoable), groups serialize in the document, ELK lays out nested structures natively, and a collapsed group exports exactly as it looks on screen.

Live: group demos — drag in, collapse, nest →

Where next

  • The model — where groups sit among nodes and links.
  • Kits — the dashboard grid built on frameless groups.