npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

react-prehydrate

v0.1.1

Published

Eliminate flash-of-incorrect-state for user preferences in React Server Component apps.

Readme

react-prehydrate

Eliminate flash-of-incorrect-state for user preferences in React Server Component apps.

The problem

In Next.js RSC apps, user preferences (sidebar widths, themes, panel states) create a tension:

  • Cookies in Server Components make rendering dynamic, breaking static generation and PPR
  • Client-only state causes a visible flash as the page renders with defaults then snaps to the user's value

The solution

An inline <script> reads a cookie and sets a CSS variable on <html> before first paint. The DOM is identical server/client — only CSS values differ. React hydrates without mismatch because useState reads the CSS variable already set by the script.

Server HTML (static) → Browser parses → Inline script sets CSS var → First paint (correct) → React hydrates (matches)

Warning: The inline script is render-blocking — every byte delays FCP/LCP. The output is intentionally minimal (~230 bytes for one value, ~50 per additional). Use short cookie names and always combine values into a single <PrehydrateScript>.

Install

npm install react-prehydrate

Usage

1. Define values

// prehydrate.ts
import { createPrehydrateValue } from "react-prehydrate";

export const sidebarWidth = createPrehydrateValue({
  cookie: "sidebar-width",
  cssVar: "--sidebar-width",
  defaultValue: "280",
});

2. Add the script and provider

// providers.tsx
"use client";

import { PrehydrateScript } from "react-prehydrate";
import { sidebarWidth } from "./prehydrate";

export function Providers({ children }: { children: React.ReactNode }) {
  return (
    <sidebarWidth.Provider>
      <PrehydrateScript values={[sidebarWidth.config]} />
      {children}
    </sidebarWidth.Provider>
  );
}

When using multiple values, pass all configs to a single <PrehydrateScript> — this shares the cookie-reading helper and reads document.cookie once.

3. Use the CSS variable

Set a :root fallback matching defaultValue, then reference the variable:

:root {
  --sidebar-width: 280;
}

.sidebar {
  width: calc(var(--sidebar-width) * 1px);
}

With Tailwind CSS v4 you can use @utility:

@utility sidebar-w {
  width: calc(var(--sidebar-width) * 1px);
}

4. Read and write in components

"use client";

import { sidebarWidth } from "./prehydrate";

function ResizeHandle() {
  const [width, setWidth] = sidebarWidth.useValue();
  // setWidth triple-writes: React state + CSS variable + cookie
}

5. Suppress hydration warning on <html>

The inline script sets a style attribute on <html> that the server doesn't know about. This is the only mismatch:

<html lang="en" suppressHydrationWarning>
  <body>{children}</body>
</html>

CSS vs text content

Anything driven by CSS (layout, colors, spacing) works with no flash.

Rendering the value as text content (e.g. <span>280px</span>) would cause a hydration mismatch. Use useHydratedValue() instead — it returns null before hydration:

function WidthLabel() {
  const [width] = sidebarWidth.useHydratedValue();
  if (width === null) return <Skeleton />;
  return <span>{width}px</span>;
}

API

createPrehydrateValue(config)

Returns { Provider, useValue, useHydratedValue, config }.

| Config | Type | Description | | -------------- | -------- | -------------------------------------------------- | | cookie | string | Cookie name to read/write | | cssVar | string | CSS custom property name (e.g., --sidebar-width) | | defaultValue | string | Fallback when no cookie is set |

Provider

Client component that provides context. Wrap your layout with it.

useValue() — for CSS

Returns [value, setValue]. value is always a string. setValue triple-writes React state, CSS variable, and cookie.

useHydratedValue() — for text content

Returns [value, setValue]. value is null during SSR and before hydration, string after. Use this when rendering the value as text in the React tree.

config

The original config object, for passing to <PrehydrateScript>.

<PrehydrateScript values={configs} />

Renders a single inline <script> for one or more values.

generatePrehydrateScript(configs)

Returns the raw JS string. Useful for inspection or rendering the script tag yourself.

Example

See examples/next-resizable-sidebars for a full Next.js app with resizable sidebars.

License

MIT