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.

Declare the site as a module-level const (rather than inline in the Stack) — every prop is optional, so the minimal declaration is just a name:

alchemy.run.ts
import * as Cloudflare from "alchemy/Cloudflare";
export const Website = Cloudflare.Website.Vite("Vue");

Yield the class from your Stack and return its URL — see examples/cloudflare-vue for the checked-in example:

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

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
export const Website = Cloudflare.Website.Vite("Vue", {
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:

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

Type it for your Vue code with Vite’s standard ImportMetaEnv augmentation:

src/vite-env.d.ts
/// <reference types="vite/client" />
interface ImportMetaEnv {
readonly VITE_API_URL: string;
}

Client code reads it as import.meta.env.VITE_API_URL. See Environment for the full inlining semantics.

Because a SPA ships no server code, there are no runtime bindings to type with Cloudflare.InferEnv — when you add server routes (via an SSR framework like TanStack Start or a separate Worker), that’s where InferEnv comes in.