Skip to content

React hydration fails on every page load (#418/#423), which makes the FOUC control test racy #126

Description

@mmcky

What happens

Every page load throws two React errors — minified #418 (hydration mismatch) and #423 (error during hydration, recovering with a client render). Reproduced in WebKit against a clean make build-theme, serving the visual fixture.

Why it matters beyond console noise

The #423 recovery re-renders the tree, and app/root.tsx passes the critical CSS as head={<style dangerouslySetInnerHTML={...} />}. So React puts the <style> block back shortly after hydration — including in the FOUC control test, which strips it from the served HTML precisely to prove the guard is meaningful.

Sampling under the control's exact conditions (external stylesheets aborted, inline <style> stripped from the document):

t after DOMContentLoaded critical <style> tags .simple-center-grid display sidebar right
30 ms 0 block 1272 px
138 ms 0 block 1272 px
195 ms 1 grid 0 px

firstPaintState() samples after waitUntil: "domcontentloaded" plus a waitForSelector, so it has roughly a 150 ms budget. On a loaded CI runner it can slip past that, and then all three control assertions flip at once — gridDisplay and bodyFont (both pre-existing) and sidebarOnScreen (added in #123). fouc-guard gates PRs, so this is a latent red-for-no-reason.

Suggested direction

Two things, probably in this order:

  1. The hydration failure itself — the root cause, and worth understanding on its own regardless of the test. Running an unminified React build locally will name the mismatching node.
  2. Make the control insensitive to it — sample at first paint via an init script rather than after domcontentloaded, or bail early with a clear message if the critical <style> reappears, so a slow runner reports the real reason instead of three confusing assertion failures.

Turned up while reviewing #123. Not introduced there — main carries the same exposure through its own two control assertions, which fail first.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions