The main web application for Blehprint, built with React Router v7 and deployed to Cloudflare Workers.
- Framework: React Router v7 — Full-stack React framework with SSR
- Runtime: Cloudflare Workers — Edge-first serverless compute
- UI: @blehprint/ui — Shared component library
- Styling: Tailwind CSS v4
workers/web/
├── app/
│ ├── routes/ # Route modules (loaders, actions, meta)
│ ├── pages/ # Pure React page components
│ ├── utils/ # Shared utilities
│ ├── root.tsx # Root layout component
│ ├── routes.ts # Route configuration
│ ├── app.css # Global styles
│ └── entry.*.ts # Entry points (client, server, worker)
├── public/ # Static assets
├── wrangler.jsonc # Cloudflare Workers config
└── vite.config.ts # Vite configuration
Route files handle all the heavy lifting:
loader— Server-side data fetching, authentication checks, redirectsaction— Form submissions, mutations, API callsmeta— Page title and SEO metadata- Default export — Thin wrapper that passes loader data to page components
// routes/home.tsx
import { HomePage } from "../pages/home";
export function meta() {
return [{ title: "Home" }];
}
export async function loader({ request }: Route.LoaderArgs) {
const session = await getSession(request);
return { session };
}
export default function HomeRoute({ loaderData }: Route.ComponentProps) {
return <HomePage session={loaderData.session} />;
}Page files contain only presentational React components:
- No server code — No loaders, actions, or server imports
- Props-driven — Receive all data via props from route files
- Focused on UI — Layout, styling, and user interactions
- Easily testable — Can be rendered in isolation with mock props
// pages/home.tsx
export function HomePage({ session }: { session: SessionData }) {
return (
<main>
<h1>Welcome, {session?.user.name ?? "Guest"}</h1>
</main>
);
}Start the development server:
# From the monorepo root
bun run dev:web
# Or from this directory
bun run devThe app will be available at http://localhost:3000.
Import components from the shared UI package:
import { Button } from "@blehprint/ui/components/button";
import { cn } from "@blehprint/ui/lib/utils";
export function MyPage() {
return (
<div className={cn("container", "py-8")}>
<Button>Click me</Button>
</div>
);
}The global styles are already imported in app/app.css.
bun run buildDeploy to Cloudflare Workers:
# From the monorepo root
bun run deploy:web
# Or from this directory
bun run deployCopy the example environment file:
cp .dev.vars.example .dev.varsRequired variables:
| Variable | Description |
|---|---|
BETTER_AUTH_SECRET |
Secret key for BetterAuth sessions |
For production, set secrets using Wrangler:
bunx wrangler secret put BETTER_AUTH_SECRETMIT