One Framework Guide
Official docs: Introduction, Installation, Configuration
One is a full-stack React framework targeting both web and React Native with a single codebase. Built on Vite with file system routing, server-side loaders, and flexible render modes (SSG, SSR, SPA). For native builds, One supports Metro (recommended) and an experimental Vite bundler.
References
Consult these resources as needed:
references/
routing.md File conventions, dynamic routes, groups, parallel routes, intercepting routes, typed routes
layouts.md _layout.tsx, Stack (+ Header Composition API), Tabs, Drawer, Slot, NativeTabs, nested layouts
navigation.md Link (mask, scroll, asChild), useRouter, useLinkTo, Protected, Redirect, route masking
components.md Head (+ iOS Handoff/Spotlight), SafeAreaView, ScrollBehavior, LoadProgressBar, withLayoutContext
hooks.md All hooks: useParams, useSearchParams, useActiveParams, useSegments, useMatches, useBlocker, etc.
configuration.md vite.config.ts, one() plugin options, web/native/global settings, env vars
render-modes.md SSG, SSR, SPA, API per-page and global, folder suffixes, layout render modes
middleware.md _middleware.ts, createMiddleware, auth, CORS, redirects, context sharing
devtools.md Alt+Space spotlight, SEO preview, route debug, loader timing, source inspector, input recording
Quick Start
npx one
This scaffolds a new project with starter templates. Then:
bun install
bun run dev
Access at http://localhost:8081. For native, use Expo Go or build with one prebuild and one run:ios.
Project Structure
app/
_layout.tsx Root layout (Stack, Tabs, Drawer, or Slot)
_middleware.ts Server middleware (optional)
index.tsx Home route (/)
about.tsx Static route (/about)
blog/
[slug].tsx Dynamic route (/blog/:slug)
_layout.tsx Nested layout
dashboard+spa/ SPA folder (all children are SPA)
index.tsx
api/
route+api.ts API endpoint (/api)
users/[id]+api.ts Dynamic API route
+not-found.tsx 404 page
vite.config.ts One configuration
app/routes.d.ts Auto-generated route types
Key Rules
- Routes live in the
app/directory only - Never co-locate components, types, or utilities in
app/— keep them insrc/,components/, etc. - Always have a route that matches
/(even inside a group route) - Use
_layout.tsxfor layouts — it must render<Slot>,<Stack>,<Tabs>, or<Drawer> - Use
+api.tssuffix for API routes, not regular route files - Platform-specific files:
.web.tsx,.native.tsx,.ios.tsx,.android.tsx— never import these directly, the bundler resolves them - Pin the
oneversion for production stability
CLI
one dev [--clean] [--host] [--port] [--debug] # dev server (web + native)
one build [web | ios | android] # production build
one serve [--cluster] [--port] # production server (Hono)
one prebuild # generate native Xcode/Android projects
one run:ios # build and run iOS app
one run:android # build and run Android app
one generate-routes [--typed=runtime|type] # regenerate route types
one patch [--force] # apply package patches
Configuration
All config goes through the Vite plugin in vite.config.ts:
import { one } from 'one/vite'
export default {
plugins: [
one({
web: {
defaultRenderMode: 'ssg',
deploy: 'node', // 'node' | 'vercel' | 'cloudflare'
linkPrefetch: 'intent', // trajectory-based prefetching
sitemap: true,
},
native: {
bundler: 'metro', // recommended for stability
},
}),
],
}
See ./references/configuration.md for all options.
Running the App
Web:
one dev
Native with Expo Go (recommended for quick iteration):
one dev
# press q, r in terminal to show QR code
# scan with Expo Go app
Native with custom build (for custom native dependencies):
one prebuild
one run:ios # or one run:android
Production:
ONE_SERVER_URL=https://myapp.com one build
one serve --cluster # cluster mode for high traffic
Code Style
- Use
process.env.EXPO_OSinstead ofPlatform.OSfor platform checks - Use
import.meta.env.VITE_ENVIRONMENTfor environment checks ('client', 'ssr', 'ios', 'android') - Prefix client-exposed env vars with
VITE_,EXPO_PUBLIC_, orONE_PUBLIC_ - Add
/// <reference types="one/env" />to a.d.tsfile for typed env vars - Use
createRoute<'/path/[param]'>()for fully typed route params and loaders
Routing Exports
Route files support special exports:
// loader — server-side data fetching (tree-shaken from client)
export async function loader({ params, path, request }) { ... }
// generateStaticParams — expand dynamic SSG routes at build time
export async function generateStaticParams() { return [{ slug: 'hello' }] }
// sitemap — control sitemap.xml entry for this route
export const sitemap = { priority: 0.8, changefreq: 'daily', exclude: false }
Devtools
In development, press these shortcuts:
| Shortcut | Panel |
|---|---|
Alt+Space |
Spotlight menu |
Alt+S |
SEO Preview |
Alt+R |
Route Debug |
Alt+L |
Loader Timing |
Alt+P |
Route Preload |
Alt+E |
Error Panel |
Environment Variables
| Variable | Value |
|---|---|
VITE_ENVIRONMENT |
'client', 'ssr', 'ios', 'android' |
VITE_NATIVE |
'1' on native, '' on web |
EXPO_OS |
'web', 'ios', 'android' |
ONE_SERVER_URL |
Auto in dev, set manually in prod |
ONE_CACHE_KEY |
Per-build random key |
Client-safe prefixes: VITE_*, EXPO_PUBLIC_*, ONE_PUBLIC_*.
Platform Support
- Web: Browser via Vite
- iOS: React Native via Metro (recommended) or Vite
- Android: React Native via Metro (recommended) or Vite
- Not supported: Windows, Bun runtime for development