Skip to content
Latchkey

Qwik hydration/serialization mismatch during SSR in CI

Qwik resumes rather than hydrates: it serializes server state into the HTML and continues on the client. If the render depends on non-deterministic values or browser globals during SSR, the serialized state does not match and the resume warns or breaks.

What this error means

A CI prerender or e2e run reports a Qwik hydration/serialization mismatch, or the UI breaks on resume, because the server render used a value not reproducible on the client.

qwik
Warning: Qwik serialization mismatch: server state for signal "now"
does not match resumed client state.
  server: 1700000000000  client: 1700000005000

Common causes

Non-deterministic values in SSR render

Date.now()/Math.random() produce different server and client values, so the resumed state diverges.

Browser globals read during SSR

Reading window/document in useTask$ (which runs on the server) produces state the client cannot reproduce.

How to fix it

Keep SSR render deterministic; use useVisibleTask$

  1. Move time/random and DOM work into useVisibleTask$, which runs only on the client.
  2. Store only serializable, reproducible state in useStore/useSignal.
  3. Re-run the prerender to confirm the mismatch clears.
src/components/Clock.tsx
import { component$, useSignal, useVisibleTask$ } from '@builder.io/qwik';

export default component$(() => {
  const now = useSignal(0); // stable across server + resume
  useVisibleTask$(() => { now.value = Date.now(); });
  return <time>{now.value}</time>;
});

Avoid browser globals in server tasks

Do not read window/document in useTask$; that runs on the server and cannot match the client.

How to prevent it

  • Keep SSR render deterministic and serializable.
  • Do time/random/DOM work in useVisibleTask$.
  • Store only reproducible state in Qwik signals/stores.

Related guides

Tired of flaky CI? Latchkey auto-heals failed jobs and retries them for you. Start free →