
It is easy to talk about a web app through its visible features. The homepage. The editor. The upload flow. The account screen. The dashboard. The generated result that appears after a user clicks a button. Those parts matter, but they are not the only parts I check before shipping. For a full-stack React app, I want to know where the server-side really runs, what assumptions it is allowed to make, and which deployment command will turn the local project into a production Worker. That boundary matters even more for an AI image website like Flux 2, where routes, user state, generated media, and admin workflows can all end up touching server-side code. This is why I spend time reading the configuration. TanStack Start gives the app a full-stack shape. Cloudflare Workers gives it an edge runtime. Wrangler is the command-line layer that turns the project into something deployable. The important question is not only "does it deploy?" The better question is: Can I tell, from the configuration, what runtime this app is being built for? Runtime Drift Is a Real Problem A lot of web apps start with local convenience. You install packages. You run a dev server. You add routes. You add server functions. You call a helper from a route loader. Everything works locally, so the project feels healthy. Then the production runtime gets involved. Maybe a dependency expects a Node.js API that is not available. Maybe a server function accidentally assumes filesystem access. Maybe an environment binding exists in production but not in preview. Maybe the build output and the Worker entry point do not describe the same app. These are not dramatic architecture failures. They are smaller forms of runtime drift. Runtime drift happens when the code is developed under one set of assumptions and deployed under another. The goal is not to remove every difference between local and production. That is not realistic. The goal is to make the important differences visible early. That is where TanStack Start, the Cloudflare Vite plugin, and Wrangler configuration need to agree with each other. Vite Should Know the Server Target For a TanStack Start app, Vite is not just a frontend bundler sitting off to the side. It participates in the full-stack build. If the app is going to run on Cloudflare Workers, I want to see that reflected in vite.config.ts . The shape I expect is close to this: import { cloudflare } from "@cloudflare/vite-plugin"; import { tanstackStart } from "@tanstack/react-start/plugin/vite"; import react from "@vitejs/plugin-react"; import { defineConfig } from "vite"; export default defineConfig({ plugins: [ cloudflare({ viteEnvironment: { name: "ssr" } }), tanstackStart(), react(), ], }); The exact file may be different in a real project. There may be aliases, test settings, analytics code, or other plugins. The point is simpler: the server-side part of the app should be intentionally wired for the runtime that will execute it. The official Cloudflare guide for TanStack Start follows this direction. It treats the Cloudflare runtime as part of the build story, not as an afterthought that appears only during deployment. That detail matters when the app grows. Server functions, route loaders, auth checks, pricing state, image metadata, and admin actions all need predictable runtime assumptions. If those assumptions are implicit, the app becomes harder to reason about. Wrangler Is Not Just a Deploy Button I do not think of Wrangler configuration as deployment decoration. It is closer to an operational contract. A small wrangler.jsonc file can tell me a lot: { "$schema": "node_modules/wrangler/config-schema.json", "name": "example-tanstack-start", "compatibility_date": "2026-06-04", "compatibility_flags": ["nodejs_compat"], "main": "@tanstack/react-start/server-entry", "observability": { "enabled": true } } This is not a complete production file. It is a useful starting point. It tells me the Worker name. It tells me the compatibility date. It tells me whether Node.js compatibility is expected. It tells me the server entry point. It tells me whether basic observability is enabled. That is already more than a deploy target. It is a readable statement of how the app expects to run. Bindings Should Prove a Product Need Cloudflare makes it easy to add resources. R2, D1, KV, Queues, Service Bindings, Cron Triggers, and other pieces can all be useful. That does not mean they should all appear in the configuration early. I like bindings that prove a product need. If generated media must be stored by the app, an object storage binding makes sense. If lightweight relational state belongs near the Worker, a database binding may make sense. If slow work should not block a request, a queue binding may make sense. If one responsibility needs to move into another Worker, a service binding may make sense. The important thing is that each binding should answer a question: Which workflow depends on this resource? If nobody can answer that, the binding is probably early. This matters because configuration is documentation whether we intend it to be or not. A future developer reading wrangler.jsonc will assume listed resources are real dependencies. Unused dependencies make the map harder to trust. Environment Names Should Be Boring The staging and production boundary should not depend on memory. I want the environment names to be obvious, and I want the Worker names to be obvious: { "env": { "staging": { "name": "example-tanstack-start-staging" }, "production": { "name": "example-tanstack-start-production" } } } This is not clever. That is the advantage. The deployment commands can then read like an operational checklist: pnpm build wrangler deploy --env staging --dry-run wrangler deploy --env staging wrangler tail --env staging wrangler deploy --env production --dry-run wrangler deploy --env production wrangler tail --env production The dry-run step is useful because it slows down the release just enough to inspect the target. The tail step is useful because obvious runtime mistakes often show up immediately after deployment. I do not want deployments to feel impressive. I want them to feel inspectable. Prerendering Needs a Data Boundary Prerendering can be useful in a TanStack Start app, especially on Cloudflare where static assets can be served efficiently. But I do not want prerendering treated as a global optimization switch. The real question is when a route is allowed to read data. Marketing pages, documentation-style pages, and static comparison pages may be good candidates for build-time output. Signed-in dashboards, generation history, billing state, and admin workflows are different. They usually depend on request-time data. That distinction is easy to describe and easy to forget. Build-time data is for pages that can be decided before the request. Request-time data is for pages that depend on the current user or current system state. Once that boundary is clear, prerendering becomes a useful tool instead of a source of stale or confusing pages. Package Scripts Should Explain the Workflow I like package scripts that make the common path obvious: { "scripts": { "dev": "vite dev", "build": "vite build", "preview": "vite preview", "deploy": "pnpm build && wrangler deploy", "cf-typegen": "wrangler types" } } There is no special trick here. That is the point. dev is local work. build proves the app compiles. preview gives a way to inspect the built output. deploy keeps the build step attached to release. cf-typegen helps keep Worker bindings typed when the project starts depending on platform resources. The scripts are not the architecture, but they influence how people move through the architecture every day. The Boundary Is the Product Work Users do not care about Vite plugins, Worker entry points, compatibility dates, or wrangler deploy . They care whether the page loads, whether the app responds, whether their generated media is available, and whether account flows behave consistently. That visible experience depends on invisible boundaries. The Vite config says what the app is built for. The Wrangler config says how the app runs. Bindings say which platform resources the app depends on. Environment sections say where a release goes. Package scripts say how developers operate the project. For a small project, this can stay small. For a growing production app, it can grow carefully. The goal is not to make configuration complex. The goal is to make the runtime boundary clear enough that future changes do not feel like guesses. That is the part I want before shipping: not a perfect architecture, but a configuration surface that tells the truth about how the app runs.
View original source — Hacker Noon ↗


