SolidStart
SolidStart builds through Vite: the
solidStart() plugin in your vite.config.ts owns routing, SSR, and
the server entry, and a single vite build produces the whole app.
That makes it a pure-Vite project, so
Cloudflare.Website.Vite deploys it
directly — no adapter config, no Wrangler file, no manual entrypoint.
Using TanStack Start with Solid instead? See TanStack Start.
Configure Vite
Section titled “Configure Vite”Your vite.config.ts is just the SolidStart plugin — Alchemy layers
its Cloudflare integration on top when it builds:
import { defineConfig } from "vite";
import { solidStart } from "@solidjs/start/config";
export default defineConfig({ plugins: [solidStart()],});Because SolidStart is fully expressed as a Vite plugin, Alchemy’s
programmatic vite build picks up the client assets and the SSR
server bundle from this one config — the server bundle becomes the
deployed Worker, the client output becomes its static assets.
Declare the Website
Section titled “Declare the Website”Declare the site as a module-level const (rather than inline in the Stack) and derive the typed shape of its bindings from it:
import * as Cloudflare from "alchemy/Cloudflare";
export const Website = Cloudflare.Website.Vite("Website");
export type WebsiteEnv = Cloudflare.InferEnv<typeof Website>;SolidStart’s SSR server bundle uses Node APIs at runtime — the
nodejs_compat compatibility flag covers that, and it is enabled by
default for every Worker, so there is nothing to pass.
Add it to the Stack
Section titled “Add it to the Stack”Yield the class from your Stack and return its URL:
import * as Alchemy from "alchemy";import * as Effect from "effect/Effect";
export default Alchemy.Stack( "CloudflareSolidStartExample", { providers: Cloudflare.providers(), state: Cloudflare.state(), }, Effect.gen(function* () { const worker = yield* Website;
return { url: worker.url, }; }),);Add bindings
Section titled “Add bindings”Both env channels of the Vite resource apply unchanged:
VITE_-prefixed entries are inlined into the client bundle as
import.meta.env.<KEY> at build time, and everything else attaches
to the SSR Worker as runtime bindings:
import * as Config from "effect/Config";
export const Uploads = Cloudflare.R2.Bucket("Uploads");
export const Website = Cloudflare.Website.Vite("Website", { env: { UPLOADS: Uploads, API_KEY: Config.redacted("API_KEY"), },});Uploads is a description, not a deploy — Alchemy provisions the
real bucket because the Website binds it. Config.redacted reads
API_KEY from your environment at deploy time and binds it as a
Worker secret — see Secrets & env.
WebsiteEnv (from
Declare the Website) is the typed shape of
the runtime bindings — import it wherever your server code reads the
Worker env. See
the Vite resource page for
how each env channel works.
Hand-rolled SolidJS SSR
Section titled “Hand-rolled SolidJS SSR”You don’t need SolidStart to server-render Solid. The
examples/cloudflare-solidjs-ssr
example uses plain vite-plugin-solid with SSR enabled and declares
an ssr environment whose input is a hand-written server entry:
import { dirname, resolve } from "node:path";import { fileURLToPath } from "node:url";import { defineConfig } from "vite";import solidPlugin from "vite-plugin-solid";
const __dirname = dirname(fileURLToPath(import.meta.url));
export default defineConfig({ plugins: [solidPlugin({ ssr: true })], environments: { ssr: { build: { emptyOutDir: false, rolldownOptions: { input: resolve(__dirname, "src/entry-server.tsx"), }, }, }, },});src/entry-server.tsx default-exports a standard Worker fetch
handler that calls Solid’s renderToStringAsync and injects the
result (plus the hydration script) into the HTML template — that
file becomes the Worker entry, while the client build ships as
static assets.
Deploy with runWorkerFirst
Section titled “Deploy with runWorkerFirst”Because the Worker itself renders every page, requests must reach it
before the asset layer answers — set assets.runWorkerFirst:
export const SolidSsr = Cloudflare.Website.Vite("SolidJSSsr", { assets: { runWorkerFirst: true, },});With runWorkerFirst: true the SSR handler sees every request first
and delegates to the ASSETS binding itself for static files —
without it, a request matching a built asset (like /index.html)
would be served directly and never hit your renderer.