A long list can load its next page when the user scrolls to its end, without custom JavaScript. The "load more" link at the bottom is a deferred link that loads when revealed, appends the next page to the list and replaces itself with a link to the page after that.
All pages are children of a container (#pages). Below the container,
a link to the next page carries an [up-defer="reveal"] attribute:
<div id="pages">
<div class="page">items for page 1</div>
</div>
<a id="next-page" href="/items?page=2" up-defer="reveal" up-target="#next-page, #pages:after"> <!-- mark: up-target="#next-page, #pages:after" -->
Load next page
</a>
Note the following:
[up-target] names two fragments: the link itself (#next-page) and the list of pages (#pages).#pages container, the :after suffix
appends the children of the response's #pages to it.The link remains a standard hyperlink to /items?page=2, so every page has a URL that can be
shared or indexed. This follows
Google's recommendation
for paginated content.
When the user scrolls to the link, Unpoly requests it:
GET /items?page=2 HTTP/1.1
X-Up-Target: #next-page, #pages
The server responds with HTML containing both targeted elements.
The #pages container holds only the items for page 2, and the link now points to page 3:
<div id="pages">
<div class="page">items for page 2</div> <!-- mark-line -->
</div>
<a id="next-page" href="/items?page=3" up-defer="reveal" up-target="#next-page, #pages:after"> <!-- mark: /items?page=3 -->
Load next page
</a>
Unpoly appends the new .page to the existing container and swaps the link.
The page now looks like this:
<div id="pages">
<div class="page">items for page 1</div>
<div class="page">items for page 2</div> <!-- mark-line -->
</div>
<a id="next-page" href="/items?page=3" up-defer="reveal" up-target="#next-page, #pages:after"> <!-- mark: /items?page=3 -->
Load next page
</a>
The new link is again deferred with [up-defer="reveal"].
When the user keeps scrolling, it loads page 3 the same way.
Pages loaded by scrolling are not part of the URL. When the user navigates away and comes back, the list starts again with the first page.
On the last page, render an empty or text-only #next-page element instead of a link.
Without an [up-defer] attribute, nothing more is loaded:
<div id="pages">
<div class="page">items for last page</div>
</div>
<div id="next-page">You've reached the end.</div> <!-- mark-line -->
By default the next page loads when the link becomes visible.
To start loading earlier, set an [up-intersect-margin] attribute
with a number of pixels:
<a id="next-page" href="/items?page=2" up-defer="reveal" up-intersect-margin="500" up-target="#next-page, #pages:after"> <!-- mark: up-intersect-margin="500" -->
Load next page
</a>
The link now loads when it is within 500 pixels of the viewport, so the next page is often there before the user reaches the end of the list.
Tip Your coding agent can search these docs locally with the Unpoly agent skill