A modern full-stack TypeScript monorepo template for building applications on Cloudflare's edge platform.
Auth, database, UI components, dark mode — all wired up and ready to deploy.
Prerequisites: Bun and a Cloudflare account
bunx degit gielcobben/blehprint my-app
cd my-app
bun run rename my-appdegit downloads the template without git history, giving you a clean slate. The rename script updates all packages from @blehprint/* to @my-app/*.
bun installcp workers/web/.dev.vars.example workers/web/.dev.vars
openssl rand -base64 32Add the output to .dev.vars as BETTER_AUTH_SECRET.
bun run db:migrate:localbun run dev:webOpen http://localhost:3000.
- Runtime: Bun — Fast all-in-one JavaScript runtime
- Framework: React Router v7 — Full-stack React with SSR
- Hosting: Cloudflare Workers — Edge-first serverless
- Database: Cloudflare D1 + Drizzle ORM
- Auth: BetterAuth — Modern TypeScript authentication
- Styling: Tailwind CSS v4 + shadcn/ui
| Package | Description | Docs |
|---|---|---|
@blehprint/auth |
BetterAuth authentication | README |
@blehprint/database |
Drizzle ORM + D1 database | README |
@blehprint/ui |
shadcn/ui component library | README |
workers/web |
Main web application | README |
| Command | Description |
|---|---|
bun run dev:web |
Start development server |
bun run build:web |
Build for production |
bun run deploy:web |
Build and deploy to Workers |
bun run deploy:all |
Deploy everything |
bun run db:generate |
Generate Drizzle migrations |
bun run db:migrate:local |
Apply migrations locally |
bun run db:migrate:remote |
Apply migrations to production |
bun run db:studio |
Open Drizzle Studio |
bun run typecheck |
Run TypeScript checks |
bun run rename <name> |
Rename the project (one-time) |
import { getSession, requireAuth } from "@blehprint/auth";
import { env } from "cloudflare:workers";
// Check if authenticated
const session = await getSession(request, env.DB, env.BETTER_AUTH_SECRET);
// Require authentication (redirects to /login)
const session = await requireAuth(request, env.DB, env.BETTER_AUTH_SECRET);import { database } from "@blehprint/database";
import { env } from "cloudflare:workers";
const db = database(env.DB);
const users = await db.query.user.findMany();cd packages/ui && bunx shadcn@latest add buttonimport { Button } from "@blehprint/ui/components/button";Icons are provided by Lucide. The package is available in both @blehprint/ui and workers/web so you can import icons directly where you need them without re-exporting through the UI package:
import { ArrowRight, Loader2, Menu } from "lucide-react";
<ArrowRight className="size-4" />Browse all available icons at lucide.dev/icons. Only the icons you import are included in the bundle.
Dark/light mode powered by remix-themes.
Setup — The theme is configured in workers/web/app/root.tsx:
ThemeProviderwraps the app and syncs theme statePreventFlashOnWrongThemeprevents flash of wrong theme on SSR- Theme class (
dark/light) is applied to<html> - Theme action endpoint at
/api/themehandles persistence
Usage — Toggle theme anywhere with the useTheme hook:
import { useTheme } from "remix-themes";
function ThemeToggle() {
const [theme, setTheme] = useTheme();
return (
<button onClick={() => setTheme(theme === "dark" ? "light" : "dark")}>
Toggle theme
</button>
);
}Configuration — Cookie settings are in workers/web/app/utils/theme.server.ts. Update the domain for production.
bunx wrangler d1 create my-app-databaseCopy the database_id into packages/database/wrangler.jsonc and workers/web/wrangler.jsonc.
bun run db:migrate:remotebunx wrangler secret put BETTER_AUTH_SECRETbun run deploy:web # Deploy web worker
bun run deploy:all # Deploy everythingblehprint/
├── packages/
│ ├── auth/ # @blehprint/auth
│ ├── database/ # @blehprint/database
│ └── ui/ # @blehprint/ui
├── workers/
│ └── web/ # Main application
└── .wrangler/state/ # Local D1 state (gitignored)
This project is open source and available under the MIT License.