1---
2title: SolidStart
3description: Deploy SolidStart to Cloudflare with Cloudflare.Website.Vite — plus the hand-rolled SolidJS SSR variant for full control over the server entry.
4sidebar:
5 order: 8
6---
7
8[SolidStart](https://start.solidjs.com) builds through Vite: the
9`solidStart()` plugin in your `vite.config.ts` owns routing, SSR, and
10the server entry, and a single `vite build` produces the whole app.
11That makes it a pure-Vite project, so
12[`Cloudflare.Website.Vite`](/cloudflare/frontend/vite) deploys it
13directly — no adapter config, no Wrangler file, no manual entrypoint.
14
15:::note[Verified version line]
16The checked-in example
17([examples/cloudflare-website-solidstart](https://github.com/alchemy-run/alchemy/tree/main/examples/cloudflare-website-solidstart))
18pins `@solidjs/start` **2.0.0-alpha.2** with
19`@solidjs/vite-plugin-nitro-2` — the Vite-native alpha line where
20`solidStart()` is a plain Vite plugin. That is the version line
21verified against Alchemy.
22:::
23
24Using TanStack Start with Solid instead? See
25[TanStack Start](/cloudflare/frontend/tanstack-start).
26
27## Configure Vite
28
29Your `vite.config.ts` is just the SolidStart plugin — Alchemy layers
30its Cloudflare integration on top when it builds:
31
32```typescript
33// vite.config.ts
34import { defineConfig } from "vite";
35
36import { solidStart } from "@solidjs/start/config";
37
38export default defineConfig({
39 plugins: [solidStart()],
40});
41```
42
43Because SolidStart is fully expressed as a Vite plugin, Alchemy's
44programmatic `vite build` picks up the client assets and the SSR
45server bundle from this one config — the server bundle becomes the
46deployed Worker, the client output becomes its static assets.
47
48## Declare the Website
49
50Declare the site as a module-level const (rather than inline in the
51Stack) and derive the typed shape of its bindings from it:
52
53```typescript
54// alchemy.run.ts
55import * as Cloudflare from "alchemy/Cloudflare";
56
57export const Website = Cloudflare.Website.Vite("Website");
58
59export type WebsiteEnv = Cloudflare.InferEnv<typeof Website>;
60```
61
62SolidStart's SSR server bundle uses Node APIs at runtime — the
63`nodejs_compat` compatibility flag covers that, and it is enabled by
64default for every Worker, so there is nothing to pass.
65
66## Add it to the Stack
67
68Yield the class from your Stack and return its URL:
69
70```typescript
71// alchemy.run.ts
72import * as Alchemy from "alchemy";
73import * as Effect from "effect/Effect";
74
75export default Alchemy.Stack(
76 "CloudflareSolidStartExample",
77 {
78 providers: Cloudflare.providers(),
79 state: Cloudflare.state(),
80 },
81 Effect.gen(function* () {
82 const worker = yield* Website;
83
84 return {
85 url: worker.url,
86 };
87 }),
88);
89```
90
91## Add bindings
92
93Both env channels of the Vite resource apply unchanged:
94`VITE_`-prefixed entries are inlined into the client bundle as
95`import.meta.env.<KEY>` at build time, and everything else attaches
96to the SSR Worker as runtime bindings:
97
98```diff lang="typescript"
99// alchemy.run.ts
100+import * as Config from "effect/Config";
101
102+export const Uploads = Cloudflare.R2.Bucket("Uploads");
103
104export const Website = Cloudflare.Website.Vite("Website", {
105+ env: {
106+ UPLOADS: Uploads,
107+ API_KEY: Config.Redacted("API_KEY"),
108+ },
109});
110```
111
112`Uploads` is a description, not a deploy — Alchemy provisions the
113real bucket because the Website binds it. `Config.Redacted` reads
114`API_KEY` from your environment at deploy time and binds it as a
115Worker secret — see [Secrets & env](/cloudflare/security/secrets-env).
116
117`WebsiteEnv` (from
118[Declare the Website](#declare-the-website)) is the typed shape of
119the runtime bindings — import it wherever your server code reads the
120Worker env. See
121[the Vite resource page](/cloudflare/frontend/vite#environment) for
122how each env channel works.
123
124## Hand-rolled SolidJS SSR
125
126You don't need SolidStart to server-render Solid. The
127[examples/cloudflare-solidjs-ssr](https://github.com/alchemy-run/alchemy/tree/main/examples/cloudflare-solidjs-ssr)
128example uses plain `vite-plugin-solid` with SSR enabled and declares
129an `ssr` environment whose input is a hand-written server entry:
130
131```typescript
132// vite.config.ts
133import { dirname, resolve } from "node:path";
134import { fileURLToPath } from "node:url";
135import { defineConfig } from "vite";
136import solidPlugin from "vite-plugin-solid";
137
138const __dirname = dirname(fileURLToPath(import.meta.url));
139
140export default defineConfig({
141 plugins: [solidPlugin({ ssr: true })],
142 environments: {
143 ssr: {
144 build: {
145 emptyOutDir: false,
146 rolldownOptions: {
147 input: resolve(__dirname, "src/entry-server.tsx"),
148 },
149 },
150 },
151 },
152});
153```
154
155`src/entry-server.tsx` default-exports a standard Worker `fetch`
156handler that calls Solid's `renderToStringAsync` and injects the
157result (plus the hydration script) into the HTML template — that
158file becomes the Worker entry, while the client build ships as
159static assets.
160
161### Deploy with `runWorkerFirst`
162
163Because the Worker itself renders every page, requests must reach it
164before the asset layer answers — set `assets.runWorkerFirst`:
165
166```typescript
167// alchemy.run.ts
168export const SolidSsr = Cloudflare.Website.Vite("SolidJSSsr", {
169 assets: {
170 runWorkerFirst: true,
171 },
172});
173```
174
175With `runWorkerFirst: true` the SSR handler sees every request first
176and delegates to the `ASSETS` binding itself for static files —
177without it, a request matching a built asset (like `/index.html`)
178would be served directly and never hit your renderer.