The Qwik diagram library that renders on the server

Node editors and flow charts as Qwik components — QRL callbacks (onInit$, onNodesChange$), your own component$ as a custom node, hooks for selection and viewport. The server sends the finished diagram in the HTML, and the browser resumes it instead of redrawing it. Underneath: obstacle-avoiding routing, ELK auto-layout, undo/redo, CRDT collaboration and SVG/PNG/PDF export. MIT, no Pro tier.

npm install @grafloria/qwik @grafloria/renderer @grafloria/engine @grafloria/element
flow.tsx
import { component$, $ } from '@builder.io/qwik';
import { GrafloriaFlow, type NodeProps } from '@grafloria/qwik';

// Your own Qwik component draws the 'job' nodes.
const Job = component$((p: NodeProps<{ title: string }>) => (
  <div class="job-card">{p.data.title}</div>
));

export const Flow = component$(() => (
  <GrafloriaFlow
    nodes={[
      { id: 'a', type: 'job', position: { x: 0, y: 0 }, data: { title: 'Extract' } },
      { id: 'b', position: { x: 260, y: 0 }, label: 'Load' },
    ]}
    edges={[{ source: 'a', target: 'b' }]}
    nodeTypes={{ job: Job }}
    layout="elk"
    onNodeClick$={$(({ node }) => console.log(node.id))}
  />
));
live — the same engine this binding wraps, drag anything
rendering…

Why teams pick it

Idiomatic Qwik

Every callback is a QRL, so it loads lazily. Custom nodes are plain component$s keyed by node type, and <GrafloriaProvider> with useGrafloria(), useSelection() and useViewport() lets a toolbar or inspector drive the canvas.

Server-rendered, then resumed

renderToStaticSVG() runs the real renderer in Node with no DOM. Pass the result as ssr: the page is correct before any script runs, and the browser adopts that markup with no hydration pass and no re-layout.

The hard parts are library code

Obstacle-avoiding orthogonal routing with jump-overs, ELK/dagre/force layout in a worker, one-gesture-one-undo command history, the drag-and-pack dashboard kit, UML/ER kits.

Server rendering in three lines

const ssr = renderToStaticSVG({ nodes, edges, width: 900, height: 420, fitView: true }); on the server, <GrafloriaFlow nodes={nodes} edges={edges} ssr={ssr} /> in the component, and ssr.css in the document head. The diagram’s script starts once the page is ready, so onInit$ fires and the hooks have the instance before anyone touches it. Custom nodes are framework components, so they mount in the browser, inside the layer the server already sent.

One engine, five surfaces

The identical JSON spec drives Qwik, React, Angular, Vue and the plain web component — and the same model exports to Mermaid text, SVG, PNG and true-vector PDF, in the browser or in Node. Move a diagram between apps without rewriting it.

Every demo, in your framework

The gallery is the documentation: every demo is a real, clickable page, and our CI drives each one with real pointer events so the examples can't rot — and each one is available as a Qwik component, source shown. Qwik demos — every gallery page as a real Qwik component → Server rendering with resume has its own app: clone the repository and run npx vite --config apps/demos-qwik/vite.config.ts --mode ssr.

Grafloria is MIT — every feature, no commercial tier. A custom node renders in its own Qwik container, so it cannot read contexts from the surrounding app; feed it through node.data. Qwik, React, Angular and Vue are trademarks of their respective owners; no affiliation implied. Corrections welcome via GitHub issues.