Cloudflare Web App — House Stack
The reference for what to pick when starting a new web project. Every choice
here is already running in production across the other repos, so matching it
means CI, hooks, deploy, and agent workflows all behave the way they do
elsewhere.
Cloudflare is the infra and the backend. Workers, D1, KV, R2, Durable
Objects, Workers AI, Queues. Not AWS, not Vercel, not Netlify, not Supabase.
Reach outside Cloudflare only for something Cloudflare genuinely lacks (Stripe,
Resend, npm registry) and say so explicitly.
Two modes
Reference mode (default) — the user described a project and wants the stack.
Read the tables below, pick the project shape, read the reference files that
matter for that shape, and report the recommended stack with concrete versions
and file names. Do not write files.
Scaffold mode — the user passed --scaffold or asked to actually create the
project. Do reference mode first, confirm the shape, then copy from
assets/templates/ and adapt. See "Scaffolding" below.
Step 1 — Pick the project shape
| Shape |
When |
Frontend |
Deploy target |
| Content site |
Marketing, blog, portfolio, landing |
zfb + Preact + Tailwind v4 |
Workers Static Assets |
| Docs site |
Reference docs, manuals, changelog |
zudo-doc (a zfb preset) |
Workers Static Assets |
| SSR app |
Auth, forms, DB-backed pages |
zfb SSR routes (prerender = false) + CF adapter |
Workers Static Assets + bindings |
| API / worker |
JSON API, webhook, proxy, cron, agent |
Hono on Workers, no frontend |
Workers |
| Interactive app |
Editor, dashboard, canvas, heavy client state |
Vite + React + Tailwind v4 |
Workers Static Assets (+ separate API worker) |
| Library / CLI |
Publishable npm package |
none |
npm — use /dev-npm-package instead |
Mixed projects are normal: a pnpm workspace with a zfb site at the root and
worker packages under workers/*. Split when the deploy units differ.
Step 2 — The non-negotiable base
Every repo gets these regardless of shape. Details and rationale in
references/tooling.md.
| Concern |
Choice |
| Package manager |
pnpm, packageManager pinned in package.json, engines.node >= 22 |
| Language |
TypeScript, strict: true |
| Unit tests |
vitest |
| Worker tests |
@cloudflare/vitest-pool-workers |
| E2E |
Playwright |
| Format |
Prettier for code, @takazudo/mdx-formatter for .md/.mdx |
| Lint |
ESLint flat config (eslint.config.js) |
| Git hooks |
lefthook (lefthook.yml) + prepare script |
| Pre-push gate |
pnpm b4push — mirrors CI locally |
| CI |
GitHub Actions, SHA-pinned actions |
| Deploy |
wrangler deploy from GitHub Actions |
| Dep updates |
Renovate (preferred) or Dependabot |
Step 3 — Read the reference for what you're deciding
Load only what the current decision needs.
references/stack.md — every library choice with its
rationale, plus the explicit do-not-use list (Pages, Next.js, AWS SDK,
Prisma, Jest, styled-components, …). Read this for any "what should we use
for X" question.
references/cloudflare.md — wrangler.toml
anatomy, Workers Static Assets, bindings (D1/KV/R2/DO/AI/Queues/services),
named environments, preview aliases, secrets vs vars, and the TOML/config
gotchas that cost real debugging time.
references/ci.md — the standard workflow set, SHA pinning,
concurrency, self-skipping deploy jobs, PR preview comments, actionlint,
security audit.
references/tooling.md — pnpm settings, workspace
layout, lefthook, prettier, eslint, mdx-formatter, design-token-lint,
tsconfig, the b4push script.
references/testing.md — test levels and tiers, what
runs in b4push vs CI vs nightly, Playwright conventions.
references/conventions.md — directory layout,
CLAUDE.md, docs site, README, worktrees, .gitignore, scratch dirs.
For Cloudflare platform API detail (exact binding APIs, runtime limits,
current wrangler flags), defer to the /cloudflare, /wrangler,
/workers-best-practices, /durable-objects, and /agents-sdk skills — they
retrieve live docs. This skill owns the opinion; those own the facts.
Step 4 — Report
Report as a short stack table (shape, framework, styling, data, tests, CI,
deploy target) plus the list of files the project needs. Name the Cloudflare
resources that must be provisioned (D1 database, KV namespace, R2 bucket,
custom domain) and the secrets that must be set — these are manual, one-time,
and easy to forget.
Scaffolding
Only in scaffold mode. Copy from assets/templates/ and adapt — every file
carries comments explaining the traps, so keep them.
| Template |
Copy to |
Notes |
package.json |
package.json |
Replace latest with resolved versions after the first install |
pnpm-workspace.yaml |
same |
Delete if single-package |
npmrc |
.npmrc |
|
gitignore |
.gitignore |
|
prettierignore |
.prettierignore |
|
.prettierrc.json |
same |
|
tsconfig.json |
same |
|
lefthook.yml |
same |
|
wrangler.toml |
same |
Strip unused binding blocks |
.github/workflows/*.yml |
same |
ci.yml, deploy.yml, actionlint.yml |
scripts/run-b4push.sh |
same |
chmod +x; keep steps identical to ci.yml |
scripts/smoke.mjs |
same |
Fill in EXPECTED with real page markers |
scripts/install-git-hooks.sh, scripts/hooks/pre-push |
same |
chmod +x both |
cloudflare-setup.md |
docs/cloudflare-setup.md |
|
Three templates ship without their leading dot (npmrc, gitignore,
prettierignore) so they stay inert inside this skill's own repo — rename on
copy.
Then:
Replace every __PROJECT_NAME__ and __DOMAIN__ placeholder
Write README.md and CLAUDE.md (outline in
references/conventions.md)
pnpm install, then confirm pnpm b4push passes before the first push
Do not invent Cloudflare resource ids. Leave REPLACE_ME plus the
wrangler ... create command that produces it — the deploy job self-skips until
the real id is committed.
1---2name: dev-basic-cloudflare-webapp3description: The house tech stack for a new web project on Cloudflare — framework, lint, test, CI, docs, and repo conventions, with copy-ready config templates. Cloudflare is the default infra and backend (Workers, D1, KV, R2, Durable Objects); AWS is not. Use this whenever a new web app, site, API, or worker is being started or scaffolded, whenever someone asks 'what stack should we use', 'what should I pick for X', 'set up CI/lint/test for this project', or 'scaffold a new project' — even if Cloudflare is never named, because Cloudflare is the assumed target. Also use when adding CI, lint, formatting, git hooks, deploy workflows, or a docs site to an existing project so it matches the other repos. Keywords: new project, scaffold, boilerplate, starter, tech stack, tech choice, Cloudflare, Workers, wrangler, D1, KV, R2, Durable Objects, zfb, zudo-doc, pnpm, lefthook, vitest, playwright, GitHub Actions, b4push.4---56# Cloudflare Web App — House Stack78The reference for what to pick when starting a new web project. Every choice9here is already running in production across the other repos, so matching it10means CI, hooks, deploy, and agent workflows all behave the way they do11elsewhere.1213**Cloudflare is the infra and the backend.** Workers, D1, KV, R2, Durable14Objects, Workers AI, Queues. Not AWS, not Vercel, not Netlify, not Supabase.15Reach outside Cloudflare only for something Cloudflare genuinely lacks (Stripe,16Resend, npm registry) and say so explicitly.1718## Two modes1920**Reference mode (default)** — the user described a project and wants the stack.21Read the tables below, pick the project shape, read the reference files that22matter for that shape, and report the recommended stack with concrete versions23and file names. Do not write files.2425**Scaffold mode** — the user passed `--scaffold` or asked to actually create the26project. Do reference mode first, confirm the shape, then copy from27`assets/templates/` and adapt. See "Scaffolding" below.2829## Step 1 — Pick the project shape3031| Shape | When | Frontend | Deploy target |32| --- | --- | --- | --- |33| **Content site** | Marketing, blog, portfolio, landing | zfb + Preact + Tailwind v4 | Workers Static Assets |34| **Docs site** | Reference docs, manuals, changelog | zudo-doc (a zfb preset) | Workers Static Assets |35| **SSR app** | Auth, forms, DB-backed pages | zfb SSR routes (`prerender = false`) + CF adapter | Workers Static Assets + bindings |36| **API / worker** | JSON API, webhook, proxy, cron, agent | Hono on Workers, no frontend | Workers |37| **Interactive app** | Editor, dashboard, canvas, heavy client state | Vite + React + Tailwind v4 | Workers Static Assets (+ separate API worker) |38| **Library / CLI** | Publishable npm package | none | npm — use `/dev-npm-package` instead |3940Mixed projects are normal: a pnpm workspace with a zfb site at the root and41worker packages under `workers/*`. Split when the deploy units differ.4243## Step 2 — The non-negotiable base4445Every repo gets these regardless of shape. Details and rationale in46[references/tooling.md](references/tooling.md).4748| Concern | Choice |49| --- | --- |50| Package manager | pnpm, `packageManager` pinned in package.json, `engines.node >= 22` |51| Language | TypeScript, `strict: true` |52| Unit tests | vitest |53| Worker tests | `@cloudflare/vitest-pool-workers` |54| E2E | Playwright |55| Format | Prettier for code, `@takazudo/mdx-formatter` for `.md`/`.mdx` |56| Lint | ESLint flat config (`eslint.config.js`) |57| Git hooks | lefthook (`lefthook.yml`) + `prepare` script |58| Pre-push gate | `pnpm b4push` — mirrors CI locally |59| CI | GitHub Actions, SHA-pinned actions |60| Deploy | `wrangler deploy` from GitHub Actions |61| Dep updates | Renovate (preferred) or Dependabot |6263## Step 3 — Read the reference for what you're deciding6465Load only what the current decision needs.6667- [references/stack.md](references/stack.md) — every library choice with its6869 rationale, plus the explicit **do-not-use** list (Pages, Next.js, AWS SDK,70 Prisma, Jest, styled-components, …). Read this for any "what should we use71 for X" question.7273- [references/cloudflare.md](references/cloudflare.md) — `wrangler.toml`7475 anatomy, Workers Static Assets, bindings (D1/KV/R2/DO/AI/Queues/services),76 named environments, preview aliases, secrets vs vars, and the TOML/config77 gotchas that cost real debugging time.7879- [references/ci.md](references/ci.md) — the standard workflow set, SHA pinning,8081 concurrency, self-skipping deploy jobs, PR preview comments, actionlint,82 security audit.8384- [references/tooling.md](references/tooling.md) — pnpm settings, workspace8586 layout, lefthook, prettier, eslint, mdx-formatter, design-token-lint,87 tsconfig, the b4push script.8889- [references/testing.md](references/testing.md) — test levels and tiers, what9091 runs in b4push vs CI vs nightly, Playwright conventions.9293- [references/conventions.md](references/conventions.md) — directory layout,9495 `CLAUDE.md`, docs site, README, worktrees, `.gitignore`, scratch dirs.9697For Cloudflare **platform** API detail (exact binding APIs, runtime limits,98current wrangler flags), defer to the `/cloudflare`, `/wrangler`,99`/workers-best-practices`, `/durable-objects`, and `/agents-sdk` skills — they100retrieve live docs. This skill owns the *opinion*; those own the *facts*.101102## Step 4 — Report103104Report as a short stack table (shape, framework, styling, data, tests, CI,105deploy target) plus the list of files the project needs. Name the Cloudflare106resources that must be provisioned (D1 database, KV namespace, R2 bucket,107custom domain) and the secrets that must be set — these are manual, one-time,108and easy to forget.109110## Scaffolding111112Only in scaffold mode. Copy from `assets/templates/` and adapt — every file113carries comments explaining the traps, so keep them.114115| Template | Copy to | Notes |116| --- | --- | --- |117| `package.json` | `package.json` | Replace `latest` with resolved versions after the first install |118| `pnpm-workspace.yaml` | same | Delete if single-package |119| `npmrc` | `.npmrc` | |120| `gitignore` | `.gitignore` | |121| `prettierignore` | `.prettierignore` | |122| `.prettierrc.json` | same | |123| `tsconfig.json` | same | |124| `lefthook.yml` | same | |125| `wrangler.toml` | same | Strip unused binding blocks |126| `.github/workflows/*.yml` | same | `ci.yml`, `deploy.yml`, `actionlint.yml` |127| `scripts/run-b4push.sh` | same | `chmod +x`; keep steps identical to `ci.yml` |128| `scripts/smoke.mjs` | same | Fill in `EXPECTED` with real page markers |129| `scripts/install-git-hooks.sh`, `scripts/hooks/pre-push` | same | `chmod +x` both |130| `cloudflare-setup.md` | `docs/cloudflare-setup.md` | |131132Three templates ship without their leading dot (`npmrc`, `gitignore`,133`prettierignore`) so they stay inert inside this skill's own repo — rename on134copy.135136Then:1371381. Replace every `__PROJECT_NAME__` and `__DOMAIN__` placeholder1392. Write `README.md` and `CLAUDE.md` (outline in140141 [references/conventions.md](references/conventions.md))1421433. `pnpm install`, then confirm `pnpm b4push` passes before the first push144145Do not invent Cloudflare resource ids. Leave `REPLACE_ME` plus the146`wrangler ... create` command that produces it — the deploy job self-skips until147the real id is committed.