Render a flow, connect sibling UI to its live instance, and mount the diagram and comments components in a Vue 3 app.
One headless model drives every framework binding, so you can learn the model once and use the same diagram behavior across frameworks.
Prerequisites
Use Vue 3.4 or later; for the shared engine, renderer, and element package setup, see the JavaScript quick start.
Install the binding and its peers:
bashnpm install @grafloria/vue @grafloria/engine @grafloria/renderer @grafloria/element vue
1. Render a flow
In Vue, bind typed initial arrays to GrafloriaFlow with :default-nodes and :default-edges; for the shared flow and EdgeSpec overview, see the React quick start. Give the flow's parent a height because the canvas fills that parent.
Create src/App.vue:
vue<script setup lang="ts"> import { GrafloriaFlow } from '@grafloria/vue'; import type { EdgeSpec, NodeSpec } from '@grafloria/vue'; const nodes: NodeSpec[] = [ { id: 'ingest', position: { x: 60, y: 80 }, size: { width: 180, height: 80 }, data: { label: 'Ingest' }, }, { id: 'publish', position: { x: 380, y: 80 }, size: { width: 180, height: 80 }, data: { label: 'Publish' }, }, ]; const edges: EdgeSpec[] = [ { id: 'ingest-to-publish', source: 'ingest', target: 'publish' }, ]; </script> <template> <div style="height: 480px"> <GrafloriaFlow :default-nodes="nodes" :default-edges="edges" :plugins="true" /> </div> </template>
The mounted canvas shows Ingest connected to Publish, with the minimap, zoom/fit controls, and background grid enabled by plugins. For application-owned state, bind v-model:nodes and v-model:edges instead of using the default… props.
2. Reach the instance from a sibling
Place the sibling panel under GrafloriaProvider, and call useGrafloria, useSelection, useViewport, and useOnSelectionChange in its Vue setup scope; the callback subscription ends with that scope. For the shared sibling-instance pattern, see the React quick start.
Create src/App.vue with a sibling inspector component declared in the same file. The child component calls the composables from its own setup scope, under the provider:
vue<script setup lang="ts"> import { defineComponent, h, ref } from 'vue'; import { GrafloriaFlow, GrafloriaProvider } from '@grafloria/vue'; import type { EdgeSpec, NodeSpec } from '@grafloria/vue'; import { useGrafloria, useOnSelectionChange, useSelection, useViewport, } from '@grafloria/vue'; const nodes: NodeSpec[] = [ { id: 'ingest', position: { x: 60, y: 80 }, size: { width: 180, height: 80 }, data: { label: 'Ingest' } }, { id: 'publish', position: { x: 380, y: 80 }, size: { width: 180, height: 80 }, data: { label: 'Publish' } }, ]; const edges: EdgeSpec[] = [ { id: 'ingest-to-publish', source: 'ingest', target: 'publish' }, ]; const InspectorPanel = defineComponent({ setup() { const diagram = useGrafloria(); const selection = useSelection(); const viewport = useViewport(); const latestChange = ref('No selection changes yet'); useOnSelectionChange((change) => { latestChange.value = `${change.nodes.length} nodes, ${change.edges.length} edges`; }); return () => h('aside', { style: 'width: 220px; padding: 16px' }, [ h('p', `${selection.value.nodes.length} nodes selected`), h('p', `Zoom: ${viewport.value.zoom.toFixed(2)}`), h('p', `Latest change: ${latestChange.value}`), h( 'button', { type: 'button', onClick: () => diagram.value?.fitView() }, 'Fit diagram', ), ]); }, }); </script> <template> <GrafloriaProvider> <div style="display: flex; height: 480px"> <div style="flex: 1; min-width: 0"> <GrafloriaFlow :default-nodes="nodes" :default-edges="edges" /> </div> <InspectorPanel /> </div> </GrafloriaProvider> </template>
3. Render a diagram spec
Use GrafloriaDiagram when you have a diagram spec instead of flow props. Its spec accepts a diagram spec object or a JSON string; this example uses a JSON string with nodes and edges.
Create src/SpecDiagram.vue:
vue<script setup lang="ts"> import { GrafloriaDiagram } from '@grafloria/vue'; const spec = JSON.stringify({ nodes: [ { id: 'draft', position: { x: 70, y: 90 }, size: { width: 170, height: 70 }, data: { label: 'Draft' }, }, { id: 'review', position: { x: 340, y: 90 }, size: { width: 170, height: 70 }, data: { label: 'Review' }, }, ], edges: [{ id: 'draft-to-review', source: 'draft', target: 'review' }], }); </script> <template> <div style="height: 400px"> <GrafloriaDiagram :spec="spec" /> </div> </template>
The component mounts a diagram with Draft connected to Review. Changing the spec's value replaces the mounted diagram; an equal value does not trigger a replacement.
4. Add a comment panel
In Vue, capture the store in a nullable shallowRef from the flow's @init handler, then use v-if to mount GrafloriaCommentPanel only when the store exists. For the shared flow comments and CommentStore pattern, see the React quick start.
Create src/CommentsExample.vue:
vue<script setup lang="ts"> import { shallowRef } from 'vue'; import { GrafloriaCommentPanel, GrafloriaFlow } from '@grafloria/vue'; import type { DiagramInstance, EdgeSpec, NodeSpec } from '@grafloria/vue'; import type { CommentStore } from '@grafloria/engine'; const store = shallowRef<CommentStore | null>(null); const nodes: NodeSpec[] = [ { id: 'design', position: { x: 40, y: 100 }, size: { width: 150, height: 66 }, data: { label: 'Design' } }, { id: 'review', position: { x: 260, y: 100 }, size: { width: 150, height: 66 }, data: { label: 'Review' } }, { id: 'ship', position: { x: 480, y: 100 }, size: { width: 150, height: 66 }, data: { label: 'Ship' } }, ]; const edges: EdgeSpec[] = [ { id: 'design-to-review', source: 'design', target: 'review' }, { id: 'review-to-ship', source: 'review', target: 'ship' }, ]; function onInit(instance: DiagramInstance): void { const comments = instance.getCommentStore(); if (!comments) return; const threadId = comments.createThread( { kind: 'node', id: 'review' }, 'Can we tighten the review checklist?', ); comments.reply(threadId, 'I will update it before handoff.'); store.value = comments; } </script> <template> <div style="display: flex; height: 400px"> <div style="flex: 1; min-width: 0"> <GrafloriaFlow :default-nodes="nodes" :default-edges="edges" :comments="true" @init="onInit" /> </div> <aside v-if="store" style="width: 300px; overflow: auto"> <GrafloriaCommentPanel :store="store" /> </aside> </div> </template>
The initial view contains the Design → Review → Ship flow and a comment thread attached to Review. getCommentStore() returns null when comments are not enabled, so the panel renders only after the enabled flow supplies its store.
What you have
You can mount a typed flow, read live instance state from a sibling component, render a JSON diagram spec, and attach the comments UI to a flow's comment store.
Where to go next
- State and data flow in Vue explains controlled and uncontrolled flow data and saving diagrams.
- Lay out diagrams automatically covers layout choices.
- See the live drag-and-undo demo and live comments demo.
Was this page helpful?