Awesome-omni-skill solidstart-hydration-guard
SolidStart hydration guard: keep SSR/CSR output identical, gate browser-only APIs, use stable IDs, align Suspense/resource fallbacks, and use clientOnly/onMount for client-only UI.
install
source · Clone the upstream repo
git clone https://github.com/diegosouzapw/awesome-omni-skill
Claude Code · Install into ~/.claude/skills/
T=$(mktemp -d) && git clone --depth=1 https://github.com/diegosouzapw/awesome-omni-skill "$T" && mkdir -p ~/.claude/skills && cp -r "$T/skills/frontend/solidstart-hydration-guard" ~/.claude/skills/diegosouzapw-awesome-omni-skill-solidstart-hydration-guard && rm -rf "$T"
manifest:
skills/frontend/solidstart-hydration-guard/SKILL.mdsource content
SolidStart Hydration Guard
SSR/CSR Symmetry
- No browser-only globals during SSR: guard
,window
,document
,localStorage
,matchMedia
withResizeObserver
orif (!isServer)
.typeof window !== "undefined" - Keep server HTML stable: avoid
,Math.random()
in render; useDate.now()
or values passed from server data.createUniqueId() - Ensure Suspense fallbacks are the same server/client; don’t branch on browser checks for fallback.
- Avoid rendering different markup based on
unless the client version is wrapped inisServer
orclientOnly
.<NoHydration>
Stable IDs & Data
- For IDs, use
inside components, or pass stable IDs from server data/props.createUniqueId() - Call
the same number of times on server and client; don’t gate it behindcreateUniqueId()
orisServer
.<NoHydration> - Only pass JSON-serializable data from server fetchers; avoid class instances/functions.
Resources & Suspense
- For
/routercreateResource
, keepcreateAsync
consistent between server and client.initialValue - Prefer
when you need the server-rendered HTML to match hydration;ssrLoadFrom: "server"
will re-fetch on the client."initial" - Use
to inspect resource values during hydration when debugging.onHydrated - Resources inside non-hydrating sections are not serialized; wrap client-needed data in
or pass it through props.<Hydration> - Use
only when you can set headers/status before flush; otherwise keep data in initial render to avoid mismatch.deferStream
Client-Only UI
- Wrap client-only components with
and provide a SSR-safe fallback.clientOnly - For browser-only effects (e.g., viewport size), run inside
oronMount
gated bycreateEffect
.!isServer - Effects do not run during the initial client hydration, so they cannot fix initial mismatches.
is client-only with hydration disabled; use it for overlays that should not hydrate.<Portal>
skips hydration for its subtree; use<NoHydration>
to resume for interactive islands when needed.<Hydration>
Streaming & Headers
- When streaming (
), setrenderToStream
/HttpHeader
before first flush; hydrate assumes status/headers are fixed.HttpStatusCode
Router Integration
- Wrap
with<FileRoutes />
in the Router root to avoid hydration errors.<Suspense> - Keep route
functions pure; SSR runs them and resumes on the client during hydration.preload
Document & Script Setup
- Include
once (SolidStart injects it via the document<HydrationScript />
slot).assets
Debug Checklist
- Verify Router root uses
and that fallbacks match server/client.<Suspense> - Check for non-deterministic render output (
,Math.random()
).Date.now() - Confirm
call counts are identical on server/client.createUniqueId() - Validate query args and server data are JSON-serializable.
- If you must render different client UI, isolate it with
,clientOnly
, or<NoHydration>
.<Portal>
Quick Checks
- Are all browser APIs guarded?
- Are IDs and fallbacks stable between server and client?
- Are Suspense boundaries identical on both sides?