Cloudflare Workers introduced support for static assets in September 2024 as part of the effort to unify Cloudflare Workers and Pages. That means that it’s now possible to host your frontend single-page applications on Workers. And it’s my favorite way to host SPAs so it’s worth showing you how to set it up.
TLDR: I created this repository with sample React, Vue, and Angular projects to showcase how these can be hosted on Workers with static assets.
Read on if you want to know why you might prefer using Workers static assets over Pages, and why each setup step is necessary.
Cloudflare Workers static assets vs. Cloudflare Pages
I prefer Workers to host my single-page applications because: 1) Workers are better integrated with the rest of the Cloudflare developer platform (see rate limiting, durable objects, observability, and other differences between Pages and Workers on the compatibility matrix). 2) Workers also give you full access to specify how to handle each API or HTTP request, which is more flexible than Pages’ Functions.
Pages does have some features that can be convenient for certain workflows, such as deploy hooks and branch deploy controls. It’s worth reviewing the compatibility matrix to see the tradeoffs.
How to get your single-page application hosted on Workers static assets
Hosting your single-page application on Workers varies if you want to have custom handling of routes & APIs in addition to your single-page application, or if you’re just hosting a single-page application/static website.
If you want to have Workers + single-page application hosted on Workers Static Assets:
-
Create a Cloudflare Workers project:
npm create cloudflare@latest
. SelectHello World example
. -
Create your single-page application within this Worker project
cd <WORKERS PROJECT NAME>
,npm create vite@latest
. -
Configure your
wrangler.toml
/wrangler.json
file to specify your single-page application’sdist
orbuild
folder. You also must specify the binding keyword ofASSETS
which will be used in the next step.//if using wrangler.toml assets = { binding = "ASSETS", directory = "./spa-app/dist" } //if using wrangler.json "assets": { "binding": "ASSETS", // only required when there is Workers code to configure navigation fallback "directory": "./spa-app/dist", }
-
At the end of your Workers code, you need to serve your static assets. This is necessary in order to handle navigation fallback (if a user directly visits a page that is not a static asset, nor handled by the API, your single-page application assets must be served since your 404 or custom pages need to be handled by the single-page application).
export default { async fetch(request, env) { const url = new URL(request.url); if (url.pathname.startsWith("/api/")) { // TODO: Add your custom /api/* logic here. return new Response("Ok"); } // Passes the incoming request through to the assets binding. // No asset matched this request, so this will evaluate `not_found_handling` behavior. return env.ASSETS.fetch(request); }, };
-
Change your
dev
,start
anddeploy
scripts in your Workers project’spackage.json
. These should first perform a build of the child single-page application project before running the Workers commands. In the below example,spa-app
contains our React/Angular/Vue/SPA app and callsnpm run build
(or otherwise configured) to build the SPA project.{ "name": "spa-on-workers-assets", "scripts": { "deploy": "cd spa-app && npm run build && cd .. && wrangler deploy", "dev": "cd spa-app && npm run build && cd .. && wrangler dev", "start": "cd spa-app && npm run build && cd .. && wrangler dev", ... }, ... }
Pro-tip: When developing locally, you may find yourself wanting some level of hot reloading. The above scripts don’t include this. Instead, you can open a separate terminal instance, navigate to the single-page application project, and run npx vite build --watch
in order to build your single-page application project on every save. You’ll then be able to refresh your Workers application in the browser and it will be serving the new version of your application.
If you only want to host your single-page application on Workers Static Assets (without APIs or custom Worker code):
-
Create a Cloudflare Workers project with static assets (only):
npm create cloudflare@latest -- --experimental
. SelectHello World example
andHello World - Assets-only
. -
Create your single-page application within this Worker project
cd <WORKERS PROJECT NAME>
,npm create vite@latest
. -
Configure your
wrangler.toml
/wrangler.json
file to specify your single-page application’sdist
orbuild
folder. You also must specify thenot_found_handling
mode ofsingle-page-application
.//if using wrangler.toml assets = { directory = "./spa-app/dist", not_found_handling = "single-page-application" } //if using wrangler.json "assets": { "directory": "./spa-app/dist", "not_found_handling": "single-page-application" // only required when there is no Workers code }
That’s it! That’s how to host single-page applications on Cloudflare Workers, whether you’re hosting only your single-page application or hosting your single-page application with a full Workers project. This GitHub repository contains the sample code showing how to host React, Angular and Vue applications on Workers with static assets, all configuration included.