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

next-lazy-hydration-on-scroll

v1.2.1

Published

Hydrate components dynamically as the user scrolls.

Readme

next-lazy-hydration-on-scroll

Hydrate Next.js components when they scroll into view instead of all at once on load.

The HTML is still server-rendered, so the page looks and indexes the same. Only the JavaScript is deferred — which cuts hydration work off the critical path and lowers TBT.

Pages Router only. On the App Router, use Server Components and streaming instead. Requires React 18+.

Still relevant in 2026

Plenty of production apps are still on the Pages Router. It ships in Next.js 16, is not deprecated, and has its own actively maintained docs.

Migration is also incremental by designapp/ and pages/ run side by side, page by page — so codebases sit half-migrated for a long time, and the pages/ half never gets Server Components. Those routes hydrate the whole tree on every load. That's what this fixes, without a rewrite.

Install

npm install next-lazy-hydration-on-scroll

Usage

import { lazyHydrate } from 'next-lazy-hydration-on-scroll'

const HeavyComponent = lazyHydrate(() => import('./components/HeavyComponent'))

export default function Page() {
  return (
    <div>
      <header>Hydrated immediately</header>
      <HeavyComponent /> {/* hydrates on scroll */}
    </div>
  )
}

With options:

const HeavyComponent = lazyHydrate(() => import('./components/HeavyComponent'), {
  rootMargin: '0px 400px',
  wrapperElement: 'div',
  LoadingComponent: () => <div>Loading…</div>,
})

Options

| Option | Type | Default | Description | | ------------------ | ----------------------------------- | ------------- | ------------------------------------------------------ | | rootMargin | string | '0px 250px' | How early to hydrate, relative to the viewport | | wrapperElement | keyof React.JSX.IntrinsicElements | 'section' | Tag used for the wrapper element | | LoadingComponent | ComponentType | undefined | Shown while the chunk loads |

Props are forwarded to your component, except wrapperProps, which is spread onto the wrapper:

<HeavyComponent title="forwarded" wrapperProps={{ className: 'wrapper' }} />

Why not next/dynamic

next/dynamic splits the chunk, but the component is still part of the tree, so its JavaScript is needed to hydrate the page. Using ssr: false avoids that but drops the server-rendered HTML.

lazyHydrate keeps the HTML and keeps the chunk off the initial path.

Gotchas

  • Give the component its own file. Barrel files (index.ts re-exports) pull siblings into the same chunk and defeat the split.
  • The wrapper is a real element. Parent flex/grid rules see it, not your component. Adjust with wrapperElement / wrapperProps, or display: contents.
  • Nothing runs before hydration. No effects, no event handlers — so no analytics impressions inside a lazy component.
  • Skip it above the fold. Visible components hydrate right away anyway.

How it works

The wrapper renders empty on the client with dangerouslySetInnerHTML and suppressHydrationWarning, so React leaves the server HTML alone and never descends into the subtree. An IntersectionObserver then loads and hydrates the component as it nears the viewport. Without IntersectionObserver, it hydrates immediately.

Further reading

License

MIT