Vite
Source:
src/Cloudflare/Website/Vite.ts
A Cloudflare Worker deployed from a Vite project.
Vite uses the Cloudflare Vite plugin to build both the server bundle
and client assets in a single vite build invocation — no manual
main entrypoint, build command, output directory, or Wrangler
configuration required.
Input files are content-hashed (respecting .gitignore by default) so
unchanged projects skip the build and deploy entirely.
Deploying a Static Site
Section titled “Deploying a Static Site”For a pure static site (no SSR), a single call is all you need. Vite builds the project and Alchemy deploys the output as a Cloudflare Worker with static assets.
const site = yield* Cloudflare.Website.Vite("Website");SSR Frameworks
Section titled “SSR Frameworks”SSR frameworks like TanStack Start or SolidStart work with a single
call — the nodejs_compat compatibility flag is enabled by default
so the server bundle can use Node.js APIs.
TanStack Start
const app = yield* Cloudflare.Website.Vite("TanStackStart");SolidStart with worker-first routing
const app = yield* Cloudflare.Website.Vite("SolidStart", { assets: { runWorkerFirst: true },});React Router
React Router’s server build (virtual:react-router/server-build) is a
build manifest with no default export, so it cannot be deployed as the
Worker entry directly. Point main at a module that wraps it with
createRequestHandler (React Router’s Cloudflare template ships this
as workers/app.ts):
const app = yield* Cloudflare.Website.Vite("ReactRouter", { main: "workers/app.ts",});React Server Components
Section titled “React Server Components”Frameworks that emit more than one server environment (e.g. React
Server Components, which split into an rsc environment and an ssr
environment) need viteEnvironments to declare which environment
produces the deployed Worker entry and which additional server
environments to bundle alongside it. The client environment is
always deployed as static assets.
const app = yield* Cloudflare.Website.Vite("ReactRouterRSC", { viteEnvironments: { entry: "rsc", children: ["ssr"], },});Custom Worker Entry
Section titled “Custom Worker Entry”By default the deployed Worker entry is the server bundle the
framework produces. When the Worker must export more than the
framework’s fetch handler — Durable Object classes, additional
handlers — point main at your own module that wraps the framework
handler and re-exports the extras. main takes precedence over any
entry configured in the Vite config.
const app = yield* Cloudflare.Website.Vite("App", { main: "worker/index.ts", viteEnvironments: { entry: "rsc", children: ["ssr"], },});Single-Page Applications
Section titled “Single-Page Applications”For SPAs (React, Vue, etc.), configure asset handling so all
routes fall back to index.html.
Vue SPA
const app = yield* Cloudflare.Website.Vite("Vue", { assets: { htmlHandling: "auto-trailing-slash", notFoundHandling: "single-page-application", },});Foldkit
Foldkit apps are client-only Vite projects, so a
single call deploys them — the Foldkit Vite plugin in the app’s own
vite.config.ts composes with the injected Cloudflare plugin. Enable
single-page-application not-found handling so deep links boot the app:
const app = yield* Cloudflare.Website.Vite("Foldkit", { assets: { notFoundHandling: "single-page-application", },});Cloudflare.Website.Foldkit is the same thing with that default already applied.
Octane SPA
A client-only OctaneJS app (no octane.config.ts
routes) is a plain Vite SPA — the octane() compiler plugin in the app’s
own vite.config.ts composes with the injected Cloudflare plugin:
const app = yield* Cloudflare.Website.Vite("Octane", { assets: { notFoundHandling: "single-page-application", },});Fullstack Octane apps (routes + SSR in octane.config.ts) run their own
two-pass build through Octane’s Cloudflare adapter — deploy those with
Cloudflare.Website.Octane instead.
Serving on a Zone Route with a Path Prefix
Section titled “Serving on a Zone Route with a Path Prefix”Cloudflare matches static assets against the full request pathname,
so a site attached to a route like example.com/docs* only serves
assets whose uploaded paths carry the /docs prefix. Set Vite’s
base in your vite.config.ts — the emitted HTML references its
assets under the prefix, and Alchemy keys the uploaded asset manifest
with the same resolved base so the two always agree.
vite.config.ts
import { defineConfig } from "vite";
export default defineConfig({ base: "/docs/",});alchemy.run.ts
const docs = yield* Cloudflare.Website.Vite("Docs", { routes: [{ pattern: "example.com/docs*", zoneName: "example.com" }],});Custom Rebuild Scope
Section titled “Custom Rebuild Scope”By default, every non-gitignored file is hashed to decide whether
a rebuild is needed. Use memo to narrow the scope when your
project has large directories that don’t affect the build output.
Narrowing the memo scope
const site = yield* Cloudflare.Website.Vite("Docs", { memo: { include: ["src/**", "content/**", "package.json"], },});Rebuilding when a sibling workspace package changes
The default scope only hashes files under the project root (plus the
nearest lockfile), so edits to a sibling workspace package the app
imports do not retrigger the build on their own. Add the sibling’s
sources with a ../ include glob — and keep lockfile: true, since
providing include otherwise drops the lockfile from the hash:
const site = yield* Cloudflare.Website.Vite("Web", { rootDir: "apps/web", memo: { include: ["**\/*", "../../packages/env/src/**"], lockfile: true, },});Class Form
Section titled “Class Form”Calling Vite with no arguments returns a constructor you can
extend to declare the Worker as a named class. The class is both
an Effect you can yield* to deploy and a type you can reference
elsewhere — useful when other resources need to bind to this Worker.
class Website extends Cloudflare.Website.Vite<Website>()("Website") {}
const site = yield* Website;