Skip to content
Back to posts

June 7, 2026

Hydration-proof components

Deferring browser reads to useEffect keeps the server happy, but it introduces a new annoyance. The user sees the default first, then a flash as the real value snaps in. On a feed, that means the layout jumps from comfortable density to compact the instant React hydrates.

The flash

The effect based version is correct, but it always paints the wrong value for one frame.

function FeedDensityProvider({ children }) {
  const [density, setDensity] = useState('cozy')

  useEffect(() => {
    setDensity(localStorage.getItem('feedDensity') || 'cozy')
  }, [])

  return <div data-density={density}>{children}</div>
}

Anyone who set their feed to compact gets a visible jump on every page load.

The fix

Inject a tiny synchronous script that sets the correct value before the browser paints and before React hydrates. By the time React takes over, the DOM already holds the right state, so there is nothing to correct.

function FeedDensityProvider({ children }) {
  return (
    <>
      <div id="feed">{children}</div>
      <script
        dangerouslySetInnerHTML={{
          __html: `
            try {
              const density = localStorage.getItem('feedDensity') || 'cozy'
              document.getElementById('feed').dataset.density = density
            } catch (e) {}
          `,
        }}
      />
    </>
  )
}

The script runs inline, synchronously, before paint. No flash, no jump, no flicker on the feed.