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

jb-infinite-scroll

v2.0.0

Published

infinite scroll web component

Readme

jb-infinite-scroll

Published on webcomponents.org GitHub license NPM Version GitHub Created At

Infinite-scroll container web component with loading, empty, ended, scroll-capture, and chat-style stick-to-bottom states.

  • Custom content slot.
  • Custom loading and empty states.
  • Scroll-end event for loading more data.
  • Capture guard to prevent duplicate load calls.
  • Stick-to-bottom behavior for chat or log views.

When to use

Use jb-infinite-scroll when a scrollable area should ask the app to load more content as the user reaches the bottom. Start with the normal content demo.

Use stick-to-bottom when the content behaves like a chat or log feed and should stay pinned to the bottom while the user is already near the bottom; see the stick-to-bottom demo.

Demo

Using With JS Frameworks

See the React documentation.

Other integrations: Angular · Vue · Nuxt · Svelte · SvelteKit · SolidJS · Lit · Next.js · Astro · Blazor · Server-rendered templates · WordPress · Alpine.js and HTMX

Installation

npm i jb-infinite-scroll
import 'jb-infinite-scroll';
<jb-infinite-scroll>
  <div slot="content">
    <div>Item 1</div>
    <div>Item 2</div>
  </div>
</jb-infinite-scroll>

API reference

Attributes

| name | type | default | description | | --- | --- | --- | --- | | is-loading | boolean | false | Shows loading UI and prevents scroll-end capture while true. Demo | | is-empty | boolean | false | Shows empty UI, hides content, and prevents scroll-end capture while true. Demo | | has-more | boolean | true | Controls whether more data can be loaded. Set it to false to prevent future scroll-end capture. Demo | | disable-capture-scroll | boolean | false | Disables scroll-end capture while true. Demo | | state-change-waiting-behavior | 'FORCE_WAIT' \| 'NO_WAIT' | FORCE_WAIT | Controls whether scroll-end waits for a state change before it can fire again. Demo | | stick-to-bottom | boolean | false | Keeps the scroll position at the bottom when content changes, unless the user has scrolled more than 6.25rem from the bottom. Demo |

Properties

| name | type | readonly | description | | --- | --- | --- | --- | | isLoading | boolean | no | Shows loading UI and prevents scroll-end capture while true. Demo | | isEmpty | boolean | no | Shows empty UI and prevents scroll-end capture while true. Demo | | hasMore | boolean | no | Controls whether more data can be loaded. Set it to false to prevent future scroll-end capture. Demo | | disableCaptureScroll | boolean | no | Disables scroll-end capture while true. Demo | | stateChangeWaitingBehavior | 'FORCE_WAIT' \| 'NO_WAIT' | no | Controls waiting behavior after scroll-end fires. Demo | | canCaptureScroll | boolean | yes | true when scroll capture is currently allowed. Demo |

Methods

| name | returns | description | | --- | --- | --- | | scrollTo(options) | void | Forwards scrollTo to the internal scrollable content wrapper. Demo | | scrollTo(x, y) | void | Forwards coordinate scrolling to the internal scrollable content wrapper. Demo | | scrollToEnd(options?) | void | Scrolls the internal content wrapper to the bottom. Demo |

Events

| event | detail | description | | --- | --- | --- | | scroll | none | Dispatched from the host when the internal content wrapper scrolls. Demo | | scroll-end | none | Dispatched when the internal scroll wrapper reaches the bottom and canCaptureScroll is true. Demo | | load | none | Dispatched from connectedCallback before initialization. Demo | | init | none | Dispatched from connectedCallback after initialization. Demo |

Content

Put the scrollable list or content in slot="content"; the normal content demo shows the required slot.

<jb-infinite-scroll>
  <div slot="content">
    <div>Item 1</div>
    <div>Item 2</div>
    <div>Item 3</div>
  </div>
</jb-infinite-scroll>

Load more on scroll end

Listen to scroll-end, start your fetch, then update isLoading, hasMore, or isEmpty so the component can capture the next scroll when using the default FORCE_WAIT behavior. The load-more demo shows this cycle.

const infiniteScroll = document.querySelector('jb-infinite-scroll');

infiniteScroll.addEventListener('scroll-end', async () => {
  infiniteScroll.isLoading = true;

  const nextItems = await loadMoreItems();
  renderItems(nextItems);

  infiniteScroll.isLoading = false;
  infiniteScroll.hasMore = nextItems.length > 0;
});

