Diagnosing and Fixing StencilJS SSR Hydration Mismatch
When a Stencil app shows a hydration mismatch warning after an SSR build, the page flickers and stateful components rebuild. This guide walks through the common causes, diagnostic checks, and step‑by‑step fixes so you can restore a smooth client takeover.
01 Jul 2025, 17:35 UTC

Recognizable Condition
When a Stencil app built with stencil build --ssr loads in the browser you may see a brief flicker followed by a console warning:
Stencil: Hydration mismatch
or a stack trace mentioning Cannot read property … of undefined during the client takeover. The page still renders, but the component tree is rebuilt from scratch, which hurts performance and can break stateful logic.
Common Causes
- Missing hydration marker – the server‑generated HTML lacks the
<!-- client-hydrate -->comment, so Stencil thinks the DOM is “stale”. - Browser‑only references in component state – accessing
window,document, or other globals duringcomponentWillLoadorrendercauses a crash on the server. - Non‑serializable props – Maps, Sets, or objects with circular references cannot be stringified into the SSR payload, so the client receives incomplete data.
- Hydration marker format mismatch – upgrading from Stencil 2.x to 3.x without updating the root
index.htmltemplate can generate a different marker syntax.
Diagnostic Table
| Check | What to Look For | Expected Result |
|---|---|---|
| Server output | Open dist/index.html after stencil build --ssr | Contains <!-- client-hydrate --> comment immediately after <body> |
| Open DevTools, reload page | No “Hydration mismatch” warning | |
| Inspect component instance in Elements panel | State properties defined, no undefined errors | |
View network request for app.js and app.css | Payload contains serializable JSON | |
Check src/index.html for <!-- client-hydrate --> marker | Matches the version used by your Stencil build |
Ordered Checks & Fixes
- Verify the hydration marker
- Run
npx stencil build --ssrin your project root. - Open
dist/index.htmland search for<!-- client-hydrate -->. - If missing, update
src/index.htmlto include the comment just after<body>and rebuild.
- Run
- Guard browser globals
- Wrap any
windowordocumentusage in a runtime check:if (typeof window !== 'undefined') { // safe to use } - Prefer
componentWillLoadfor side‑effects that only run on the client.
- Wrap any
- Serialize component props
- Replace
MaporSetwith plain arrays or objects before passing them as props. - If you must use a complex type, provide a
toJSONmethod or convert to a serializable shape incomponentWillLoad.
- Replace
- Align SSR and client templates
- When upgrading to Stencil 3.x, generate a fresh
index.htmlwithstencil generateor copy the template from the new package. - Ensure the
<!-- client-hydrate -->comment matches the new marker format.
- When upgrading to Stencil 3.x, generate a fresh
- Re‑hydrate after fixes
- Clear browser cache or use incognito mode.
- Reload the page and confirm the console shows no hydration warnings.
- Use the Elements panel to verify that the component tree remains the same after load.
Concrete Example
Suppose you have a <my-widget> component that reads the viewport width on load:
export class MyWidget {
viewport: number;
componentWillLoad() {
this.viewport = window.innerWidth; // ❌ will crash on SSR
}
render() {
return {this.viewport};
}
}
Fix:
export class MyWidget {
viewport: number;
componentWillLoad() {
if (typeof window !== 'undefined') {
this.viewport = window.innerWidth;
}
}
After rebuilding, dist/index.html will contain the hydration marker, and the console will no longer emit the mismatch warning.
Escalation Criteria
- If the hydration warning persists after the above steps, check that
stencil.config.tshasssr: trueand thatbuildDocs: false(docs builds can interfere with SSR output). - Verify that the build output is served from the correct directory (e.g.,
/distvs./build) – a mismatch can cause the client to load a staleapp.jsthat doesn’t match the server HTML. - If the issue is limited to a single component, isolate it by creating a minimal reproduction and opening a GitHub issue against the Stencil repository.
Limitations & Practical Checks
- SSR hydration mismatches are only reproducible in a browser environment; Node.js unit tests won’t surface them.
- Some third‑party libraries (e.g., D3, Chart.js) internally access
windowand may need to be loaded lazily viaimport()inside acomponentWillLoadguard. - After any change, run
npx stencil build --ssr --devand serve withnpx stencil serve --ssrto quickly verify the result.
0 replies
A thoughtful contribution can make all the difference. Be the first to share one.