Skip to main content

Command Palette

Search for a command to run...

React SPA with APIs on Cloudflare Worker

Updated
•4 min read•View as Markdown
React SPA with APIs on Cloudflare Worker

Cloudflare is amazing. Think of it as AWS’s thrifty cousin: it can do almost everything, but won’t drain your wallet. Then there’s Vercel, the flashy nephew in a sports car — a fun free ride, but only if you play by their rules.


Options

Vercel Functions

I tried a Vite React SPA with Vercel Functions for APIs, and it was almost too easy. Drop an endpoint .ts file in the api folder and it just works — no extra setup.
Still, Vercel feels like Heroku: trendy, convenient, but risky for a long-term relationship. I don’t want to be locked into their ecosystem.

AWS

I got burned by AWS in college — surprise bills and high usage costs. That was over a decade ago, but it left a scar.AWS has the “premium“ image, but for me it’s a no-go.

Google Cloud Run

Pixel suggested Google Cloud Run. But my history with Google App Engine wasn’t great — the app I built no longer runs because the frameworks were all decommissioned. Competitive pricing aside, I don’t want to step into Google’s ecosystem again.

Fly.io

Fly.io is neat: you provide a Docker image, and they’ll run it. But large images timed out for me, and the community feels smaller. I decided to skip it.

Cloudflare Workers

Now to my favorite: Cloudflare. It offers almost everything you need for SaaS infra at a fraction of AWS’s cost. Generous free tier, solid docs, and a company that’s been strong since 2004.
Their pricing is straightforward — more like Amazon retail than AWS. The Cloudflare brand stands for stability, consistency, and value.

The UI may look a little old-school, but that actually gives it a charming, no-frills vibe. No excessive border-radius here.

When I discovered Cloudflare Workers could handle both SPA hosting and API endpoints, I knew I had to try it.


Set Up Guide

The guide I followed:
https://developers.cloudflare.com/workers/vite-plugin/tutorial/

It’s exactly what I needed. Deploying an SPA with minimal APIs and high availability felt simple.

You install @cloudflare/vite-plugin and add it to vite.config.ts. After that, you can use the usual vite dev server. No ecosystem-specific commands like vercel dev or next dev.
I like that — it’s just a plugin, not a lock-in.

// vite.config.ts
export default defineConfig({
  plugins: […, cloudflare()]
})

Now, just run npm run dev. Your SPA, APIs, HMR, and React Refresh all work. The API even runs inside a proper V8 runtime like production.

You’ll need wrangler for deployments and a wrangler.jsonc config:

{
  "name": "sprite-dx",                               // Project name
  "compatibility_date": "2025-08-20",                // Just use today's date
  "assets": {
    "directory": "dist",                             // SPA to use when deployed.
    "not_found_handling": "single-page-application", // This routes all non-matching urls to the SPA
    "run_worker_first": ["/api", "/api/*"]           // Route /api/* to worker
  },
  "main": "./worker/index.ts"                        // API endpoint entrypoint.
}

Then add a worker-specific tsconfig.json:

// conf/tsconfig.worker.json 
{
  "extends": "./tsconfig.node.json",
  "compilerOptions": {
    "tsBuildInfoFile": "../node_modules/.tmp/tsconfig.worker.tsbuildinfo",
    "types": ["@cloudflare/workers-types/2023-07-01", "vite/client"],
  },
  "include": ["../worker"],
}

// tsconfig.json
{
  "files": [],
  "references": [
    { "path": "./conf/tsconfig.app.json" },
    { "path": "./conf/tsconfig.node.json" },
    { "path": "./conf/tsconfig.worker.json" }   // added
  ]
}

And your worker entrypoint:

// worker/index.ts
import type { ExportedHandler } from '@cloudflare/workers-types/2023-07-01';

export default {
  fetch(request) {
    const url = new URL(request.url);

    if (url.pathname.startsWith("/api/")) {
      return Response.json({
        name: "Cloudflare",
      });
    }

    return new Response(null, { status: 404 });
  },
} satisfies ExportedHandler;

At this point, npm run dev gives you a working SPA + API locally.


Deployment

In package.json, add:

   "scripts": {
     "dev": "vite",
     "build": "tsc -b && vite build",
     "lint": "eslint .",
     "preview": "vite preview",
+    "deploy": "npm run build && wrangler deploy"
   },

Deploy with:

npm run deploy

You don’t even need to create a project beforehand — the first deploy sets it up automatically. You’ll find it under Cloudflare > Compute (Workers) > Workers & Pages.


Yeah, the whole experience has been smooth. Next for me: adding environment variables, connecting it to RunPod Serverless with Comfy installed, and testing a full end-to-end image generation workflow at app.spritedx.com.

— Sprited Dev 🌱