Skip to content
D
Documentation

Qwik quick start

tutorial
2 min readUpdated

Use the Qwik binding and continue to the working diagram, hook, dashboard, and server-rendering surfaces.

The first idea is that one headless model drives every framework binding: Qwik converts your node and edge specs into the same diagram model used by the other Grafloria bindings.

Prerequisites

Use Qwik 1.x with Node.js and a strict TypeScript project. Install the Qwik binding and its peer packages:

bash
npm install @grafloria/qwik @grafloria/engine @grafloria/renderer @builder.io/qwik @grafloria/element

The binding targets @builder.io/qwik 1.5 or later. The package versions used by this guide are @grafloria/qwik 0.10.6, @grafloria/engine 0.3.18, and @grafloria/renderer 0.4.19.

1. Mount the Qwik surfaces

For the shared provider-and-hook pattern, see the Vue quick start; this Qwik version uses QRL event props and Qwik signals, so useOnSelectionChange$ takes a module-level QRL and the values from useGrafloria, useSelection, and useViewport are read through .value.

tsx
import { component$, $, noSerialize, useSignal, type NoSerialize } from '@builder.io/qwik';
import type { CommentStore } from '@grafloria/engine';
import {
  GrafloriaCommentPanel,
  GrafloriaDashboard,
  GrafloriaDiagram,
  GrafloriaFlow,
  GrafloriaProvider,
  useGrafloria,
  useOnSelectionChange$,
  useSelection,
  useViewport,
  type DiagramInstance,
  type NodeSpec,
  type SelectionChange,
} from '@grafloria/qwik';

const nodes: NodeSpec[] = [
  { id: 'review', position: { x: 60, y: 60 }, size: { width: 180, height: 80 }, label: 'Review' },
  { id: 'approve', position: { x: 320, y: 60 }, size: { width: 180, height: 80 }, label: 'Approve' },
];

const logSelection = $((change: SelectionChange) => {
  console.log(`Selected ${change.nodes.length} node(s)`);
});

const Toolbar = component$(() => {
  const instance = useGrafloria();
  const selection = useSelection();
  const viewport = useViewport();
  useOnSelectionChange$(logSelection);

  return (
    <p>
      Zoom {viewport.value.zoom.toFixed(2)}; selected {selection.value.nodes.length} node(s)
      <button type="button" onClick$={() => instance.value?.fitView(40)}>Fit view</button>
    </p>
  );
});

const FlowWithComments = component$(() => {
  const store = useSignal<NoSerialize<CommentStore>>();

  return (
    <>
      <GrafloriaFlow
        defaultNodes={nodes}
        defaultEdges={[{ source: 'review', target: 'approve' }]}
        comments
        fitView
        onInit$={$((instance: DiagramInstance) => {
          const commentStore = instance.getCommentStore();
          if (commentStore) store.value = noSerialize(commentStore);
        })}
        style={{ height: '320px' }}
      />
      {store.value && <GrafloriaCommentPanel store={store.value} />}
    </>
  );
});

export default component$(() => (
  <GrafloriaProvider>
    <Toolbar />
    <FlowWithComments />
    <GrafloriaDiagram
      spec="graph\n  A[Review] --> B[Approve]"
      style={{ height: '240px' }}
    />
    <GrafloriaDashboard
      views={[{
        id: 'main',
        widgets: [{
          id: 'revenue',
          kind: 'kpi',
          span: 2,
          rows: 1,
          x: 0,
          y: 0,
          data: { label: 'Revenue', value: '$6.81M', delta: 12.4 },
        }],
      }]}
      layout="grid"
      sizing="grow"
      style={{ height: '240px' }}
    />
  </GrafloriaProvider>
));

The mounted page gives you a flow, a comment panel after the flow creates its store, a generic text diagram, and a KPI dashboard. The flow's onInit$ callback receives the live DiagramInstance. The toolbar displays the current camera and selection and fits the flow when you click its button. Keep the canvas hosts at a real height; a host without resolved height has no pixels to draw into.

2. Follow the working surfaces

Start with the flow and toolbar to verify the instance and reactive state. Then use the generic diagram for text or kit specifications, the comment panel for a live comment store, and the dashboard for widget views. Keep live stores and instances out of serializable Qwik state; the sample wraps the comment store with noSerialize() before passing it to the panel.

For a live, clickable version, open the Grafloria Qwik demos. The toolbar-and-hooks demo exercises the provider and hooks; ssr-resumable shows the server-rendered diagram before client code runs.

Next steps

Was this page helpful?

Qwik quick start — Grafloria · GPT-5.6 Luna