During the development of my multimedia platform, I quickly realised that traditional pagination felt outdated. I wanted a seamless, modern way for users to browse media collections. ReactJS and similar frameworks felt excessive for my needs, so I kept searching for a middle ground – and that’s when I found HTMX.

What is HTMX?

HTMX is like a toolkit that extends your HTML, letting you build interactive web pages without heavy JavaScript frameworks. With just a few attributes, you can sprinkle AJAX, CSS transitions, WebSockets, and server-sent events into your HTML elements, enabling actions like updating parts of the page and submitting forms without reloadin

Implementing Infinite Scroll with HTMX

To create an infinite scroll for my media collection landing page, I used HTMX’s attributes to dynamically load more content as users scroll. Here’s how I did it:

Infinite Scrolling

Key Attributes:

AttributeDescriptionDocumentation Link
hx-getTriggers a GET request to a specified URLLink
hx-swapSpecifies how the response content is inserted into the pageLink
hx-triggerDetermines the event that triggers the action (e.g., intersect on scroll which we will use )Link

Example Code:

<div class="movie"
     hx-get="/content/movies?page=2"
     hx-trigger="intersect"
     hx-swap="afterend">
</div>

Backend Code Explanation

To handle these requests, I created a Go endpoint that paginates content data:

func getMovies(rw http.ResponseWriter, req *http.Request) {
    const maxMoviesPerRequest = 20

    // obtain movies, etc.

    pageNumber := 1
    totalMovies := len(movies)
    if totalMovies > maxMoviesPerRequest {
        pageParam := req.URL.Query().Get("page")
        if pageParam != "" {
            pageNumber, _ = strconv.Atoi(pageParam)
        }
    }

    // ...

    startIndex := (pageNumber - 1) * maxMoviesPerRequest
    endIndex := startIndex + maxMoviesPerRequest
    if endIndex > totalMovies {
        endIndex = totalMovies
    }

    if req.Header.Get("HX-Request") == "true" {
        // Render partial template
    } else {
        // Render main template
    }
}

Main Template Structure

The main template displays the maxMoviesPerRequest number in a grid. The last element of the grid will have the HTMX trigger. Once it is displayed on screen, it will trigger a GET request, rendering further items appended after the triggering element, and so on.

<main>
    <div class="movie-list">
        {{ range $index, $element := .Movies }}
        {{ if and (eq $index $.LastMovieIndex) (gt $.TotalPages 1) }}
        <div class="movie"
             hx-get="/content/movies?page={{ add $.CurrentPage 1 }}"
             hx-trigger="intersect"
             hx-swap="afterend">
        {{ else }}
        <div class="movie">
        {{ end }}
            <div class="movie-poster-container">
                <img src="{{ $element.Poster }}" class="movie-poster" id="poster-item">
            </div>
            <p>{{ $element.Title }}</p>
        </div>
        {{ end }}
    </div>
</main>

Conclusion

HTMX makes adding modern, dynamic interactions to your web app straightforward. Infinite scroll is just one example; with minimal code, you can create a smooth user experience that would otherwise require heavier tools. Try HTMX for your next project and see how it can simplify your development workflow.