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-drizzleNote: You don’t need to install
mobxormobx-react-liteseparately -observeris 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/sqliteWeb:
pnpm add drizzle-orm better-sqlite3Step 2: Initialize Database
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:
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_queuefor pending mutations andsync_metafor sync state) - you don’t need to define them!
Step 3: Define Models
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
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
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:
SyncProviderhandles all initialization automatically:
- When
tokenis set → Bootstraps and starts syncing- When
tokenchanges → Clears local data, then bootstraps with new token (prevents data leakage between accounts)- When
tokenisnull→ 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-sqliteand@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-decoratorsmodule.exports = function (api) {
api.cache(true)
return {
presets: ['babel-preset-expo'],
plugins: [
['@babel/plugin-proposal-decorators', { version: '2023-11' }]
]
}
}{
"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-transpileimport { vitePluginTypescriptTranspile } from 'vite-plugin-typescript-transpile'
export default defineConfig({
plugins: [
vitePluginTypescriptTranspile(), // Handles decorators
react()
]
}){
"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:
- On mount - Connects to local database
- When token provided - Full bootstrap and sync
- Queue replay - Restores pending mutations from previous session
- WebSocket connects - Real-time updates begin
- When token changes - Clears all local data, then bootstraps (prevents account data mixing)
- 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:
- Defining Models - Model decorators and relationships
- Mutations - Create, update, delete data
- Offline Handling - Network state management