Create endless pagination with Django and AJAX

Create endless pagination with Django and AJAX

Hi Devs! Today I am going to show you quick tutorial about how to create endless pagination or infinite scroll with Django

There are many sources on internet related with this topic but majority of them are not fully explained. So, I will try to explain it clearly and step by step for you.

Click to see YouTube Tutorial of this post

Alright! Let's launch our infinite-scroll-django rocket!

I am assuming that you already created your project

Step 1: Creating our model

We are going to create very simple blog with posts. Let's create our post model in

from django.db import model

   class Post(models.Model):
     title = models.CharField(max_length=250)
     description = models.TextField()
     image = models.FileField(null=True, blank=True)

     def __str__(self):
         return self.title

Don't forget to migrate your model :)

Step 2: Creating our view

In we are going to import ListView, it is a class-based view and it will contain the list of objects.

 from django.shortcuts import render
  from .models import Post
  from django.views.generic.list import ListView

  class PostsView(ListView):
      model = Post 
      paginate_by = 2
      context_object_name = 'posts'
      template_name = 'posts.html'
      ordering = ['title']

As you see I will load 2 posts per scroll by using paginate_by attribute but you can change it how many you want. The context_object_name attribute specifies the name of variable to access from templates. Providing a useful context_object_name is always a good idea. Your coworkers who design templates will thank you. In addition, you can order posts by title or published date but for now let's keep it title.

Step 3: URL Configuration

Time to configure our, so let's see the code first.

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

  from posts.views import PostsView
  from django.conf import settings 
  from django.conf.urls.static import static 

  urlpatterns = [
      path('', PostsView.as_view(),  name="posts"),
  ] + static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)

In class-based views, you have to call as_view() function so as to return a callable view that takes a request and returns a response. Its the main entry-point in request-response cycle in case of generic views.

Just reminder static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT) is for serving media files.

Step 4: Waypoints JS and Template

Waypoints is a small jQuery plugin that makes it easy to execute a function whenever you scroll to an element.

Documentation of Waypoints

Alright! Let's see the code first

 <div class="container">
                <div class="row infinite-container">
                    {% for post in posts %}
                        <div class="col-md-6 infinite-item">
                            <div class="card mb-4 shadow-sm">
                                <img class="img-thumbnail"  src="{{post.image.url}}"/>
                                <div class="card-body">
                                    <p class="card-text">
                    {% endfor %}
                {% if page_obj.has_next %}
                <a class="infinite-more-link" href="?page={{ page_obj.next_page_number }}"></a>
                {% endif %}
                <div class="d-flex justify-content-center" style="display:none;">
                    <div class="spinner-border" role="status">
                        <span class="sr-only">Loading...</span>

    <script src="/static/js/jquery-2.2.4.min.js"></script>
    <script src="/static/js/jquery.waypoints.min.js"></script>
    <script src="/static/js/infinite.min.js"></script>
    var infinite = new Waypoint.Infinite({
        element: $('.infinite-container')[0],
        handler: function(direction) {
    offset: 'bottom-in-view',

    onBeforePageLoad: function () {
    onAfterPageLoad: function () {


This is the main part of tutorial, we are first including jquery-2.2.4.min.js. It is better to include 2.2.4 version of jQuery otherwise it will cause errors. Then add jquery.waypoints.min.js and infinite.min.js

You can get scripts from directly github repo of waypoints.

jquery.waypoints.min.js infinite.min.js

element option for Infinite referes to the container around all infinite items that will be used as the waypoint and fetched items will be appended to.

.infinite-more-link is matching the "Next Page" element on every page.

.infinite-item is a selector string that should match the individual items that will be pulled out of each page and appended to the item container.

offset is the same offset option from a regular Waypoint, except the default is now 'bottom-in-view' instead of 0. This means, by default, new items will be loaded when the bottom of the container comes into view.

PageLoad functions is for display spinner while posts are loading

Mission Accomplished!

You just learned something very useful today and launched your infinite-scroll-django rocket! You can clone or download this project from my git repository and don't forget to follow me on social media, join Reverse Astronauts community!

Please support the lab with buying a cup of coffee! It is important to make the lab active! Thank you!

Buy me a coffeeBuy me a coffee



Stay Connected!