Basic Example
- Category: Examples
examples/basic is the canonical Point0 app, IdeaNick — a collective ideas
blog where anyone posts an idea and adds news updates to it. It is the reference
the other four examples are described against: each of them is "the same app,
but with one piece swapped". The buttons above and below open it on CodeSandbox,
GitHub, and github.dev — the full source is one click away, so this page only
sketches the shape.
What it puts together, in one app:
- SSR for the first load, then client-side (SPA-style) navigation between pages
- the whole point family wired up — pages, layouts, queries, mutations, components — plus an MDX page
- Prisma + SQLite, type-safe routing through wouter, Tailwind v4, React 19 with the React Compiler
- a client bundled by Bun (this is the distinctive choice — the vite example bundles the same client with Vite instead)
- a file upload, infinite-query pagination, and an OpenAPI spec behind basic auth
Everything grows from the root — one builder chain that sets the serialization wire, the schema helper, the error class, the prefetch policy, the global head, the loading/error fallbacks, and the OpenAPI middleware:
// examples/basic/src/lib/root.tsx
export const root = Point0.lets
.root()
.serverUrl(sharedEnv.SERVER_URL)
.clientUrl(sharedEnv.CLIENT_URL)
.transformer(superjson) // Date/Map/etc survive the SSR wire
.schemaHelper(zodSchemaHelper()) // teach Point0 to read zod schemas
.errorClass(AppError) // your own error class (default is ErrorPoint0)
.head('global', () => ({
titleTemplate: '%s | IdeaNick',
htmlAttrs: { lang: 'en' },
}))
.loading(() => <LoadingCard />) // shown while any point's data is pending
.error(({ error }) => <ErrorCard error={error} />) // shown on a thrown error
.middleware(
openapi({
route: '/openapi.json',
filter: 'all',
before: basicAuth({ users: { admin: 'admin' } }),
}),
)
.root()Every page, query, and mutation in the app starts from root.lets. From there a
typical point is a short .lets chain — a server .loader (Prisma never ships
to the browser), then the closing call that picks its kind:
// examples/basic/src/lib/idea.ts — a query: a reusable, cacheable read
export const ideaViewQuery = root.lets
.query()
.input(z.object({ id: z.number() }))
.loader(async ({ input }) => ({
idea: await prisma.idea.findUniqueOrThrow({ where: { id: input.id } }),
}))
.query()For each piece — pages, layouts, queries, mutations, components, assets, file upload, infinite query, MDX, OpenAPI — follow the link to its own page; the source shows how they come together here.
Running it
From the repo root once, then inside examples/basic:
# repo root — build the @point0/* packages so the `point0` CLI exists
bun install
bun run build
# in examples/basic
bun run setup # SQLite DB + prisma generate + point0 generate + seed
bun run dev # point0 dev --hot — server + client, server hot reloadsrc/generated/ (the Prisma client and Point0's points/routes/assets) is
gitignored — bun run setup produces it. For production, bun run build
then bun run start. See getting-started, CLI, and
Dev.
For a real app
This example shows Point0 in isolation. For a real product, start from
start0 — the SaaS boilerplate with auth, admin,
forms, CRUD, email, and deploy already wired together
(bun create start0 my-app).
The other examples
The same blog appears in each, with one piece swapped:
- vite — same app, client bundled by Vite instead of Bun.
- better-auth — same app, plus Better Auth.
- capacitor — same app, wrapped in a native mobile shell. Experimental.
- expo — Point0's server and data layer behind a React Native client. Experimental.
Enjoying Point0?
Star Point0
Start0
YouTube
Discord
Telegram
of the Lord Jesus Christ ☦️
With love for developers
of all backgrounds around the world ❤️