We built our sister agency's entire website and lead system as one Cloudflare Worker: React 19 with TanStack Start, 74 file-based routes server-rendered at the edge, D1 as the database behind 25 tracked migrations, and Workers AI powering the on-site assistant. No origin server exists.
This is the site for CurrentAds, a performance-advertising agency that shares ownership with TrainYourAgent. We are labelling that plainly rather than presenting it as a third-party client engagement, because an unlabelled sister-company logo on a portfolio is exactly the kind of quiet misdirection this page exists to avoid. The requirement was ordinary and the constraint was not. An agency site needs marketing pages, a multi-step audit funnel, lead capture, a CRM-ish back office, billing hooks and an on-site assistant. The constraint was that it should have no origin server to operate, no container to patch, and no cold-start penalty for a visitor in any geography — because an agency that cannot keep its own site fast has an awkward sales conversation ahead of it. The conventional answer is a static site plus a pile of serverless functions plus a hosted database, which is three vendors, three deployment models and three places for state to diverge. We wanted one deployable artefact.
TanStack Start's Vite build emits `dist/server/server.js` — a module with a default `fetch` export. That is exactly the shape a Cloudflare Worker takes, so the Worker entry point *is* the server-side rendering bundle. There is no adapter shim and no separate function layer. `wrangler.jsonc` points `main` at the built server and `compatibility_flags` carries `nodejs_compat`. Static assets are served asset-first from `dist/client` through the `ASSETS` binding, with `not_found_handling: none` so that any non-asset path — including `/` — falls through to the SSR Worker. That last detail is the one that bites people: no `index.html` is emitted, so `/` never matches an asset and always renders server-side. The configuration is commented with an explicit warning not to set `run_worker_first`, because doing so makes the Worker intercept `/assets/*` and 404 them, since the SSR handler has no asset route. That comment exists because someone learned it the hard way. The result is a single `wrangler deploy` producing one versioned artefact that contains the routing, the rendering, the API handlers and the asset manifest. Rollback is one command.
Routing is file-based under `src/routes` — 74 `.tsx` route files, 80 files in total once route-adjacent modules are counted. TanStack Router generates the route tree from the filesystem at build time, so the type of every route's params and loader data is checked by `tsc` rather than asserted at runtime. In a site with this many pages, that is the difference between a broken link being a compile error and being a customer's problem. The database is Cloudflare D1 — SQLite at the edge, bound directly into the Worker, with no connection pool to size and no cold-start connection cost. There are 25 tracked migration files, and reading their names is a decent history of the business: `0002_core`, `0004_lead_score`, `0006_newsletter`, `0009_sms_consent`, `0011_client_intake`, `0014_billing`, `0015_crm`, `0016_attribution`, `0019_delivery_evidence`, `0020_proof`, `0022_staff_pipeline`. The site assistant runs on Workers AI through the `AI` binding, which needs no API key and no third-party egress: inference happens on Cloudflare, against the deploying account, and every call is server-side. That has a security consequence worth naming — because the assistant never runs from the browser, adding it required no Content-Security-Policy change and sends nothing to a third party.
The server surface lives under `src/lib/api` as seventeen `*.functions.ts` modules, and reading the list tells you what this site actually is rather than what it looks like: `leads`, `intake`, `crm`, `portal`, `billing`, `apikeys`, `auth`, `reset`, `delivery`, `report-draft`, `case-studies`, `newsletter`, `feedback`, `traffic`, `chat`, `account-assistant` and `ops`. That is not a brochure with a contact form. It is a lead pipeline, a client portal with API-key issuance, a billing surface, a delivery-evidence trail and an internal operations console, all inside the same deployable Worker as the marketing pages. TanStack Start's server functions are what make that coherent: a server function is called like a typed function from the client and executes on the Worker, so there is no separate API layer to keep in sync and no hand-written fetch wrapper to drift from its endpoint. Scheduled work lives in a second, separate Worker under `cron/`, which is the right split — a request-serving Worker and a scheduled Worker have different failure modes, different observability needs and different deployment cadences, and coupling them means a cron bug can take the site down. Three internal packages (`fnf`, `fnf-react`, `quanta`) carry shared primitives, and there are focused Vitest suites over the two pieces of logic most likely to break quietly: the assistant and the tracking gate.
The most interesting file in the repository is `wrangler.jsonc`, and specifically its header comment: it declares itself the build and local-development config, and states that it is never trusted at deploy. The deploy pipeline generates a fresh, authoritative `wrangler.jsonc` from trusted metadata — `app.manifest.json` plus the platform's own record — and ignores everything in the checked-in file. The `name` and binding IDs in the repository are explicitly build-valid placeholders. Infrastructure is declarative and default-deny: `app.manifest.json` is four lines (`db: true`, `r2: false`, `kv: false`, `durableObject: null`), and nothing is provisioned unless it is declared there. Secrets are injected on server-side egress by a platform outbound Worker and are never present in the Worker's own environment. That is a stronger posture than most production deployments manage. A checked-in config cannot leak a real resource ID because it does not contain one, and a compromised build cannot exfiltrate a credential the Worker never holds.
Server-side rendering React 19 inside a Workers runtime rather than Node. Workers is not Node — there is no filesystem, the module graph is resolved differently, and a dependency that reaches for a Node built-in fails at deploy rather than at install. `nodejs_compat` covers a lot of it and does not cover all of it, so every dependency in a fairly large tree (Three.js, GSAP, Radix, Recharts, a form stack) has to be viable in that runtime. This is the work that does not appear anywhere in the finished site and consumed a real fraction of the build. Asset-first routing with no emitted `index.html` is a genuinely subtle configuration. The failure modes are asymmetric and both are bad: get it slightly wrong one way and `/` returns a 404; get it wrong the other way and every static asset is intercepted by an SSR handler with no route for it. The correct configuration is three lines and took real debugging to arrive at, which is why it is commented in the repository rather than left for the next person to rediscover. Then there is the ordinary discipline that a marketing site fights against: 74 routes is enough that duplicated layout logic, drifting metadata and orphaned pages become inevitable without a generated route tree and type-checked loaders. The type system is doing genuine architectural work here, not decoration.
Built with React 19.2, TanStack Start 1.167 / Router 1.168, Cloudflare Workers, Cloudflare D1, Workers AI, Vite, Tailwind CSS 4, Radix UI, Three.js / React Three Fiber, GSAP + Lenis and Bun. Relationship: Sister company. Stage: In production. Period: 2026. Our role: Full build — architecture, routing, edge deployment, data layer Figures re-derived on 2026-08-23.