Expo Example
- Category: Examples
Experimental. This example is a work in progress. It shows how a React Native client uses Point0's queries, mutations, and components, but it isn't a polished path. For a real app, start from start0.
examples/expo is a React Native client built with Expo
that talks to a Point0 server. It is the opposite trade from
capacitor: capacitor keeps the Point0 client (Point0
builds the web bundle the shell wraps), while expo drops it. Point0 keeps
only the parts that don't touch the DOM — the root, queries,
mutations, and components — and Expo Router owns
routing while Metro bundles the client. The buttons above and below open the
full source.
The distinctive shape: the data hooks are the same ones you'd call in any Point0 app, but they live inside a plain Expo screen and route through Expo Router, not a Point0 page:
// examples/expo/src/app/index.tsx
import { useRouter } from 'expo-router'
import { ideasQuery, createIdeaMutation } from '../ideas'
export default function IdeasScreen() {
const router = useRouter()
const { data, refetch } = ideasQuery.useQuery() // a Point0 query
const mutation = createIdeaMutation.useMutation() // a Point0 mutation
// <FlatList ... onPress={() => router.push(`/${item.id}`)} /> ← routing is Expo Router
}Two things make the client-less setup work. The engine config is server only
(no client block), and server.importer.mock rewrites client-only imports
(react-native, expo-router) to an empty proxy at compile time so the same
point files load on the server too. On the client side there is no Point0 build
at all — a single Babel plugin runs the compiler with side: 'client',
stripping the server-only method bodies (loader, input, ctx, …) before
Metro bundles:
// examples/expo/babel.config.js
plugins: [['@point0/compiler/plugin/babel', { side: 'client', scope: 'root' }]]The scope here must match the rest of the app: the server is scope: 'root'
(src/engine.ts) and the root point is declared with
Point0.lets('root', 'root') (src/lib/root.tsx). A client compiled under a
different scope would resolve env.scope.* checks against the wrong scope name.
The root carries serverUrl (the client has no co-located server),
@point0/cors middleware (it's cross-origin), and a transformer, and its
.loading / .error render React Native components. One more quirk: expo
commits its points.server.ts and has no generate script — Metro and
Babel have no point0 generate integration, so a committed registry gives the
server a stable source to load. See compiler, importer,
and root.
Running it
Two processes, in two terminals: bun run dev:server (the Point0 server,
point0 dev --hot) and bun run dev (the Expo client, expo start; or
bun run ios / android / web). Set up SQLite with bun run prisma:generate
and bun run prisma:push. The server boots through the usual Point0 wiring. See
getting-started and engine-runtime.
For a real app
This example shows Point0 as a React Native data layer in isolation. For a real
app, start from start0 — the SaaS boilerplate
with the pieces already wired (bun create start0 my-app).
Enjoying Point0?
Star Point0
Start0
YouTube
Discord
Telegram
of the Lord Jesus Christ ☦️
With love for developers
of all backgrounds around the world ❤️