Skip to content

Vue

Vue is pure Vite — the entire app builds from @vitejs/plugin-vue in your vite.config.ts, so Cloudflare.Website.Vite deploys it with a single declaration: no main entrypoint, no build command, no output directory, no Wrangler configuration.

Your Vite config stays exactly what Vue’s scaffold gives you:

vite.config.ts
import { defineConfig } from "vite";
import vue from "@vitejs/plugin-vue";
export default defineConfig({
plugins: [vue()],
});

Alchemy runs Vite programmatically on the project root and layers its Cloudflare integration on top of this config — your plugins, aliases, and the rest of your setup are preserved as-is.

Yield the Vite resource from your Stack — this is examples/cloudflare-vue verbatim:

alchemy.run.ts
import * as Alchemy from "alchemy";
import * as Cloudflare from "alchemy/Cloudflare";
import * as Effect from "effect/Effect";
export default Alchemy.Stack(
"CloudflareVueExample",
{
providers: Cloudflare.providers(),
state: Cloudflare.state(),
},
Effect.gen(function* () {
const worker = yield* Cloudflare.Website.Vite("Vue", {
compatibility: {
flags: ["nodejs_compat"],
},
memo: {},
});
return {
url: worker.url,
};
}),
);

Every prop is optional — a bare Cloudflare.Website.Vite("Vue") deploys too; Alchemy builds the client assets and serves them from a Worker at the returned url.

If your app uses vue-router in history mode (createWebHistory), a deep link like /about arrives at the server as a request for a file that doesn’t exist. Configure the asset layer to fall back to index.html:

alchemy.run.ts
const worker = yield* Cloudflare.Website.Vite("Vue", {
compatibility: {
flags: ["nodejs_compat"],
},
memo: {},
assets: {
htmlHandling: "auto-trailing-slash",
notFoundHandling: "single-page-application",
},
});

With notFoundHandling: "single-page-application", unmatched paths return index.html instead of a 404, and vue-router resolves the route on the client.

A pure SPA has no server, so anything it needs from the rest of your Stack is baked into the bundle at build time — pass a VITE_-prefixed key in env and read it as import.meta.env.VITE_API_URL in your Vue code:

alchemy.run.ts
const worker = yield* Cloudflare.Website.Vite("Vue", {
env: {
VITE_API_URL: backend.url,
},
});

See Environment for the full inlining semantics.

Terminal window
bun alchemy deploy
Terminal window
bun alchemy dev

alchemy dev boots Vite’s own dev server, so you keep Vue’s HMR while Alchemy wires in the same env values as a deploy.