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 📽️:
Let’s go 🚀
pip install django faker
django-admin startproject core .
python manage.py startapp sim
settings.py
INSTALLED_APPS = [
# ...
'sim',
]
# 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
python manage.py makemigrations
python manage.py migrate
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"
)
# 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})
from django.contrib import admin
from django.urls import path, include
urlpatterns = [
path('admin/', admin.site.urls),
path('', include('sim.urls')),
]
sim/urls.py
containing:from django.urls import path
from . import views
urlpatterns = [
path('', views.articles, name='articles'),
]
sim/templates/
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>
python manage.py runserver
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.
I'm building Photon Designer - an entirely visual editor for building Django frontend at the speed that light hits your eyes 💡.