You've built a beautiful clock component. It works perfectly in development. But in production, something's off.
When users first load your page, they see the clock frozen at 3:42 PM — the exact time your site was built. For a brief moment, your app looks broken. Then React loads, the clock jumps to the correct time, and everything starts working.
That moment? That's the problem Prehydrate solves.
Without Prehydrate:
- Wrong content (build-time values)
- Content jumps when React loads
- App becomes interactive
With Prehydrate:
- Correct content from the start
- App becomes interactive
The "wrong content" phase disappears entirely.
npm install prehydrateHere's a clock that shows the correct time the instant the page loads:
import { prehydrate } from 'prehydrate';
import { useState, useEffect } from 'react';
function Clock({ bind }) {
const [time, setTime] = useState(() => {
const props = bind('time');
return props.time || new Date();
});
useEffect(() => {
const interval = setInterval(() => setTime(new Date()), 1000);
return () => clearInterval(interval);
}, []);
return <div>{time.toLocaleTimeString()}</div>;
}
const { Prehydrate, bind } = prehydrate({
key: 'clock',
initialState: () => new Date(), // Runs when page loads, not at build time
});
export function PrehydratedClock() {
return (
<Prehydrate>
<Clock bind={bind} />
</Prehydrate>
);
}The key: initialState: () => new Date() is a function. It runs when the page loads, not when the server builds — so users always see the current time.
Any time your server-rendered content could be "wrong" when users first see it:
- Clocks and timers — Show the current time, not build time
- Countdowns — "Sale ends in 2 hours" shouldn't show yesterday's countdown
- User preferences — Dark mode, locale, timezone
- Live data — Stock prices, scores, availability
- Personalization — "Good morning" at 10 PM looks broken
HTML loads Prehydrate runs React hydrates
(build-time data) (fresh data) (interactive)
| | |
v v v
0ms ~50ms ~200ms
A tiny inline script runs the instant the browser parses your HTML — before React even starts downloading. By the time React loads, the DOM already shows the correct values.
Read auth from cookies before React loads:
const { Prehydrate, bind } = prehydrate({
key: 'auth',
initialState: () => {
return document.cookie.includes('session=')
? 'Welcome back!'
: 'Sign in';
},
});No more flash of wrong theme:
const { Prehydrate, bind } = prehydrate({
key: 'theme',
initialState: () => localStorage.getItem('theme') || 'light',
});Show times in their timezone from the start:
const { Prehydrate, bind } = prehydrate({
key: 'timezone',
initialState: () => Intl.DateTimeFormat().resolvedOptions().timeZone,
});Works with any React SSR framework:
- Next.js (App Router & Pages Router)
- Vite SSR
- Remix
- Any SSR setup using
renderToStringandhydrateRoot
// app/components/PrehydratedClock.tsx
'use client';
import { prehydrate } from 'prehydrate';
// ... rest of the clock codeFull type support included:
const { Prehydrate, bind } = prehydrate<Date>({
key: 'clock',
initialState: () => new Date(),
});const { Prehydrate, bind } = prehydrate({
key: 'unique-id', // Identifies this component
initialState: () => value, // Runs when page loads
deps: { helpers }, // Optional helper functions
});
// In your component:
const [state] = useState(() => {
const props = bind('state');
return props.state || fallback;
});
// Wrap it:
<Prehydrate>
<YourComponent bind={bind} />
</Prehydrate>For complete documentation, examples, and API reference:
git clone https://github.com/gruckion/prehydrate
cd prehydrate
pnpm install
pnpm devMIT
