Bun SPA
Quick guide to initializing and deploying a Bun-based React application to Cloudflare Workers using Alchemy.
This guide shows how to initialize and deploy a Bun-based React TypeScript application to Cloudflare using Alchemy. BunSPA provides a full-stack development experience with Bun’s frontend tooling and Cloudflare Workers for the backend.
You have two options to get started:
Option 1: Use Alchemy Create (Recommended)
Section titled “Option 1: Use Alchemy Create (Recommended)”Start by creating a new Bun SPA project with Alchemy:
bunx alchemy create my-bun-app --template=bun-spacd my-bun-app
npx alchemy create my-bun-app --template=bun-spacd my-bun-app
pnpm dlx alchemy create my-bun-app --template=bun-spacd my-bun-app
yarn dlx alchemy create my-bun-app --template=bun-spacd my-bun-app
Option 2: Add Alchemy to an Existing Bun Project
Section titled “Option 2: Add Alchemy to an Existing Bun Project”If you’ve already initialized a Bun project with bun init
(which supports Tailwind CSS, shadcn/ui, and other templates), you can add Alchemy to it using alchemy init
:
# If you haven't already, initialize your Bun projectbun init
# Add Alchemy to the existing projectbun alchemy init --framework bun-spa
The alchemy init
command will:
- Create an
alchemy.run.ts
file with BunSPA configuration - Validate or create
bunfig.toml
with requiredenv='BUN_PUBLIC_*'
configuration - Add Alchemy scripts to your
package.json
(deploy
,destroy
,alchemy:dev
) - Install Alchemy as a dev dependency
After initialization, update the paths in alchemy.run.ts
to match your project structure:
export const bunsite = await BunSPA("website", { frontend: "src/index.html", // adjust to match your HTML entrypoint(s) entrypoint: "src/server.ts", // adjust to match your backend API entrypoint});
Log in to Cloudflare
Section titled “Log in to Cloudflare”Authenticate once with your Cloudflare account:
bun alchemy login
npx alchemy login
pnpm alchemy login
yarn alchemy login
Deploy
Section titled “Deploy”Run the deploy script generated by the template:
bun run deploy
npm run deploy
pnpm run deploy
yarn run deploy
You’ll get the live URLs of your application:
{ url: "https://website.<your-account>.workers.dev", apiUrl: "https://website.<your-account>.workers.dev"}
Local Development
Section titled “Local Development”Work locally using the dev script:
bun run dev
npm run dev
pnpm run dev
yarn run dev
This starts both Bun’s dev server for the frontend (with hot module reloading) and Miniflare for the backend API.
Destroy
Section titled “Destroy”Clean up all Cloudflare resources created by this stack:
bun run destroy
npm run destroy
pnpm run destroy
yarn run destroy
What files are created
Section titled “What files are created”Alchemy requires a locally set password to encrypt Secrets that are stored in state. Be sure to change this.
ALCHEMY_PASSWORD=change-me
alchemy.run.ts
Section titled “alchemy.run.ts”alchemy.run.ts
is your infrastructure as code with Alchemy.
Alchemy commands such as alchemy dev
and alchemy deploy
use alchemy.run.ts
as their entrypoint.
Import types from alchemy.run.ts
into your application code to get runtime types for Cloudflare Bindings.
bunfig.toml
Section titled “bunfig.toml”BunSPA requires a bunfig.toml
file to expose BUN_PUBLIC_*
environment variables to the frontend during development:
[serve.static]env='BUN_PUBLIC_*'
tsconfig.json
Section titled “tsconfig.json”tsconfig.json
is created including alchemy.run.ts
and registering @cloudflare/workers-types
and bun-env.d.ts
globally
How it works
Section titled “How it works”BunSPA provides a full-stack development experience:
- Frontend: Bun’s native dev server serves your HTML entrypoints with hot module reloading
- Backend: Miniflare runs your Cloudflare Worker locally with access to bindings (KV, D1, R2, etc.)
- Deployment: Both are deployed together to Cloudflare Workers with static assets
Multiple HTML files
Section titled “Multiple HTML files”You can serve multiple HTML pages by passing an array to the frontend
property:
const bunsite = await BunSPA("website", { frontend: ["src/index.html", "src/about.html"], entrypoint: "src/worker.ts",});
Accessing the backend from the frontend
Section titled “Accessing the backend from the frontend”Use the getBackendUrl
utility to reliably connect to your backend API:
import { getBackendUrl } from "alchemy/cloudflare/bun-spa";
const apiBaseUrl = getBackendUrl();
// Make API requests in your frontentfetch(new URL('api/request/path', apiBaseUrl)) .then(res => res.json()) .then(data => console.log(data));
This utility automatically uses BUN_PUBLIC_BACKEND_URL
in development (injected by Alchemy) and falls back to the current origin in production, allowing your frontend to communicate seamlessly with your backend in both environments.
Hot Module Replacement
Section titled “Hot Module Replacement”BunSPA includes Hot Module Replacement (HMR) for instant updates during development. Add this to your main entry file:
if (import.meta.hot) { import.meta.hot.accept();}
This tells Bun that your module can be hot-replaced, preserving application state when you save changes to your frontend files.