Skip to content

Frameworks & bundlers ​

granth is plain ESM with no framework coupling. The core works anywhere; only React and Vue get a (tiny, optional) binding, because they have no store contract.

The one universal requirement ​

Your bundler must be able to resolve a worker URL:

js
worker: () => new Worker(new URL('./db.worker.js', import.meta.url), { type: 'module' })

Vite, webpack 5, Rollup, Parcel 2, esbuild and Next.js all understand this form natively.


React / Next.js ​

jsx
// db.js
import Granth from 'granthdb';
export const db = new Granth('myapp', {
  worker: () => new Worker(new URL('./db.worker.js', import.meta.url), { type: 'module' }),
});
db.version(1).stores({ friends: '++id, name, age, *tags' });

// Friends.jsx
import { useLiveQuery, useIsSupported } from 'granth-react';
import { db } from './db';

export function Friends() {
  const supported = useIsSupported();          // false during SSR — no hydration mismatch
  const friends = useLiveQuery(db, () => db.friends.orderBy('name').toArray(), [], []);
  if (!supported) return <p>Loading…</p>;
  return <ul>{friends.map((f) => <li key={f.id}>{f.name}</li>)}</ul>;
}

Prefer the Dexie call shape? Bind it once:

js
export const useLive = createLiveQueryHook(db);
// const friends = useLive(() => db.friends.toArray(), [], []);

SSR is safe. new Granth(...) at module scope touches no browser API — it only connects when you first query. Server renders return the initialValue.

Angular ​

No adapter needed — liveQuery implements Symbol.observable:

ts
import { from } from 'rxjs';
import { db } from './db';

@Component({ /* ... */ })
export class FriendsComponent {
  friends$ = from(db.liveQuery(() => db.friends.orderBy('name').toArray()));
}
html
<li *ngFor="let f of friends$ | async">{{ f.name }}</li>

Svelte / SvelteKit ​

No adapter needed — subscribe() returns an unsubscribe function, which is the Svelte store contract, so it works with $:

svelte
<script>
  import { db } from './db';
  const friends = db.liveQuery(() => db.friends.orderBy('name').toArray());
</script>

{#each $friends ?? [] as f}<li>{f.name}</li>{/each}

Under SvelteKit SSR, guard with browser from $app/environment before querying.

Vue / Nuxt ​

vue
<script setup>
import { useLiveQuery } from 'granth-vue';
import { db } from './db';

const { data: friends } = useLiveQuery(db, () => db.friends.orderBy('name').toArray(), {
  initialValue: [],
});
</script>

<template><li v-for="f in friends" :key="f.id">{{ f.name }}</li></template>

Unsubscribes automatically with the component's effect scope.

Solid, Qwik, Lit, Alpine, vanilla ​

Use subscribe directly:

js
const stop = db.liveQuery(() => db.friends.toArray()).subscribe(render);
// later: stop();

No bundler (<script type="module">) ​

html
<script type="module">
  import Granth from 'https://esm.sh/granthdb';
  const db = new Granth('myapp', {
    worker: () => new Worker('/db.worker.js', { type: 'module' }), // a real URL
  });
  db.version(1).stores({ friends: '++id, name' });
</script>

db.worker.js must be served from your origin and import sqlite-wasm from a CDN.


Bundler notes ​

Vite ​

js
// vite.config.js
export default {
  optimizeDeps: { exclude: ['@sqlite.org/sqlite-wasm'] }, // esbuild mangles its wasm loading
  worker: { format: 'es' },
};

webpack 5 ​

Works out of the box. Ensure experiments.asyncWebAssembly if you inline the wasm.

Next.js ​

Put the worker file under your app directory and use the new URL(...) form. Keep all database access in client components ('use client') or effects.

Angular CLI ​

Add the worker with ng generate web-worker, or reference it with new URL(...) — Angular 16+ uses esbuild and handles it.

Environments without a Worker ​

Strict CSP without worker-src, some extension and embedded contexts, SSR and Node can all run granth — on the inline runtime, paired with a non-OPFS backend.

js
import { Granth } from 'granthdb';
import { inlineRuntime } from 'granth-runtime-inline';

const db = new Granth('myapp', { runtime: inlineRuntime({ createHandlers }) });

SQL then runs on the calling thread and OPFS is unavailable. See Runtimes.

Requirements everywhere ​

  • Secure context — HTTPS or localhost. OPFS and Web Locks both require it.
  • Chrome 108+, Safari 16.4+, Firefox 111+.
  • No COOP/COEP headers needed.