Infinite Scroll
<zn-infinite-scroll>
ZnInfiniteScroll
Loads further pages of server-rendered content as the user scrolls to the end.
Wrap the first page of any server-rendered list. When the end of the content comes within one screen of
view, the next page is fetched from uri (with #page# replaced by the page number)
and its HTML is appended. Loading stops at the first empty response. With no content inside, it starts by
loading page 1.
<div style="height: 300px; overflow: auto;"> <zn-infinite-scroll uri="/assets/infinite-scroll/page-#page#.html"> <zn-tile caption="Item 1" description="Rendered with the page"></zn-tile> <zn-tile caption="Item 2" description="Rendered with the page"></zn-tile> <zn-tile caption="Item 3" description="Rendered with the page"></zn-tile> <zn-tile caption="Item 4" description="Rendered with the page"></zn-tile> <zn-tile caption="Item 5" description="Rendered with the page"></zn-tile> </zn-infinite-scroll> </div>
Examples
Starting Empty
Leave the component empty and it loads the first page itself.
<div style="height: 300px; overflow: auto;"> <zn-infinite-scroll uri="/assets/infinite-scroll/page-#page#.html"></zn-infinite-scroll> </div>
Tile Grid
Lay the component out as a grid and each loaded page flows into it. Content in the loader slot
is shown while a page loads and can line up with the grid’s columns using subgrid, so
placeholder tiles stand in for the next row.
<div class="tile-feed"> <zn-infinite-scroll uri="/assets/infinite-scroll/tiles-#page#.html"> <zn-tile caption="Q3 revenue report" description="Report · Updated 2h ago" icon="bar_chart" icon-color="success"></zn-tile> <zn-tile caption="Spring campaign" description="Campaign · 12 assets" icon="campaign" icon-color="ai"></zn-tile> <zn-tile caption="Supplier invoices" description="Folder · 48 files" icon="receipt_long" icon-color="warning"></zn-tile> <zn-tile caption="Onboarding checklist" description="Template · Shared" icon="checklist" icon-color="info"></zn-tile> <zn-tile caption="Churned accounts" description="Segment · 312 customers" icon="person_remove" icon-color="danger"></zn-tile> <zn-tile caption="Brand guidelines" description="Document · v4" icon="palette" icon-color="neutral"></zn-tile> <zn-tile caption="Weekly standup" description="Event · Mondays 9:30" icon="event" icon-color="info"></zn-tile> <zn-tile caption="Product photos" description="Folder · 126 images" icon="photo_library" icon-color="ai"></zn-tile> <zn-tile caption="Refund policy" description="Document · Legal review" icon="gavel" icon-color="warning"></zn-tile> <zn-tile caption="Renewal reminders" description="Automation · Active" icon="autorenew" icon-color="success"></zn-tile> <zn-tile caption="Failed payments" description="Report · 18 this week" icon="credit_card_off" icon-color="danger"></zn-tile> <zn-tile caption="Warehouse stock" description="Inventory · 2,410 items" icon="inventory_2" icon-color="neutral"></zn-tile> <div slot="loader" class="tile-skeleton-row"> <zn-tile loading icon></zn-tile> <zn-tile loading icon></zn-tile> <zn-tile loading icon></zn-tile> <zn-tile loading icon></zn-tile> </div> </zn-infinite-scroll> </div> <style> /* Padding keeps tile borders and hover shadows clear of the scroll edges and scrollbar */ .tile-feed { height: 360px; overflow: auto; padding: var(--zn-spacing-x-small); } /* At most 4 columns, so each page of 12 tiles fills whole rows */ .tile-feed zn-infinite-scroll { --gap: var(--zn-spacing-small); display: grid; grid-template-columns: repeat(auto-fill, minmax(max(240px, calc((100% - 3 * var(--gap)) / 4)), 1fr)); gap: var(--gap); } .tile-feed zn-tile { background: rgb(var(--zn-panel)); border: 1px solid rgb(var(--zn-border-color)); border-radius: var(--zn-border-radius-large); } .tile-feed zn-tile:not([loading]) { transition: border-color 150ms ease, box-shadow 150ms ease; } .tile-feed zn-tile:not([loading]):hover { border-color: rgb(var(--zn-primary)); box-shadow: 0 2px 8px rgb(0 0 0 / 6%); } /* One row of placeholders on the grid's columns; any that don't fit collapse out of sight */ .tile-feed .tile-skeleton-row { grid-column: 1 / -1; display: grid; grid-template-columns: subgrid; grid-template-rows: auto; grid-auto-rows: 0; row-gap: 0; align-items: start; overflow: hidden; } </style>
Loading State
While a page is loading, the loader slot is shown after the content. These examples request a
page that never arrives, so the loading state stays on screen.
With no loader slot, a skeleton bar is shown.
<zn-infinite-scroll uri="/assets/infinite-scroll/pending-#page#.html"> <zn-tile caption="Item 1" description="Rendered with the page"></zn-tile> <zn-tile caption="Item 2" description="Rendered with the page"></zn-tile> <zn-tile caption="Item 3" description="Rendered with the page"></zn-tile> </zn-infinite-scroll>
In the tile grid, a row of placeholder tiles stands in for the next page. This uses the styles from the tile grid example.
<div class="tile-feed" style="height: auto;"> <zn-infinite-scroll uri="/assets/infinite-scroll/pending-#page#.html"> <zn-tile caption="Q3 revenue report" description="Report · Updated 2h ago" icon="bar_chart" icon-color="success"></zn-tile> <zn-tile caption="Spring campaign" description="Campaign · 12 assets" icon="campaign" icon-color="ai"></zn-tile> <zn-tile caption="Supplier invoices" description="Folder · 48 files" icon="receipt_long" icon-color="warning"></zn-tile> <zn-tile caption="Onboarding checklist" description="Template · Shared" icon="checklist" icon-color="info"></zn-tile> <zn-tile caption="Churned accounts" description="Segment · 312 customers" icon="person_remove" icon-color="danger"></zn-tile> <zn-tile caption="Brand guidelines" description="Document · v4" icon="palette" icon-color="neutral"></zn-tile> <zn-tile caption="Weekly standup" description="Event · Mondays 9:30" icon="event" icon-color="info"></zn-tile> <zn-tile caption="Product photos" description="Folder · 126 images" icon="photo_library" icon-color="ai"></zn-tile> <zn-tile caption="Refund policy" description="Document · Legal review" icon="gavel" icon-color="warning"></zn-tile> <zn-tile caption="Renewal reminders" description="Automation · Active" icon="autorenew" icon-color="success"></zn-tile> <zn-tile caption="Failed payments" description="Report · 18 this week" icon="credit_card_off" icon-color="danger"></zn-tile> <zn-tile caption="Warehouse stock" description="Inventory · 2,410 items" icon="inventory_2" icon-color="neutral"></zn-tile> <div slot="loader" class="tile-skeleton-row"> <zn-tile loading icon></zn-tile> <zn-tile loading icon></zn-tile> <zn-tile loading icon></zn-tile> <zn-tile loading icon></zn-tile> </div> </zn-infinite-scroll> </div>
Starting Page
Set page to the last page the server already rendered. The next request is for
page + 1.
<zn-infinite-scroll uri="/notes?page=#page#" page="3"> <!-- pages 1 to 3 --> </zn-infinite-scroll>
Server Responses
Each request is a GET with the x-kx-inline: inline header, and should return just
the items for that page.
- Items: appended to the end, then
zn-loadis emitted. -
Empty body: loading stops, the
finishedattribute is set andzn-finishis emitted. -
Error status or network failure: loading stops, the
finishedattribute is set andzn-erroris emitted.
Scroll Containers
Inside a scrolling element, a screen is that element’s height. While a page loads, scrolling that reaches the end stays in the element instead of moving the page. Once loading has finished for good, scrolling past the end moves the page as usual.
Importing
If you’re using the autoloader or the traditional loader, you can ignore this section. Otherwise, feel free to use any of the following snippets to cherry pick this component.
To import this component from the CDN using a script tag:
<script type="module" src="https://cdn.jsdelivr.net/npm/@kubex/zinc@2.0.16/dist/components/infinite-scroll/infinite-scroll.js"></script>
To import this component from the CDN using a JavaScript import:
import 'https://cdn.jsdelivr.net/npm/@kubex/zinc@2.0.16/dist/components/infinite-scroll/infinite-scroll.js';
To import this component using a bundler:
import '@kubex/zinc/dist/components/infinite-scroll/infinite-scroll.js';
Slots
| Name | Description |
|---|---|
| (default) | The first page of content. Later pages are appended here. |
loader
|
Placeholder content shown at the end while a page loads. Defaults to a skeleton bar. |
Learn more about using slots.
Properties
| Name | Description | Reflects | Type | Default |
|---|---|---|---|---|
uri
|
The page URL, with #page# replaced by the page number to load. |
string
|
''
|
|
page
|
The last page already rendered. The next request is for page + 1. When it isn’t set and
the component starts empty, the first request is for page 1.
|
|
number
|
1
|
finished
|
Set once there is nothing more to load. |
|
boolean
|
false
|
updateComplete |
A read-only promise that resolves when the component has finished updating. |
Learn more about attributes and properties.
Events
| Name | React Event | Description | Event Detail |
|---|---|---|---|
zn-load |
|
Emitted after a page has been appended. | - |
zn-finish |
|
Emitted when a page comes back empty, so there is nothing more to load. | - |
zn-error |
|
Emitted when a page request fails. Loading stops. | - |
Learn more about events.
Parts
| Name | Description |
|---|---|
loader |
The default skeleton bar shown while a page loads. |
Learn more about customizing CSS parts.
Dependencies
This component automatically imports the following dependencies.
<zn-skeleton>