Full-stack TypeScript framework — React pages and a Hono API in one process. One repo, one dev, one deploy.
Anyone can use it. It is not locked to Pubflow products, accounts, or auth. A blog, a SaaS, an internal tool, or a Pubflow app all look the same: app/pages for UI, app/api for server code.
Hono owns fetch. TanStack Router is a library for file routes and SSR — not TanStack Start, not Next.js, not HonoX. The browser talks HTTP JSON to that same process. gRPC (or Hono RPC / hc) can sit behind Native if another service needs it; it is not a faster path for the UI.
Package: @pubflow/native
app/pages/ UI (layout.tsx, index.tsx, [id].tsx)
app/api/ Hono apps → /api/...
app/actions/ functions → POST /api/actions/<id>
app/server.ts optional — you own fetch
index.html
vite.config.ts plugins: [native()]
Pages cannot read DATABASE_URL. Put queries and secrets in app/api or app/actions. The browser only sees PUBFLOW_PUBLIC_* / VITE_*.
New app: clone starter/
That folder is the app. git clone on the repo URL would pull library, docs, and examples too. Clone only starter/:
npx degit pubflow/native/starter my-app
cd my-app
bun install
bun run devnpx degit copies starter/ onto my-app/ — same files you see in that GitHub tree, nothing else.
bun run dev # Vite + Hono (port 3000)
bun run build # client + SSR
bun run start # node dist/server/node.js
bun run deploy:cf # Cloudflare WorkerThe starter (Default) is a complete example: login/dashboard, Tailwind v4, and shadcn already wired (components.json, cn(), @/ → app/, a few UI files). Add more with the official CLI — npx shadcn@latest add dialog — not init -t vite. Delete or ignore auth if you do not use Flowless — Native does not require it. GET / and your own /api/* routes work with no Pubflow services running.
pubflow create native / pubflow start native copies starter/. native-minimal and native-custom-hono copy those example apps (also cloneable with degit). Cloudflare, auth, and shadcn are on Default — examples/cloudflare-worker, examples/with-auth, and examples/shadcn are notes, not templates. Minimal/Custom Hono have no Tailwind; run pubflow add shadcn then npx shadcn add.
Install the CLI (pubflow on npm — bins pubflow and pbfl). Pick the manager you already use:
npm install -g pubflow
pnpm add -g pubflow
yarn global add pubflow
bun add -g pubflowWithout a global install:
npx pubflow start native my-app
npx pubflow create native my-app
pnpm dlx pubflow start native my-app
yarn dlx pubflow create native my-app
bunx pubflow start native my-appbun add the library. You do not need the rest of Pubflow.
bun add @pubflow/native@0.1.4 @tanstack/react-router hono react react-dom
bun add -d vite// vite.config.ts
import { defineConfig } from 'vite'
import native from '@pubflow/native/vite'
export default defineConfig({
plugins: [native()],
})<!-- index.html -->
<div id="root"><!--ssr-outlet--></div>The plugin injects the client bundle. No main.tsx.
// app/pages/layout.tsx
import type { ReactNode } from 'react'
export default function Layout({ children }: { children: ReactNode }) {
return <div>{children}</div>
}
// app/pages/index.tsx
export default function HomePage() {
return <h1>Hello</h1>
}// app/api/hello.ts → GET /api/hello
import { Hono } from 'hono'
const hello = new Hono()
hello.get('/', (c) => c.json({ hello: true }))
export default hello// app/actions/ping.ts → POST /api/actions/ping
export async function ping() {
return { ok: true }
}GET /health is registered for you. bunx vite, then hit / and /api/hello.
Already on Hono? Keep your routes and mount pages for the rest:
import { pages } from '@pubflow/native/pages'
import { apiFromDir } from '@pubflow/native/api'
const app = new Hono()
app.route('/api', apiFromDir(import.meta.glob('./api/**/*.{ts,js}', { eager: true })))
app.all('*', pages())
export default appSee examples/ — Minimal and Custom Hono are cloneable apps; Cloudflare, with-auth, and shadcn point at Default.
- One TypeScript codebase instead of a separate frontend repo and API repo
- Same-origin
/apiandPOST /api/actionsso secrets stay on the server - Hono
fetchon Bun, Node, and Cloudflare Workers — no RSC, no Nitro, no gRPC for the UI - File routes you already know:
layout.tsx,index.tsx,[id].tsx
Use something else for mobile (pubflow create react-native / Expo), a non-TS API (Go, Python, …), or an MPA/islands setup (HonoX).
| File | Route |
|---|---|
app/pages/layout.tsx |
Nested layout (children) |
app/pages/index.tsx |
/ |
app/pages/dashboard/index.tsx |
/dashboard |
app/pages/[id].tsx |
/$id |
app/api/users.ts |
/api/users |
app/api/_middleware.ts |
Middleware for /api/* |
app/actions/posts/createPost.ts |
POST /api/actions/posts.createPost |
Default export is the page, layout, or Hono app. You do not write createFileRoute. Generated files live in .pubflow/generated/ (gitignored).
Optional env: browser PUBFLOW_PUBLIC_* or VITE_* (publicEnv()). Server uses normal names (DATABASE_URL, …). pubflow.config.ts is metadata only in v0.1.
The starter can talk to Flowless for login (@pubflow/react). That is an add-on, same as plugging any other auth. Skip it and Native is still a full-stack React + Hono app.
| Path | What it is |
|---|---|
library/ |
npm @pubflow/native |
starter/ |
Default app — auth, Tailwind, shadcn, Cloudflare. npx degit pubflow/native/starter / pubflow create native |
examples/minimal, examples/custom-hono-server |
other cloneable Native apps |
examples/cloudflare-worker, examples/with-auth, examples/shadcn, docs/ |
notes / docs — not templates |
Root package.json is private. After a library fix: publish npm, then bump the pin in starter/package.json.
More: docs/