SolidJS Rendering Utilities
solid-core-rendering
SolidJS rendering: render for client apps, hydrate for SSR, renderToString for server rendering, renderToStream for streaming, isServer checks.
SKILL.md
Full skill instructions
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.
import { render } from "solid-js/web";
const dispose = render(() => <App />, document.getElementById("app")!);
// Later, unmount
dispose();
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:
// ✅ Correct - function
render(() => <App />, element);
// ❌ Wrong - JSX directly
render(<App />, element);
hydrate - SSR Hydration
Hydrate server-rendered HTML with client-side code. Essential for SSR apps.
import { hydrate } from "solid-js/web";
const dispose = hydrate(() => <App />, document.getElementById("app")!);
Key points:
- Rehydrates existing DOM
- Matches server-rendered structure
- Attaches interactivity
- Used in SSR applications
Options:
hydrate(() => <App />, element, {
renderId: "app", // Optional render ID
owner: undefined, // Optional owner context
});
renderToString - Server Rendering
Render components to HTML string for SSR.
import { renderToString } from "solid-js/web";
const html = renderToString(() => <App />);
// 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.
import { renderToStringAsync } from "solid-js/web";
const html = await renderToStringAsync(() => <App />);
// 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.
import { renderToStream } from "solid-js/web";
const stream = renderToStream(() => <App />);
// Pipe to response
stream.pipeTo(response);
Benefits:
- Faster initial render
- Progressive HTML delivery
- Better perceived performance
HydrationScript / generateHydrationScript
Bootstrap hydration before Solid runtime loads.
import { HydrationScript, generateHydrationScript } from "solid-js/web";
// As component (JSX)
<HydrationScript nonce={nonce} eventNames={["click", "input"]} />
// As string (manual HTML)
const script = generateHydrationScript({
nonce,
eventNames: ["click", "input"],
});
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.
import { isServer } from "solid-js/web";
if (isServer) {
// Server-only code
console.log("Running on server");
} else {
// Client-only code
console.log("Running on client");
}
Use cases:
- Conditional server/client code
- Environment-specific logic
- Avoiding browser APIs on server
Common Patterns
Client Entry Point
// entry-client.tsx
import { render } from "solid-js/web";
import App from "./App";
render(() => <App />, document.getElementById("app")!);
SSR Entry Point
// entry-server.tsx
import { renderToString, generateHydrationScript } from "solid-js/web";
import App from "./App";
export default function handler(req, res) {
const html = renderToString(() => <App />);
res.send(`
<!DOCTYPE html>
<html>
<head>
${generateHydrationScript()}
</head>
<body>
<div id="app">${html}</div>
</body>
</html>
`);
}
Client Hydration
// entry-client.tsx
import { hydrate } from "solid-js/web";
import App from "./App";
hydrate(() => <App />, document.getElementById("app")!);
Streaming SSR
import { renderToStream } from "solid-js/web";
export default async function handler(req, res) {
const stream = renderToStream(() => <App />);
res.setHeader("Content-Type", "text/html");
stream.pipeTo(res);
}
Environment-Specific Code
import { isServer } from "solid-js/web";
function Component() {
if (isServer) {
// Server-only initialization
return <div>Server rendered</div>;
}
// Client-only code
const [mounted, setMounted] = createSignal(false);
onMount(() => setMounted(true));
return <div>Client: {mounted()}</div>;
}
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
