Hyper · htmx-style

Lazy loading & infinite scroll

kj-trigger="revealed" waits until an element scrolls into view. Make the last item of each page load the next one and you have infinite scroll — in HTML.

How the infinite feed works

Each page the server returns ends with a sentinel: a list item carrying kj-get="/api/page.php?p=N+1", kj-trigger="revealed" and kj-swap="outerHTML". When it scrolls into view, it replaces itself with the next page — which ends with the next sentinel. The last page simply doesn't include one.

revealed uses an IntersectionObserver, fires once, and works inside scrollable containers. Use kj-trigger="load" for content that should arrive right after the page renders, such as a slow widget you don't want to block the page on.

Live · Infinite feed (scroll inside the box)

  • Loading…

Comments with kj-trigger="load"

  • Loading comments…

Zero JavaScript. page.php renders six pages of four posts; each page ends with the sentinel for the next.

View the source for this demo
HTML
<div class="feed-box" tabindex="0" role="region" aria-label="Infinite feed, scroll to load more">
  <ul class="list-plain" id="feed">
    <li kj-get="/api/page.php?p=1" kj-trigger="revealed" kj-swap="outerHTML">Loading…</li>
  </ul>
</div>
<h4 style="margin:22px 0 8px">Comments with <code>kj-trigger="load"</code></h4>
<ul class="list-plain" kj-get="/api/comments.html" kj-trigger="load"><li>Loading comments…</li></ul>

More examples

The sentinel the server appends

HTML
<li kj-get="/api/page.php?p=3" kj-trigger="revealed" kj-swap="outerHTML">
  Loading page 3…
</li>

API

kj-trigger="event [delay:Nms] | load | revealed | every Ns"

When to fire: any DOM event (with optional debounce), load (immediately), revealed (when scrolled into view, via IntersectionObserver), or every Ns (polling).

Reference: kj-trigger →
kj-swap="innerHTML | outerHTML | textContent | beforebegin | afterbegin | beforeend | afterend | none"

How the response is inserted into the target. outerHTML replaces the target entirely; the four position values insert adjacent to it; none makes the request without touching the DOM.

Reference: kj-swap →

Migrating

Coming from htmxIdentical to the classic htmx infinite-scroll pattern with hx-trigger="revealed".

Related features