Skip to content

Instantly share code, notes, and snippets.

@0xBigBoss
Created February 18, 2026 00:26
Show Gist options
  • Select an option

  • Save 0xBigBoss/33adb6f99de9bb987a20619ddbd600d6 to your computer and use it in GitHub Desktop.

Select an option

Save 0xBigBoss/33adb6f99de9bb987a20619ddbd600d6 to your computer and use it in GitHub Desktop.
OneStack research: One framework, Zero sync engine, on-zero wrapper — architecture, deployment, and production readiness notes (Feb 2026)
# OneStack Research Notes (Feb 2026)
Architecture, deployment, and production readiness notes for **One**, **Zero**, and **on-zero**.
Use as scaffolding context when starting a new project with the OneStack ecosystem.
---
## One Framework (v1.8.0)
**Repo:** [github.com/onejs/one](https://github.com/onejs/one) — 4,375 stars, 308+ releases
**What:** React framework targeting web + React Native from a single codebase via a single Vite plugin.
**Status:** Officially "stable for web and native."
### Stack
- React 19.1 + Vite 7.1 + Hono (production server)
- React Native 0.81 / Expo 54
- File-system routing (forked from Expo Router)
- TypeScript ~5.9, Bun 1.3.9 for dev
- Optional: Tamagui UI, React Compiler
### Architecture
The `one()` Vite plugin orchestrates 15+ internal sub-plugins across 4 build environments:
| Environment | Target |
|---|---|
| `client` | Web browser |
| `ssr` | Server-side Node.js |
| `ios` | React Native iOS |
| `android` | React Native Android |
Key internal plugins handle: env vars, aliases, Tamagui config, file-system routing, TypeScript route type generation, virtual entry points, server/client code splitting, SSR CSS extraction, devtools, and tree-shaking.
**Native bundling** has two paths:
- **Metro mode** — stable, recommended for production native builds
- **Vite-native bundling** — experimental
### Render Modes (per-page granularity)
| Mode | Description |
|---|---|
| **SSG** (default) | Pre-rendered at build time. Fast, cacheable. |
| **SSR** | Rendered per-request. Dynamic content with SEO. |
| **SPA** | Client-only. Dashboards, highly interactive UIs. |
### Deployment
```bash
one build # Produces dist/ with server bundles, static assets, API routes
one serve # Starts production Hono server
```
**First-class deploy targets:**
1. **Node.js** — `one serve` runs Hono with SSR, loaders, API routes, static assets
2. **Vercel** — Auto-generates `.vercel/output/` (Build Output API v3). API routes → serverless functions, middleware → Edge Runtime. Known caching issue (#672).
3. **Cloudflare Workers** — Generates `_worker-src.js`, uses `nodejs_compat`
### Minimal Config
```typescript
// vite.config.ts
import { defineConfig } from 'vite'
import { one } from 'one/vite'
export default defineConfig({
plugins: [
one({
web: { defaultRenderMode: 'ssg' },
// native: { bundler: 'metro' }, // for production native
// react: { compiler: true }, // optional React Compiler
}),
],
})
```
### Production Readiness
| Signal | Assessment |
|---|---|
| Official status | "Stable for web and native" |
| Release velocity | Multiple per week, very active |
| Open issues | ~30 (reasonable) |
| **Main risk** | **Single maintainer** — natew = 80% of 3,600 commits |
| Native concerns | Android Reanimated codegen bugs, no Expo Dev Build support yet |
| SSG limitation | Dynamic param pages use client hydration workaround (#671) |
| Vercel | Known CACHE_KEY mismatch issue (#672) |
### File Structure (from starter)
```
app/
_layout.tsx # Root layout (web: full HTML doc, native: just Slot)
index.tsx # Home route (/)
[id].tsx # Dynamic route
tabs/
_layout.tsx # Tab navigation layout
home.tsx
settings.tsx
api/
health+api.ts # API route → GET /api/health
vite.config.ts
```
### Key APIs
- `useLoader()` — access server-side loader data
- `<Link href="/path">` — navigation (web + native)
- `useNavigation()`, `useRouter()` — imperative navigation
- `generateStaticParams()` — export from dynamic routes for SSG
- Setup files: `setup.ts` (shared), `setup.client.ts`, `setup.server.ts`, `setup.native.ts`
- Environment guards: `import 'server-only'`, `'client-only'`, `'native-only'`, `'web-only'`
---
## Zero Sync Engine (v0.25.x)
**Repo:** [github.com/rocicorp/mono](https://github.com/rocicorp/mono) — 2,718 stars
**Package:** `@rocicorp/zero` (stable: 0.25.12, canary: 0.26.0)
**What:** Query-driven sync engine for TypeScript web apps. Not a database — sits between your app and PostgreSQL.
**Status:** Pre-beta / public alpha.
### Architecture
```
React App (useQuery via ZQL)
→ @rocicorp/zero client (local normalized datastore)
→ zero-cache server (SQLite replica, WebSocket sync)
→ PostgreSQL (source of truth, WAL-based change detection)
```
- **zero-cache**: Server process maintaining a read-only SQLite replica of Postgres. Reads the WAL for changes. Handles auth, permissions, query resolution. Pushes updates to clients via WebSocket.
- **zero-client**: Client library with local normalized datastore. All reads/writes go local first.
- **ZQL**: TypeScript query builder with Incremental View Maintenance (IVM) for reactive updates.
### Sync Model
- **Query-driven partial sync** — only rows matching active client queries are synced
- **Server is authoritative** — not "local owns data" local-first
- **Optimistic writes** — execute locally, reconciled by server (last-write-wins)
- **Rejected mutations silently revert** (no error callback yet)
- **Reads work offline** from cache; **writes require connectivity**
### ZQL Examples
```typescript
// Basic query
const issues = z.query.issue
.where('priority', 'high')
.orderBy('created', 'desc')
.limit(100)
// With relationships
const issuesWithComments = z.query.issue
.related('comments', q => q.orderBy('modified', 'desc').limit(10))
.related('labels')
// Compound filters
z.query.issue.where(({cmp, and, or, not}) =>
or(
cmp('priority', 'critical'),
and(cmp('priority', 'medium'), not(cmp('numVotes', '>', 100)))
)
)
```
### Schema Definition
```typescript
import { table, string, number, boolean, relationships } from '@rocicorp/zero'
const issue = table('issue')
.columns({
id: string(),
title: string(),
priority: string(),
numVotes: number(),
closed: boolean(),
})
.primaryKey('id')
const comment = table('comment')
.columns({
id: string(),
issueId: string(),
body: string(),
})
.primaryKey('id')
const rels = relationships(issue, ({many}) => ({
comments: many({ sourceField: 'id', destSchema: comment, destField: 'issueId' }),
}))
```
### Known Limitations (Feb 2026)
- **No offline writes**
- **TypeScript clients only** — no native mobile SDKs
- **PostgreSQL only**
- **Single-node `zero-cache`** — no HA, requires downtime for updates
- **No aggregation** (count, group-by) — post-beta
- **No SSR support** — post-beta
- **No column-level permissions** — post-beta
- **No full-text search** — post-beta
- **Dataset size** — recommended under ~100GB
- **Postgres views not synced**
---
## on-zero Wrapper (v0.1.x)
**Repo:** [github.com/onejs/on-zero](https://github.com/onejs/on-zero)
**Package:** `on-zero` (v0.1.26)
**What:** Rails-like DRY wrapper over `@rocicorp/zero`. Co-locates schema, permissions, mutations per model.
### What It Provides
- **Models**: Single-file schema + permissions + mutations definition
- **Queries**: Plain TypeScript functions that auto-convert to validated, synced queries
- **Mutations**: Simplified CRUD with integrated permission checks
- **Permissions**: `serverWhere()` helper for server-side query-based permissions
- **React hooks**: `useQuery()`, `usePermission()`
- **CLI tooling**: Watch and generate commands
### Integration Stack
```
One Framework (routing, rendering, cross-platform)
→ on-zero (DRY models, auto-generated queries, hooks)
→ @rocicorp/zero (sync engine client)
→ zero-cache (server, SQLite replica)
→ PostgreSQL (source of truth)
```
### Current Status
Early alpha integration. Starter available via `npx one` but the official tight One+Zero integration is still in development.
---
## Decision Matrix
| Concern | One | Zero |
|---|---|---|
| **Production-ready?** | Yes for web. Cautious yes for native (use Metro). | No — pre-beta alpha. |
| **Best for** | Cross-platform apps, SSG marketing sites, SSR apps | Real-time collaborative UIs, instant-feel dashboards |
| **Deploy** | Vercel, Cloudflare Workers, Node.js | Self-host zero-cache alongside Postgres |
| **Biggest strength** | Single codebase web+native, modern Vite DX | Instant UI, query-driven sync, great TS DX |
| **Biggest risk** | Single maintainer (bus factor = 1) | No HA, no offline writes, alpha stability |
### When to Use Zero vs Traditional Loaders
**Use Zero when:**
- Building real-time collaborative features
- Want instant UI without loading states
- Data is relational and fits in Postgres < 100GB
- Web-only or can use REST/loaders for native
**Use One's built-in loaders when:**
- Traditional request/response is fine
- Need native mobile data access
- Working with non-Postgres data sources
- Want simplest possible setup
---
## Scaffolding Quick Start
```bash
# Create new One project (includes Zero starter option)
npx one
# Or manual setup
mkdir my-app && cd my-app
npm init -y
npm install one react react-dom react-native
npm install -D vite
# Add Zero (optional)
npm install @rocicorp/zero on-zero
# Dev
npx one dev
# Build & serve
npx one build
npx one serve
```
### Useful Links
- [One docs](https://onestack.dev/docs/introduction)
- [One GitHub](https://github.com/onejs/one)
- [One releases](https://github.com/onejs/one/releases)
- [Zero docs](https://zero.rocicorp.dev/docs/introduction)
- [Zero roadmap](https://zero.rocicorp.dev/docs/roadmap)
- [Zero GitHub (mono repo)](https://github.com/rocicorp/mono)
- [on-zero GitHub](https://github.com/onejs/on-zero)
- [ZQL reference](https://zero.rocicorp.dev/docs/zql)
- [Zero permissions](https://zero.rocicorp.dev/docs/permissions)
- [Independent review (Marmelab)](https://marmelab.com/blog/2025/02/28/zero-sync-engine.html)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment