Skip to Content

Client Setup

Initialize Gluonic in your React or React Native application.

Prerequisites

  • TypeScript 5.0+ (required for TC39 Stage 3 decorators)
  • React 18+
  • React Native 0.73+ or Web (Vite/Webpack)

Installation

# Core client package pnpm add @gluonic/client # Storage adapter (Drizzle adapter currently supported) pnpm add @gluonic/client-drizzle

Note: You don’t need to install mobx or mobx-react-lite separately - observer is re-exported from @gluonic/client.

Basic Setup

Step 1: Set Up Local Database

Gluonic requires a local database for offline storage using the Drizzle adapter.

React Native:

pnpm add drizzle-orm @op-rs/sqlite

Web:

pnpm add drizzle-orm better-sqlite3

Step 2: Initialize Database

db/index.ts
import { drizzle } from 'drizzle-orm/op-sqlite' import { open } from '@op-rs/sqlite' export const opDb = open('app.db') export const db = drizzle(opDb)

Step 2: Define Database Schema

Define your Drizzle schema for your app’s data:

db/schema.ts
import { sqliteTable, text, integer } from 'drizzle-orm/sqlite-core' export const user = sqliteTable('user', { id: text('id').primaryKey(), email: text('email').notNull(), name: text('name'), created_at: integer('created_at'), updated_at: integer('updated_at') }) export const post = sqliteTable('post', { id: text('id').primaryKey(), title: text('title').notNull(), content: text('content'), author_id: text('author_id').references(() => user.id), created_at: integer('created_at') })

Note: Gluonic automatically creates and manages its internal tables (sync_tx_queue for pending mutations and sync_meta for sync state) - you don’t need to define them!

Step 3: Define Models

models/User.ts
import { Model, ClientModel, Property, OneToMany, LazyCollection } from '@gluonic/client' import type { Post } from './Post' @ClientModel('user') export class User extends Model { @Property() id: string = '' @Property() email: string = '' @Property() name: string = '' @Property({ server: true }) createdAt: number = 0 @OneToMany('post', 'author') posts: LazyCollection<Post> = new LazyCollection<Post>() }

API Reference: Model · @ClientModel · @Property · @OneToMany

See Defining Models guide for complete decorator documentation.

Step 4: Create Sync Client

sync.ts
import { SyncClient } from '@gluonic/client' import { DrizzleAdapter } from '@gluonic/client-drizzle' import { db } from './db' import { User, Post } from './models' // Create storage adapter const storage = DrizzleAdapter({ db }) // Create sync client export const client = SyncClient({ server: 'https://api.example.com/sync/v1', storage, models: [User, Post] })

API Reference: SyncClient · DrizzleAdapter

Step 5: Wrap Your App

app/_layout.tsx
import { SyncProvider } from '@gluonic/client' import { client } from '../sync' import { useAuth } from '../auth' export default function RootLayout() { const { token } = useAuth() // Get token from your auth system return ( <SyncProvider client={client} token={token}> <Stack /> </SyncProvider> ) }

Note: SyncProvider handles all initialization automatically:

  • When token is set → Bootstraps and starts syncing
  • When token changes → Clears local data, then bootstraps with new token (prevents data leakage between accounts)
  • When token is null → Clears local data and pauses sync

Universal Client (React Native + Web)

Good news: The same code works in both React Native and Web! Just use the appropriate database for each platform.

Platform-specific setup:

React Native:

  • Storage: Drizzle adapter with drizzle-orm/op-sqlite and @op-rs/sqlite
  • Decorators: Supported natively (no config needed)

Web:

  • Storage: Drizzle adapter with drizzle-orm/better-sqlite3

TypeScript Decorator Configuration:

React Native (Expo):

Configure Babel to transpile decorators:

pnpm add -D @babel/plugin-proposal-decorators
babel.config.js
module.exports = function (api) { api.cache(true) return { presets: ['babel-preset-expo'], plugins: [ ['@babel/plugin-proposal-decorators', { version: '2023-11' }] ] } }
tsconfig.json
{ "compilerOptions": { "experimentalDecorators": false, // Use TC39 decorators (not legacy) "target": "ES2022" } }

Web (Vite):

If using Vite, install the TypeScript transpiler plugin:

pnpm add -D vite-plugin-typescript-transpile
vite.config.ts
import { vitePluginTypescriptTranspile } from 'vite-plugin-typescript-transpile' export default defineConfig({ plugins: [ vitePluginTypescriptTranspile(), // Handles decorators react() ] })
tsconfig.json
{ "compilerOptions": { "useDefineForClassFields": false // Required for decorators! } }

See Vite Setup Guide for details.

How It Works

Automatic Initialization

SyncProvider handles everything automatically:

<SyncProvider client={client} token={token}> <App /> </SyncProvider>

What happens:

  1. On mount - Connects to local database
  2. When token provided - Full bootstrap and sync
  3. Queue replay - Restores pending mutations from previous session
  4. WebSocket connects - Real-time updates begin
  5. When token changes - Clears all local data, then bootstraps (prevents account data mixing)
  6. When token is null - Clears all local data and stops sync

You never call init(), bootstrap(), clearAll(), or catchUp() manually - SyncProvider handles everything!

Security: Token changes always clear local storage to prevent data leakage between user accounts. This handles both token refresh and account switching safely.

Auth Integration

With NextAuth:

import { useSession } from 'next-auth/react' function App() { const { data: session } = useSession() return ( <SyncProvider client={client} token={session?.accessToken ?? null}> <YourApp /> </SyncProvider> ) }

With Clerk:

import { useAuth } from '@clerk/clerk-react' function App() { const { getToken } = useAuth() const [token, setToken] = useState(null) useEffect(() => { getToken().then(setToken) }, []) return ( <SyncProvider client={client} token={token}> <YourApp /> </SyncProvider> ) }

Custom auth:

import { useAuth } from './auth' // Your auth context function App() { const { token } = useAuth() return ( <SyncProvider client={client} token={token}> <YourApp /> </SyncProvider> ) }

Next Steps

Essential:

  • Use Hooks - Access synced data in components

Learn more:

Last updated on