Skip to content

Your first vanilla graph ​

This tutorial creates a Graphora view without a framework. It uses local workspace packages because Graphora is not published to npm.

Expected result: three blue nodes in a circle, with arrows from Gateway to Orders and from Orders to Database.

Open the live Vanilla tutorial

Preview availability

Requires repository access, Node.js 22.13+ and pnpm 11.7.0 for local setup. Packages are not published to npm. Project status.

1. Prepare the workspace ​

From an authorized source checkout:

bash
cd graph-library
pnpm install --frozen-lockfile
pnpm build

The workspace already contains this runnable tutorial. The files below are included directly from its source; you can run them without copying. Inspect examples/vanilla/first-graph/index.html, examples/vanilla/first-graph/src/style.css, and examples/vanilla/first-graph/src/main.ts using the three snippets below. Keep the checked-in examples/vanilla/vite.config.ts; it resolves the local workspace packages.

2. Give the graph a real size ​

The renderer reads the container dimensions when it mounts. A container with no height produces no useful drawing area.

html
<!doctype html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Graphora service dependencies</title>
  </head>
  <body>
    <h1>Service dependencies</h1>
    <div id="graph" aria-label="Service dependency graph"></div>
    <script type="module" src="/src/main.ts"></script>
  </body>
</html>
css
html,
body {
  width: 100%;
  height: 100%;
  margin: 0;
}

#graph {
  min-height: 360px;
}

#graph,
#root {
  height: 360px;
}
body {
  font-family: system-ui, sans-serif;
  background: #f8fafc;
  color: #0f172a;
}
h1 {
  font-size: 1rem;
  margin: 12px 16px;
}

3. Create, mount, lay out, and destroy the engine ​

ts
import { createGraphEngine } from '@graphora/engine'
import { createCircularLayout } from '@graphora/layouts'
import { CanvasGraphRenderer, createViewport } from '@graphora/renderer'

import './style.css'

const container = document.querySelector<HTMLDivElement>('#graph')

if (container === null) {
  throw new Error('Missing graph container.')
}

const engine = createGraphEngine({
  graph: {
    nodes: [
      { id: 'gateway', label: 'Gateway' },
      { id: 'orders', label: 'Orders' },
      { id: 'database', label: 'Database' }
    ],
    edges: [
      {
        id: 'gateway-orders',
        source: 'gateway',
        target: 'orders',
        directed: true
      },
      {
        id: 'orders-database',
        source: 'orders',
        target: 'database',
        directed: true
      }
    ]
  },
  renderer: new CanvasGraphRenderer(),
  layout: createCircularLayout({ radius: 120 }),
  viewport: createViewport({
    width: container.clientWidth,
    height: container.clientHeight
  }),
  theme: {
    backgroundColor: '#f8fafc',
    node: { fill: '#2563eb', radius: 22 },
    edge: { stroke: '#64748b', strokeWidth: 2, arrow: { end: true } },
    label: { visible: true, fill: '#0f172a', fontSize: 13 }
  }
})

engine.mount(container)
window.addEventListener('pagehide', () => engine.destroy(), { once: true })

void engine
  .runLayout({ fitToView: { padding: 48 } })
  .catch((error: unknown) => {
    engine.destroy()
    console.error(error)
  })

The viewport starts with the container's current size. The engine also observes later container resizes when ResizeObserver is available. runLayout() applies all returned positions and fits their bounds with 48 CSS pixels of padding. destroy() disconnects resize observation, cancels pending renders, unsubscribes engine listeners, and destroys the renderer.

4. Run it ​

The checked-in example can be started from graph-library/:

bash
pnpm exec vite examples/vanilla/first-graph \
  --config examples/vanilla/vite.config.ts

Make your first change ​

In src/main.ts, change the theme's node fill from #2563eb to #0f766e and save. Vite refreshes the page: all three nodes become teal, while their labels and connections stay the same. This changes the default node color; item styles can override individual nodes.

Your graph should show Gateway → Orders → Database. If the canvas is empty, check the container height and browser console; see blank canvas troubleshooting.

Continue with your own graph ​

For exact methods and option types, use GraphEngine, CanvasGraphRenderer, and RawGraph in the API reference.