jb-infinite-scroll
v2.0.0
Published
infinite scroll web component
Maintainers
Readme
jb-infinite-scroll
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-scrollimport '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:
isLoadingisEmptyhasMoredisableCaptureScroll- waiting for a state change after a previous
scroll-endinFORCE_WAITmode
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
- See
jb-infinite-scroll/reactif you want to use this component in React. - See All JB Design System Component List for more components.
- Use Contribution Guide if you want to contribute to this component.
AI agent notes
- Import
jb-infinite-scrollonce before using<jb-infinite-scroll>. - Put the scrollable content inside
slot="content". - Listen to
scroll-endfor load-more behavior. - Listen to
scrollonly when you need regular scroll-position updates from the internal scroll wrapper. - In default
FORCE_WAITmode, update a state such asisLoading,hasMore, orisEmptyafterscroll-endso future scroll capture can resume. - Set
hasMore = falsewhen the API has no more data. - Use
scrollToEnd()for chat/log views that need to force the bottom position. - This package includes
custom-elements.jsonand points to it with the package.jsoncustomElementsfield. 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 andexports.kind: "custom-element-definition"maps thejb-infinite-scrolltag name toJBInfiniteScrollWebComponent.
