Skip to content

Commit 7b14698

Browse files
committed
chore: implement solid example
1 parent ba5147a commit 7b14698

12 files changed

Lines changed: 1439 additions & 106 deletions

File tree

Lines changed: 333 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,333 @@
1+
---
2+
category: server side rendering
3+
icon: i-logos-solidjs-icon
4+
---
5+
6+
# SSR with SolidStart
7+
8+
> Server-side rendering with SolidStart in Nitro using Vite.
9+
10+
<!-- automd:ui-code-tree src="../../examples/vite-ssr-solidstart" default="src/entry-server.tsx" ignore="README.md,GUIDE.md" expandAll -->
11+
12+
::code-tree{defaultValue="src/entry-server.tsx" expandAll}
13+
14+
```json [package.json]
15+
{
16+
"private": true,
17+
"type": "module",
18+
"scripts": {
19+
"dev": "vite dev",
20+
"build": "vite build"
21+
},
22+
"dependencies": {
23+
"@solidjs/meta": "^0.29.4",
24+
"@solidjs/router": "^0.15.4",
25+
"@solidjs/start": "^2.0.0-alpha.2",
26+
"nitro": "^3.0.260311-beta",
27+
"solid-js": "^1.9.11",
28+
"vite": "^8.0.0"
29+
},
30+
"engines": {
31+
"node": ">=22"
32+
}
33+
}
34+
```
35+
36+
```json [tsconfig.json]
37+
{
38+
"compilerOptions": {
39+
"target": "ESNext",
40+
"module": "ESNext",
41+
"moduleResolution": "bundler",
42+
"allowSyntheticDefaultImports": true,
43+
"esModuleInterop": true,
44+
"jsx": "preserve",
45+
"jsxImportSource": "solid-js",
46+
"allowJs": true,
47+
"strict": true,
48+
"noEmit": true,
49+
"isolatedModules": true,
50+
"types": ["@solidjs/start/env"],
51+
"paths": {
52+
"~/*": ["./src/*"]
53+
}
54+
}
55+
}
56+
```
57+
58+
```ts [vite.config.ts]
59+
import { defineConfig } from "vite";
60+
import { solidStart } from "@solidjs/start/config";
61+
import { nitro } from "nitro/vite";
62+
63+
export default defineConfig({
64+
plugins: [solidStart(), nitro()],
65+
});
66+
```
67+
68+
```css [src/app.css]
69+
body {
70+
font-family:
71+
Gordita, Roboto, Oxygen, Ubuntu, Cantarell, "Open Sans", "Helvetica Neue", sans-serif;
72+
}
73+
74+
a {
75+
margin-right: 1rem;
76+
}
77+
78+
main {
79+
text-align: center;
80+
padding: 1em;
81+
margin: 0 auto;
82+
}
83+
84+
h1 {
85+
color: #335d92;
86+
text-transform: uppercase;
87+
font-size: 4rem;
88+
font-weight: 100;
89+
line-height: 1.1;
90+
margin: 4rem auto;
91+
max-width: 14rem;
92+
}
93+
94+
p {
95+
max-width: 14rem;
96+
margin: 2rem auto;
97+
line-height: 1.35;
98+
}
99+
100+
@media (min-width: 480px) {
101+
h1 {
102+
max-width: none;
103+
}
104+
105+
p {
106+
max-width: none;
107+
}
108+
}
109+
```
110+
111+
```tsx [src/app.tsx]
112+
import { MetaProvider, Title } from "@solidjs/meta";
113+
import { Router } from "@solidjs/router";
114+
import { FileRoutes } from "@solidjs/start/router";
115+
import { Suspense } from "solid-js";
116+
import "./app.css";
117+
118+
export default function App() {
119+
return (
120+
<Router
121+
root={(props) => (
122+
<MetaProvider>
123+
<Title>SolidStart - Basic</Title>
124+
<Suspense>{props.children}</Suspense>
125+
</MetaProvider>
126+
)}
127+
>
128+
<FileRoutes />
129+
</Router>
130+
);
131+
}
132+
```
133+
134+
```tsx [src/entry-client.tsx]
135+
// @refresh reload
136+
import { mount, StartClient } from "@solidjs/start/client";
137+
138+
mount(() => <StartClient />, document.getElementById("app")!);
139+
```
140+
141+
```tsx [src/entry-server.tsx]
142+
// @refresh reload
143+
import { createHandler, StartServer } from "@solidjs/start/server";
144+
145+
export default createHandler(() => (
146+
<StartServer
147+
document={({ assets, children, scripts }) => (
148+
<html lang="en">
149+
<head>
150+
<meta charset="utf-8" />
151+
<meta name="viewport" content="width=device-width, initial-scale=1" />
152+
{assets}
153+
</head>
154+
<body>
155+
<div id="app">{children}</div>
156+
{scripts}
157+
</body>
158+
</html>
159+
)}
160+
/>
161+
));
162+
```
163+
164+
```tsx [src/routes/[...404].tsx]
165+
import { Title } from "@solidjs/meta";
166+
import { HttpStatusCode } from "@solidjs/start";
167+
168+
export default function NotFound() {
169+
return (
170+
<main>
171+
<Title>Not Found</Title>
172+
<HttpStatusCode code={404} />
173+
<h1>Page Not Found</h1>
174+
<p>
175+
Visit{" "}
176+
<a href="https://start.solidjs.com" target="_blank">
177+
start.solidjs.com
178+
</a>{" "}
179+
to learn how to build SolidStart apps.
180+
</p>
181+
</main>
182+
);
183+
}
184+
```
185+
186+
```tsx [src/routes/index.tsx]
187+
import { Title } from "@solidjs/meta";
188+
189+
export default function Home() {
190+
return (
191+
<main>
192+
<Title>Hello World</Title>
193+
<h1>Hello world!</h1>
194+
<p>
195+
Visit{" "}
196+
<a href="https://start.solidjs.com" target="_blank">
197+
start.solidjs.com
198+
</a>{" "}
199+
to learn how to build SolidStart apps.
200+
</p>
201+
</main>
202+
);
203+
}
204+
```
205+
206+
::
207+
208+
<!-- /automd -->
209+
210+
<!-- automd:file src="../../examples/vite-ssr-solid/README.md" -->
211+
212+
Set up server-side rendering (SSR) with SolidJS, Vite, and Nitro. This setup uses `renderToStringAsync` for HTML generation and supports client hydration.
213+
214+
## Overview
215+
216+
1. Add the Nitro Vite plugin to your Vite config
217+
2. Configure client and server entry points
218+
3. Create a server entry that renders your app to HTML
219+
4. Create a client entry that hydrates the server-rendered HTML
220+
221+
## 1. Configure Vite
222+
223+
Add the Nitro and SolidJS plugins to your Vite config. SolidJS requires explicit JSX configuration and both `ssr` and `client` environments:
224+
225+
```js [vite.config.mjs]
226+
import solid from "vite-plugin-solid";
227+
import { defineConfig } from "vite";
228+
import { nitro } from "nitro/vite";
229+
230+
export default defineConfig({
231+
plugins: [solid({ ssr: true }), nitro()],
232+
esbuild: { jsx: "preserve", jsxImportSource: "solid-js" },
233+
environments: {
234+
ssr: {
235+
build: { rollupOptions: { input: "./src/entry-server.tsx" } },
236+
},
237+
client: {
238+
build: { rollupOptions: { input: "./src/entry-client.tsx" } },
239+
},
240+
},
241+
});
242+
```
243+
244+
Enable SSR mode in the Solid plugin with `solid({ ssr: true })`. Configure esbuild to preserve JSX for Solid's compiler and use Solid's JSX runtime. SolidJS requires explicit `ssr` and `client` environment configuration in Vite.
245+
246+
## 2. Create the App Component
247+
248+
Create a shared SolidJS component using reactive signals:
249+
250+
```tsx [src/app.tsx]
251+
import { createSignal } from "solid-js";
252+
253+
export function App() {
254+
const [count, setCount] = createSignal(0);
255+
256+
return (
257+
<div>
258+
<h1>Hello, Solid!</h1>
259+
<button onClick={() => setCount((count) => count + 1)}>Count: {count()}</button>
260+
</div>
261+
);
262+
}
263+
```
264+
265+
SolidJS uses signals (`createSignal`) for state management. Unlike React's `useState`, signals are getter functions that you call to read the value.
266+
267+
## 3. Create the Server Entry
268+
269+
The server entry renders your SolidJS app to HTML using `renderToStringAsync` and includes the `HydrationScript` for client-side hydration:
270+
271+
```tsx [src/entry-server.tsx]
272+
import { renderToStringAsync, HydrationScript } from "solid-js/web";
273+
import { App } from "./app.jsx";
274+
275+
import clientAssets from "./entry-client?assets=client";
276+
import serverAssets from "./entry-server?assets=ssr";
277+
278+
export default {
279+
async fetch(req: Request): Promise<Response> {
280+
const appHTML = await renderToStringAsync(() => <App />);
281+
const rootHTML = await renderToStringAsync(() => <Root appHTML={appHTML} />);
282+
return new Response(rootHTML, {
283+
headers: { "Content-Type": "text/html" },
284+
});
285+
},
286+
};
287+
288+
function Root(props: { appHTML?: string }) {
289+
const assets = clientAssets.merge(serverAssets);
290+
return (
291+
<html lang="en">
292+
<head>
293+
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
294+
{assets.css.map((attr: any) => (
295+
<link key={attr.href} rel="stylesheet" {...attr} />
296+
))}
297+
{assets.js.map((attr: any) => (
298+
<link key={attr.href} type="modulepreload" {...attr} />
299+
))}
300+
</head>
301+
<body>
302+
<div id="app" innerHTML={props.appHTML || ""} />
303+
<HydrationScript />
304+
<script type="module" src={assets.entry} />
305+
</body>
306+
</html>
307+
);
308+
}
309+
```
310+
311+
SolidJS requires rendering the app separately from the shell (two-phase rendering). The app HTML is injected via `innerHTML` to preserve hydration markers. Include the `HydrationScript` component to inject the script Solid needs to rehydrate on the client. Import assets using the `?assets=client` and `?assets=ssr` query parameters to collect CSS and JS from each entry point.
312+
313+
## 4. Create the Client Entry
314+
315+
The client entry hydrates the server-rendered HTML, restoring Solid's reactivity:
316+
317+
```tsx [src/entry-client.tsx]
318+
import { hydrate } from "solid-js/web";
319+
import "./styles.css";
320+
import { App } from "./app.jsx";
321+
322+
hydrate(() => <App />, document.querySelector("#app")!);
323+
```
324+
325+
The `hydrate` function attaches Solid's reactive system to the existing server-rendered DOM inside `#app`. The component is wrapped in a function `() => <App />` as required by Solid's API.
326+
327+
<!-- /automd -->
328+
329+
## Learn More
330+
331+
- [SolidJS Documentation](https://docs.solidjs.com/)
332+
- [Renderer](/docs/renderer)
333+
- [Server Entry](/docs/server-entry)

0 commit comments

Comments
 (0)