Skip to Content
Home

Gluonic

A sync engine that feels synchronous

Build offline-first apps with real-time sync, optimistic updates, and a clean synchronous API - no loading states, no complex async patterns.

Get Started →

Learn More

What is Gluonic?

Gluonic is an open-source sync engine for React and React Native that handles data synchronization between your app and server. It provides:

  • Offline-first - Apps work without network connectivity
  • Real-time sync - Changes propagate instantly when online
  • Optimistic updates - Instant UI feedback before server confirmation
  • Automatic state management - No manual useState/useEffect for synced data

What makes Gluonic unique: A synchronous data access API that makes async data feel instant.

Simple Synchronous API

Gluonic provides two ways to work with async data synchronously:

Pattern 1: Progressive Enhancement

Handle loading states inline - data appears as it loads:

const TeamView = observer(({ team }) => { const issues = team.issues.elements const createIssue = async () => { const issue = new Issue({ title: 'New Issue', teamId: team.id }) await issue.save() } return ( <div> <h1>{team.name}</h1> <button onClick={createIssue}>Create Issue</button> {issues.map(issue => ( <IssueRow key={issue.id} issue={issue} author={issue.author?.value?.name} // Optional - may be undefined /> ))} </div> ) })

Behind the scenes, Gluonic:

  • Returns empty array immediately (synchronous)
  • Kicks off background loading from storage/network
  • MobX re-renders when data arrives
  • Progressive appearance - no blocking spinners

Pattern 2: Guaranteed Data with Suspense

Use Suspense boundaries to guarantee data before rendering:

<Suspense fallback={<Spinner />}> <TeamView team={team} /> </Suspense> const TeamView = observer(({ team }) => { const issues = team.issues.suspense // Throws promise until loaded return issues.map(issue => ( <IssueRow issue={issue} author={issue.author.suspense.name} // Guaranteed - no optionals! /> )) })

Benefits:

  • Clean component code (no optional chaining)
  • Type-safe (guaranteed non-undefined)
  • Declarative loading states

Both patterns avoid async/await in components - that’s what makes them “synchronous”.

Learn more about Synchronous API →


Full Sync Engine Features

Offline-First Architecture

Your app works perfectly without network. Data is stored locally first, then synced to the server in the background.

// Works offline - changes queue for sync await store.save('task', taskId, { done: true }) // UI updates immediately // Syncs when connection returns

Real-Time Synchronization

Changes from other users appear instantly via WebSocket + delta sync.

// Another user updates an issue // Your UI updates automatically // No polling, no manual refresh

Optimistic Updates

UI updates immediately, with automatic rollback on errors.

// Update applies instantly await store.save('issue', id, { title: 'New Title' }) // If server rejects, automatically rolls back // No manual error handling needed

Type-Safe Models

Full TypeScript support with decorators.

@ClientModel('issue') export class Issue extends Model { @Property() id = crypto.randomUUID() @Property() title!: string @Property() assigneeId?: string @ManyToOne('assignee', 'assigneeId') assignee?: User }

Simple Setup

Server

server.ts
import { SyncServer } from '@gluonic/server' import { PrismaAdapter } from '@gluonic/server-prisma' import { JWTAuth } from '@gluonic/auth-jwt' const database = PrismaAdapter({ prisma }) const server = SyncServer({ database, auth: JWTAuth({ secret: process.env.JWT_SECRET }) }) await server.listen({ port: 3000 })

Client

sync.ts
import { SyncClient } from '@gluonic/client' import { DrizzleAdapter } from '@gluonic/client-drizzle' import { db } from './db' import { Issue, User } from './models' const storage = DrizzleAdapter({ db }) export const client = SyncClient({ server: 'https://api.example.com/sync/v1', storage, models: [Issue, User] })
App.tsx
import { SyncProvider } from '@gluonic/client' import { client } from './sync' export default function App() { const { token } = useAuth() return ( <SyncProvider client={client} token={token}> <YourApp /> </SyncProvider> ) }

Use in Components

IssueList.tsx
import { observer, useCollectionModels } from '@gluonic/client' import { Issue } from './models' const IssueList = observer(() => { const issues = useCollectionModels<Issue>('issue') return issues.map(issue => ( <div key={issue.id}> <h3>{issue.title}</h3> <span>{issue.assignee.value?.name ?? 'Unassigned'}</span> </div> )) })

See full tutorial →


Why Gluonic?

Sync Engine Benefits

  • ✅ Offline support out of the box
  • ✅ Real-time updates via WebSocket
  • ✅ Optimistic UI with automatic rollback
  • ✅ Delta synchronization for efficiency
  • ✅ Durable transaction queue
  • ✅ Conflict resolution
  • ✅ Works with any database

Unique Developer Experience

  • ✅ Synchronous data access (no async in components)
  • ✅ Two patterns: Progressive or Suspense
  • ✅ Automatic state management (no useState/useEffect)
  • ✅ Lazy loading (data loads on-demand)
  • ✅ Object Graph (connected model instances)
  • ✅ Identity mapping (same ID = same instance)

Self-Hosted & Open Source

  • ✅ Run on your infrastructure
  • ✅ Server: Works with Prisma, Drizzle, TypeORM, or any ORM
  • ✅ Client: Drizzle adapter for local storage
  • ✅ Universal (React Native + Web)
  • ✅ No vendor lock-in

Get Started


Status

Gluonic is in active development. The core sync engine is functional and we’re working toward a stable v1.0 release.

Last updated on