Add infinite scroll with Django and HTMX in 60 seconds ♾️

Photo of Tom Dekan
by Tom Dekan
Updated: Fri 03 May 2024

I’ll show you how to add infinite scrolling to your Django app in 60 seconds using HTMX.

Infinite scroll speeds up your initial page load. Instead of immediately loading all content, you only load content as your users scroll.

There are 5 steps to add infinite scroll with HTMX and Django below.

Our finished Django app with infinite scrolling will look like this:


Optional video tutorial featuring me below 📽️:


Photo of Tom Dekan giving a talk called 'Add infinite scroll with Django and HTMX in 60 seconds' at the London Django Meetup 🇬🇧in November
Update: Here's me presenting this guide at the Django Meetup in London 🇬🇧 in November

Let’s go 🚀

Setup Django app

  • Install our requirements into a virtual environment and create our Django project and app:
pip install django faker
django-admin startproject core .
python manage.py startapp sim

Update settings.py

INSTALLED_APPS = [
    # ...
    'sim',
]

1. Create the Model and Sample Data

# sim/models.py
from django.db import models


class Article(models.Model):
    title = models.CharField(max_length=255)
    content = models.TextField()
    photo_url = models.URLField()

    def __str__(self):
        return self.title
  • Run migrations:
python manage.py makemigrations
python manage.py migrate
  • Generate some sample data:
python manage.py shell
# In the python shell:
from sim.models import Article
from faker import Faker
fake = Faker()

for i in range(100):
    sentences = ' '.join(fake.sentences(5))
    Article.objects.create(
        title=fake.sentence(),
        content=sentences,
        photo_url=f"https://picsum.photos/seed/{i+1}/600"
    )

2. Create the views

# sim/views.py
from django.core.paginator import Paginator
from django.shortcuts import render
from .models import Article


def articles(request):
    """
    Fetch paginated articles and render them.
    """
    page_number = request.GET.get('page', 1)
    paginator = Paginator(Article.objects.all(), 10)
    page_obj = paginator.get_page(page_number)

    return render(request, 'articles.html', {'page_obj': page_obj})


3. Update URLs

  • Update core/urls.py

from django.contrib import admin
from django.urls import path, include

urlpatterns = [
    path('admin/', admin.site.urls),
    path('', include('sim.urls')),
]

  • Create sim/urls.py containing:
from django.urls import path
from . import views


urlpatterns = [
    path('', views.articles, name='articles'),
]

4. Create HTML template

  • Create templates folder at sim/templates/
  • Create articles.html in sim/templates/ containing:
<!DOCTYPE html>
<html>
<head>
    <script src="https://unpkg.com/htmx.org@1.9.6" integrity="sha384-FhXw7b6AlE/jyjlZH5iHa/tTe9EpJ1Y55RjcgPbjeWMskSxZt1v9qkxLJWNJaGni" crossorigin="anonymous"></script>
    <style>
        /* Body and Container Styles */
        body {
            font-family: Arial, sans-serif;
            line-height: 1.6;
            margin: 0;
            padding: 0;
        }
        #articles {
            display: grid;
            grid-template-columns: repeat(3, 1fr);
            gap: 20px;
            max-width: 1200px;
            margin: auto;
            padding: 20px;
        }
        h1 {
            grid-column: 1 / -1;  /* Span all columns */
            margin-bottom: 20px;
            text-align: center;
            text-decoration: underline;
        }

        /* Article Styles */
        .article {
            border: 1px solid #ccc;
            padding: 20px;
            border-radius: 8px;
        }

        .article h2 {
            font-size: 24px;
            margin-bottom: 10px;
            text-align: center;
        }
        .image-container {
            display: flex;
            justify-content: center;
        }
        .article img {
            max-width: 100%;
            height: auto;
            border-radius: 8px;
        }
        .article p {
            text-align: justify;
        }
    </style>
</head>
<body>
<h1>Articles</h1>
<div id="articles">
    {% for article in page_obj %}
    <div class="article">
        <h2>{{ article.title }}</h2>
        <a class="image-container" href="{{ article.photo_url }}">
            <img width="300" height="300" src="{{ article.photo_url }}" alt="A sample image"/>
        </a>
        <p>{{ article.content }}</p>

        {% if page_obj.has_next and forloop.last %}
        <span hx-get="{% url 'articles' %}?page={{ page_obj.next_page_number }}"
              hx-swap="beforeend" hx-target="#articles" hx-select=".article"
              hx-trigger="revealed">
            </span>
        {% endif %}

    </div>
    {% endfor %}

</div>


</body>
</html>

5. Run our server

python manage.py runserver
  • Visit your development server, normally at http://127.0.0.1:8000/

Finished - You can now scroll infinitely through your articles 🎉

You've just built a sleek Django app complete with infinite scrolling. You get the benefits of loading the content that the user wants on demand, meaning a much faster initial page load.

P.S - Photon Designer

I'm building Photon Designer - an entirely visual editor for building Django frontend at the speed that light hits your eyes 💡.

Let's get visual.

Do you want to create beautiful frontends effortlessly?
Click below to book your spot on our early access mailing list (as well as early adopter prices).
Copied link to clipboard 📋

Made with care by Tom Dekan

© 2024 Photon Designer