Server-side render (SSR-safe usage)
@flowgl/core and all three framework wrappers (@flowgl/react, @flowgl/vue, @flowgl/svelte) are SSR-safe out of the box. The constructor guards typeof window === 'undefined' and returns a dormant instance; the framework wrappers defer chart construction to their respective client-side lifecycle hooks.
What "SSR-safe" actually means here
When @flowgl/core is imported in a Node.js / Edge / Deno environment:
new FlowChart({ container })does not touch the DOM.- It returns an instance whose public methods are mostly no-ops; calling them does not throw, but they don't do anything meaningful either.
- No WebGL context is acquired. No
requestAnimationFrameis scheduled. - The instance is intentionally throwaway — when the page hydrates on the client, construct a fresh chart with a real
containerelement.
You don't get a usable chart on the server. You get a quiet failure mode that doesn't crash your SSR render. That's the contract.
Next.js (React)
The wrapper handles this for you — the <Flowchart> component renders its container div on the server and constructs the chart inside a useEffect on the client. No <ClientOnly> is required.
// app/pipeline/page.tsx
'use client'
import { Flowchart } from '@flowgl/react'
import { useState } from 'react'
export default function PipelinePage() {
const [nodes, setNodes] = useState(initialNodes)
const [edges, setEdges] = useState(initialEdges)
return (
<Flowchart
nodes={nodes}
edges={edges}
onNodesChange={setNodes}
onEdgesChange={setEdges}
style={{ width: '100%', height: 600 }}
/>
)
}The 'use client' directive is required because the chart is interactive state. The server response includes the container <div> with its size; the chart constructs on first paint.
If you're using Pages Router rather than App Router:
// pages/pipeline.tsx
import dynamic from 'next/dynamic'
const Flowchart = dynamic(
() => import('@flowgl/react').then(m => m.Flowchart),
{ ssr: false }
){ ssr: false } is optional but skips the wrapper-render-and-discard roundtrip if your SSR budget matters.
Nuxt (Vue 3)
Same shape. The Composition API wrapper handles onMounted internally:
<!-- pages/pipeline.vue -->
<script setup lang="ts">
import { ref } from 'vue'
import { Flowchart } from '@flowgl/vue'
const nodes = ref(initialNodes)
const edges = ref(initialEdges)
</script>
<template>
<Flowchart v-model:nodes="nodes" v-model:edges="edges" style="width:100%;height:600px;" />
</template>You do not need to wrap the component in <ClientOnly> — the wrapper already gates on a client check. Add <ClientOnly> only if you're hitting a hard server-side error from some other library further up the tree.
SvelteKit
<!-- routes/pipeline/+page.svelte -->
<script lang="ts">
import { Flowchart } from '@flowgl/svelte'
let nodes = initialNodes
let edges = initialEdges
</script>
<Flowchart bind:nodes bind:edges style="width:100%;height:600px;" />Works on both export const ssr = true (default) and export const ssr = false. The onMount inside the wrapper handles the deferral.
Hydration mismatch warnings
If you see a hydration mismatch from React / Vue / Svelte, it's almost always because the container dimensions differ between server and client (often a CSS-only issue). The chart itself doesn't render content during SSR, so it's not the source. Pin the container's width and height explicitly:
<Flowchart style={{ width: '100%', height: 600 }} />…rather than relying on parent flex sizing that might compute differently on the server.
Server-side SVG / PNG preview generation
If what you actually want is a static SVG preview generated on the server (not an interactive chart), the right pattern is to build the graph data on the server, send it to the client, render in a hidden container, call chart.exportSVG(), send the SVG back. That works today but is awkward.
A dedicated server-side renderer backend (so you can import { ssrRender } from '@flowgl/ssr') is on the roadmap under "Later".