scroll-end is not dispatched while any of these are true:

  • isLoading
  • isEmpty
  • hasMore
  • disableCaptureScroll
  • waiting for a state change after a previous scroll-end in FORCE_WAIT mode

Loading state

The load-more demo shows the default loading state while new content is fetched.

<jb-infinite-scroll is-loading="true">
  <div slot="loading">Loading...</div>
</jb-infinite-scroll>
document.querySelector('jb-infinite-scroll').isLoading = true;

The default loading UI uses jb-loading.

Empty state

Use the empty state demo to see custom empty-slot content.

<jb-infinite-scroll is-empty="true">
  <div slot="empty">No items found</div>
</jb-infinite-scroll>
document.querySelector('jb-infinite-scroll').isEmpty = true;

Ended state

Set has-more to false when there is no more data to load. Its capture guard is covered in the state guards demo.

<jb-infinite-scroll has-more="false"></jb-infinite-scroll>
document.querySelector('jb-infinite-scroll').hasMore = false;

Disable scroll capture

Use the state guards demo to compare disabled capture with loading, empty, ended, and NO_WAIT modes.

document.querySelector('jb-infinite-scroll').disableCaptureScroll = true;
<jb-infinite-scroll disable-capture-scroll="true"></jb-infinite-scroll>

State-change waiting behavior

The default state-change-waiting-behavior is FORCE_WAIT. After scroll-end fires, the component waits until one of the state setters runs, such as isLoading = true, isLoading = false, hasMore = true, or isEmpty = true. This prevents multiple load calls for the same bottom position. Compare it with NO_WAIT in the state guards demo.

Use NO_WAIT only when your app handles duplicate calls itself.

<jb-infinite-scroll state-change-waiting-behavior="NO_WAIT"></jb-infinite-scroll>

Change scroll position

The scroll manipulation demo provides controls for both methods.

const infiniteScroll = document.querySelector('jb-infinite-scroll');

infiniteScroll.scrollTo({ behavior: 'smooth', top: 400 });
infiniteScroll.scrollToEnd({ behavior: 'smooth' });

Stick to bottom

Use stick-to-bottom for chat, logs, or feeds where new content should keep the scroll at the bottom while the user is already near the bottom. The stick-to-bottom demo shows content growth while pinned.

<jb-infinite-scroll stick-to-bottom>
  <div slot="content">
    <!-- messages -->
  </div>
</jb-infinite-scroll>

If the user scrolls more than 6.25rem away from the bottom, automatic stick-to-bottom pauses to respect the user's position. Call scrollToEnd() when you must force the bottom position.

Slots

The normal demo uses the content slot, while the empty demo uses the empty slot.

| slot | description | | --- | --- | | content | Scrollable list/content area. | | loading | Custom loading UI. Defaults to jb-loading. | | empty | Custom empty-list UI shown when isEmpty is true. |

CSS parts and states

Styling is shared with the web component; the scroll manipulation demo provides a visible scroll container for inspecting these parts and states.

| part | description | | --- | --- | | content | Internal scrollable content wrapper. | | loading-wrapper | Loading wrapper shown while loading. | | empty-list-wrapper | Empty-list wrapper shown while empty. | | default-loading | Default jb-loading element inside the loading slot fallback. |

| custom state | description | | --- | --- | | loading | Applied while isLoading is true. | | empty | Applied while isEmpty is true. |

jb-infinite-scroll::part(content) {
  scroll-behavior: smooth;
}

jb-infinite-scroll:state(loading)::part(loading-wrapper) {
  padding: 1rem;
}

Related Docs

AI agent notes

  • Import jb-infinite-scroll once before using <jb-infinite-scroll>.
  • Put the scrollable content inside slot="content".
  • Listen to scroll-end for load-more behavior.
  • Listen to scroll only when you need regular scroll-position updates from the internal scroll wrapper.
  • In default FORCE_WAIT mode, update a state such as isLoading, hasMore, or isEmpty after scroll-end so future scroll capture can resume.
  • Set hasMore = false when the API has no more data.
  • Use scrollToEnd() for chat/log views that need to force the bottom position.
  • This package includes custom-elements.json and points to it with the package.json customElements field. The field is documented by the Custom Elements Manifest project in Referencing manifests from npm packages.
  • In custom-elements.json, exports.kind: "js" describes JavaScript/TypeScript exports and exports.kind: "custom-element-definition" maps the jb-infinite-scroll tag name to JBInfiniteScrollWebComponent.