SolidJS Rendering Utilities
Complete guide to rendering SolidJS applications. Understand client-side rendering, SSR, hydration, and streaming.
render - Client-Side Rendering
Mount your Solid app to the DOM. Essential browser entry point for SPAs.
tsx1import { render } from "solid-js/web"; 2 3const dispose = render(() => <App />, document.getElementById("app")!); 4 5// Later, unmount 6dispose();
Key points:
- First argument must be a function (not JSX directly)
- Element should be empty (render appends, dispose removes all)
- Returns dispose function to unmount
Correct usage:
tsx1// ✅ Correct - function 2render(() => <App />, element); 3 4// ❌ Wrong - JSX directly 5render(<App />, element);
hydrate - SSR Hydration
Hydrate server-rendered HTML with client-side code. Essential for SSR apps.
tsx1import { hydrate } from "solid-js/web"; 2 3const dispose = hydrate(() => <App />, document.getElementById("app")!);
Key points:
- Rehydrates existing DOM
- Matches server-rendered structure
- Attaches interactivity
- Used in SSR applications
Options:
tsx1hydrate(() => <App />, element, { 2 renderId: "app", // Optional render ID 3 owner: undefined, // Optional owner context 4});
renderToString - Server Rendering
Render components to HTML string for SSR.
tsx1import { renderToString } from "solid-js/web"; 2 3const html = renderToString(() => <App />); 4// Returns: "<div>...</div>"
Use cases:
- SSR initial render
- Static site generation
- Email templates
- Server-side HTML generation
renderToStringAsync - Async Rendering
Render components with async data to HTML string.
tsx1import { renderToStringAsync } from "solid-js/web"; 2 3const html = await renderToStringAsync(() => <App />); 4// Waits for async resources
Use cases:
- SSR with async data
- Resources and Suspense
- Async components
renderToStream - Streaming SSR
Stream HTML to client for faster time-to-first-byte.
tsx1import { renderToStream } from "solid-js/web"; 2 3const stream = renderToStream(() => <App />); 4 5// Pipe to response 6stream.pipeTo(response);
Benefits:
- Faster initial render
- Progressive HTML delivery
- Better perceived performance
HydrationScript / generateHydrationScript
Bootstrap hydration before Solid runtime loads.
tsx1import { HydrationScript, generateHydrationScript } from "solid-js/web"; 2 3// As component (JSX) 4<HydrationScript nonce={nonce} eventNames={["click", "input"]} /> 5 6// As string (manual HTML) 7const script = generateHydrationScript({ 8 nonce, 9 eventNames: ["click", "input"], 10});
Purpose:
- Sets up hydration on client
- Required for SSR apps
- Place once in
<head>or before closing</body>
isServer - Environment Check
Check if code is running on server.
tsx1import { isServer } from "solid-js/web"; 2 3if (isServer) { 4 // Server-only code 5 console.log("Running on server"); 6} else { 7 // Client-only code 8 console.log("Running on client"); 9}
Use cases:
- Conditional server/client code
- Environment-specific logic
- Avoiding browser APIs on server
Common Patterns
Client Entry Point
tsx1// entry-client.tsx 2import { render } from "solid-js/web"; 3import App from "./App"; 4 5render(() => <App />, document.getElementById("app")!);
SSR Entry Point
tsx1// entry-server.tsx 2import { renderToString, generateHydrationScript } from "solid-js/web"; 3import App from "./App"; 4 5export default function handler(req, res) { 6 const html = renderToString(() => <App />); 7 8 res.send(` 9 <!DOCTYPE html> 10 <html> 11 <head> 12 ${generateHydrationScript()} 13 </head> 14 <body> 15 <div id="app">${html}</div> 16 </body> 17 </html> 18 `); 19}
Client Hydration
tsx1// entry-client.tsx 2import { hydrate } from "solid-js/web"; 3import App from "./App"; 4 5hydrate(() => <App />, document.getElementById("app")!);
Streaming SSR
tsx1import { renderToStream } from "solid-js/web"; 2 3export default async function handler(req, res) { 4 const stream = renderToStream(() => <App />); 5 6 res.setHeader("Content-Type", "text/html"); 7 stream.pipeTo(res); 8}
Environment-Specific Code
tsx1import { isServer } from "solid-js/web"; 2 3function Component() { 4 if (isServer) { 5 // Server-only initialization 6 return <div>Server rendered</div>; 7 } 8 9 // Client-only code 10 const [mounted, setMounted] = createSignal(false); 11 onMount(() => setMounted(true)); 12 13 return <div>Client: {mounted()}</div>; 14}
Best Practices
-
Always use function form:
render(() => <App />, element)- Not
render(<App />, element)
-
Empty mount element:
- Element should be empty
- Render appends, dispose removes all
-
Hydration matching:
- Server and client must match
- Same structure and content
-
Use isServer checks:
- Avoid browser APIs on server
- Conditional logic when needed
-
Streaming for performance:
- Use renderToStream for SSR
- Faster time-to-first-byte
Summary
- render: Client-side mounting
- hydrate: SSR hydration
- renderToString: Server HTML generation
- renderToStringAsync: Async server rendering
- renderToStream: Streaming SSR
- HydrationScript / generateHydrationScript: Hydration setup
- isServer: Environment detection