Django has long been a go-to framework for developers seeking to build robust web applications.

Its templating engine allows for clean, maintainable HTML generation, while its ecosystem offers countless tools to extend functionality.

Among these tools, django-template-partials and HTMX are a great combination for creating dynamic, interactive web applications with minimal JavaScript.

In this article, we’ll explore how django-template-partials and HTMX can be used together to simplify the development of interactive components, improving both developer experience and end-user satisfaction.


What is django-template-partials?

django-template-partials is a powerful Django package designed to simplify the way templates are composed and reused.

This package introduces the concept of partials, which are reusable, encapsulated chunks of templates.

These partials help developers break down complex UIs into smaller, manageable pieces, making it easier to build and maintain web applications.

Unlike traditional template inheritance, django-template-partials emphasizes a modular approach, where components can be independently defined and rendered.

Why Use django-template-partials?

In a typical Django application, templates often rely on inheritance and block structures to maintain reusability.

While effective, this approach can lead to tightly coupled designs, where changes in parent templates propagate throughout the system, causing unintended side effects.

django-template-partials addresses this by allowing developers to create isolated template fragments, or partials, that are:

  • Reusable: Use the same partial in multiple templates without duplication.
  • Self-Contained: Encapsulate rendering logic and context within the partial, reducing complexity in the main template.
  • Composable: Combine partials to create dynamic, flexible layouts without overloading template inheritance.

Key Features

  • Encapsulation: Partials allow you to encapsulate both logic and presentation into reusable units. This encapsulation improves separation of concerns, enabling developers to focus on specific parts of the UI without worrying about the larger structure.
  • DRY Principle: By reusing partials, you avoid duplicating template code, making your application easier to maintain. Instead of copying similar sections across multiple templates, you define a single partial and use it wherever needed.
  • Cleaner Context Management: With traditional Django templates, context variables are passed down through inheritance or shared across the entire template. This can lead to naming collisions and unnecessary clutter. Partials receive their context directly, keeping the main template’s context clean.
  • Improved Testability: Since partials are self-contained, they can be tested independently, ensuring their behaviour is consistent without depending on the surrounding context.
  • Seamless Integration: Partials work seamlessly with Django’s existing template system, making them easy to adopt. You can mix and match partials with traditional templates as needed.

When to Use Partials?

  • Reusable Components: For UI elements like navigation menus, user cards, or notifications that appear across multiple pages.
  • Dynamic Content: To render sections of a page dynamically in response to user actions, often in combination with tools like HTMX.
  • Complex Layouts: To break down large, monolithic templates into smaller, manageable pieces.
  • API-Driven Frontends: For rendering small HTML fragments in response to API calls, improving performance and user experience.

A Practical Comparison: Partials vs. Template Inheritance

Feature Partials Template Inheritance
Modularity Highly modular; easy to reuse and test Less modular; relies on parent-child hierarchy
Reusability Can be reused in any template Tightly coupled to the inheritance structure
Context Isolation Context is self-contained Context is shared across the template tree
Dynamic Rendering Ideal for dynamic content updates Requires custom handling for partial updates

What is HTMX?

HTMX is a modern, lightweight JavaScript library designed to simplify the creation of dynamic, interactive web applications.

By leveraging a declarative approach, HTMX allows developers to enhance user interactions without writing extensive custom JavaScript code.

Instead, you can achieve interactivity using HTML attributes, seamlessly integrating with server-side frameworks like Django.

HTMX follows the principle of server-driven interactivity, where the server takes responsibility for rendering HTML fragments, and the browser dynamically updates specific parts of the page.

This approach minimizes JavaScript complexity and focuses on leveraging the strengths of your server-side codebase.

Why Use HTMX?

Traditional front-end development often involves frameworks like React, Vue, or Angular, which shift much of the application logic to the client side.

While powerful, these frameworks can lead to increased complexity, larger bundle sizes, and the need for specialized knowledge.

HTMX offers a more lightweight alternative by allowing the server to remain the primary source of truth for rendering, while the client handles only the interactions.

With HTMX, you can:

  • Reduce JavaScript dependency and complexity.
  • Maintain server-side rendering for improved SEO and performance.
  • Implement dynamic features with minimal effort, focusing on declarative HTML.

Key Features of HTMX

  • Server-Driven Interactivity: HTMX shifts the responsibility for rendering HTML back to the server. Instead of managing UI state and logic on the client, you can use Django views (or any server-side logic) to generate and return HTML fragments. HTMX then swaps these fragments
  • Minimal JavaScript: HTMX eliminates the need for boilerplate JavaScript. Features like live search, modals, and infinite scrolling can be implemented with just a few HTML attributes, allowing you to focus on your server-side logic.
  • Declarative API: HTMX introduces a rich set of attributes for common interaction patterns, enabling you to define behaviour directly in your HTML:
    • hx-get: Send a GET request to a specified URL.
    • hx-post: Send a POST request with form data.
    • hx-swap: Define how to replace content in the DOM (e.g., innerHTML, outerHTML, beforeend, etc.).
    • hx-trigger: Specify the event that triggers the request (e.g., click, keyup, load).
    • hx-target: Define which DOM element to update with the server’s response.

Additional Features

  • WebSocket Support: HTMX includes built-in support for WebSockets, enabling real-time updates without third-party libraries. This makes it easy to implement features like live notifications or dashboards.
  • Progress Indicators: HTMX provides built-in support for showing progress indicators during requests. Simply add a hx-indicator attribute to specify an element to show or hide while the request is processing.
  • Request Interception: HTMX supports request pre-processing through custom events, giving you fine-grained control over how requests are made or handled.
  • Event System: HTMX emits a range of events during its lifecycle, enabling you to hook into specific stages of requests (e.g., htmx:beforeRequest, htmx:afterSwap).

Benefits of Using HTMX

  • Performance: By swapping only the relevant parts of the DOM, HTMX reduces page reloads, improving the speed and responsiveness of your application.
  • SEO-Friendly: Server-rendered HTML is easily indexable by search engines, unlike client-rendered JavaScript content.
  • Simplified Workflow: Developers can focus on server-side logic and HTML without juggling complex JavaScript frameworks.
  • Lightweight: At just ~10 KB (gzipped), HTMX adds minimal overhead to your project.

Building and Comparing Implementations

To properly understand the usage of django-template-partials and it main differences with a normal Django implementation or even an HTMX implementation, we will walk through the three implementations with an example of a very simple TODO application:

  • Basic Django App (app)
  • Django with HTMX (app_htmx)
  • Django with HTMX and Template Partials (app_htmx_partials)

In parenthesis is the name of the folder/application that you can find in the source code, which you can download in the link at the end of the article.

In the next sections I assume you have knowledge of Python and Django, that you have experience creating Django apps and know how to setup models, views and URLs.

The goal here is to demonstrate and explain the key implementation differences.


Basic Django App (app)

This implementation represents a straightforward approach to building a TODO app using standard Django tools.

Let's quickly see the models.py to understand the simple database model that we will use for all implementations:

from django.db import models  
  
  
# Post model  
class Post(models.Model):  
    id = models.AutoField(primary_key=True)  
    title = models.CharField(max_length=100)  
    content = models.TextField()  
  
    def __str__(self):  
        return self.title

Here we have a simple Post model with id, title and content.

Views (views.py)

Let's now see how is the views implemented:

from django.shortcuts import render, redirect  
from app.models import Post  
  
  
# Index view  
def index(request):  
    todos = Post.objects.all()  
    return render(request, 'app/index.html', {'todos': todos})  
  
  
# Save view, receives a POST request  
def save(request):  
    if request.method == 'POST':  
        title = request.POST['title']  
        content = request.POST['content']  
        # Save the post  
        post = Post(title=title, content=content)  
        post.save()  
    # Redirect to the index page  
    return redirect('app:index')  
  
  
# Delete view, receives a GET request  
def delete(request, post_id):  
    if request.method == 'GET':  
        # Delete the post  
        post = Post.objects.get(id=post_id)  
        post.delete()  
    # Redirect to the index page  
    return redirect('app:index')

This code snippet implements basic CRUD (Create, Read, Delete) functionality for a Django-based TODO application:

  • Index View: Retrieves all Post objects (representing TODO items) from the database and renders them using the index.html template.
  • Save View: Handles form submissions via POST requests. It extracts the title and content from the request, creates a new Post object, saves it to the database, and redirects back to the index page.
  • Delete View: Processes GET requests to delete a Post by its id. After deletion, it redirects to the index page.

This structure demonstrates a typical approach to implementing views in Django, utilizing request methods to differentiate between actions and the redirect function for navigation after changes.

Templates

Now let's see the implementation of the HTML templates:

  • base.html: Provides a shared layout for the application.
  • index.html: Displays the TODO list and includes a form for adding new items.

base.html

<!DOCTYPE html>  
<html lang="en">  
<head>  
    <meta charset="UTF-8">  
    <title>Django Todo App Simple</title>  
</head>  
<body>  
{% block content %}  
{% endblock %}  
</body>  
</html>

This is a basic HTML template for a Django application. It includes a <!DOCTYPE> declaration, a <head> section with character encoding and a title, and a <body> section. The {% block content %} placeholder is used for injecting content from child templates, following Django's template inheritance system.

index.html

{% extends 'app/base.html' %}  
  
{% block content %}  
  
    <h1>Todo App Simple App</h1>  
    <form method="POST" action="{% url 'app:save' %}">  
        {% csrf_token %}  
        <input type="text" name="title" placeholder="Title" required>  
        <input type="text" name="content" placeholder="Content" required>  
        <button type="submit">Add</button>  
    </form>    <ul>        {% for todo in todos %}  
            <li>  
                {{ todo.title }} - {{ todo.content }} | <a href="{% url 'app:delete' todo.id %}">Delete</a>  
            </li>        {% endfor %}  
    </ul>  
  
{% endblock %}

This Django template extends a base template (app/base.html) and defines the content block. It renders a simple TODO app interface with the following elements:

  • Title: Displays "Todo App Simple App" as a header.
  • Form: A form for adding new TODO items, with fields for title and content, protected by a CSRF token and submitting to the save view.
  • TODO List: Iterates through todos and displays each item's title and content in a list. Each item includes a delete link that triggers the delete view for the corresponding todo.

Workflow

This app relies on traditional Django rendering techniques:

  • When a user adds or deletes a TODO item, the page reloads entirely.
  • All data is fetched from the database and passed to the template for re-rendering.
  • While simple and functional, this approach may feel slow and inefficient for users since every action disrupts the page flow.

Running example

Let's see this example in action:

0:00
/0:00

Basic Django App


Django with HTMX (app_htmx)

Let's now see a more modern approach that builds upon the basic app by integrating HTMX, a modern library that allows seamless, dynamic updates to parts of a webpage.

Views (views.py)

from django.shortcuts import render, redirect  
from app_htmx.models import Post  
  
  
# Index view  
def index(request):  
    todos = Post.objects.all()  
    return render(request, 'app_htmx/index.html', {'todos': todos})  
  
  
# Save view, receives a POST request  
def save(request):  
    if request.method == 'POST':  
        title = request.POST['title']  
        content = request.POST['content']  
        # Save the post  
        post = Post(title=title, content=content)  
        post.save()  
        # Get all posts  
        todos = Post.objects.all()  
        # Redirect to the index page  
        return render(request,'app_htmx/list.html', {'todos': todos})  
  
  
# Delete view, receives a GET request  
def delete(request, post_id):  
    if request.method == 'GET':  
        # Delete the post  
        post = Post.objects.get(id=post_id)  
        post.delete()  
        # Get all posts  
        todos = Post.objects.all()  
        # Redirect to the index page  
        return render(request, 'app_htmx/list.html', {'todos': todos})```


This code defines three Django views for a TODO application using HTMX for dynamic updates:

  • Index View: Fetches all Post objects (TODOs) and renders the main index.html template.
  • Save View: Handles POST requests to save a new TODO item. After saving, it fetches all TODOS and renders the updated TODO list using the list.html partial template, which is dynamically updated via HTMX.
  • Delete View: Handles GET requests to delete a specific TODO item by its id. After deletion, it fetches all TODOs and renders the updated TODO list using the list.html partial template for HTMX-based dynamic updates.

Templates

This app separates the TODO list into a partial template (list.html) to facilitate targeted updates:

  • base.html: Base layout remains unchanged, but there is now an import to the HTMX library.
  • index.html: Uses HTMX attributes to load and update the TODO list dynamically.
  • list.html: Contains the TODO list markup and is included dynamically via HTMX.

base.html

<!DOCTYPE html>  
<html lang="en">  
<head>  
    <meta charset="UTF-8">  
    <title>Django Todo HTMX Simple</title>  
    <script src="https://unpkg.com/htmx.org@2.0.4"></script>  
</head>  
<body>  
{% block content %}  
{% endblock %}  
</body>  
</html>

This HTML template serves as the base layout for a Django app using HTMX. It includes a script tag, which adds the HTMX library for enabling dynamic updates without full-page reloads.

list.html

<ul>  
    {% for todo in todos %}  
        <li>  
            {{ todo.title }} - {{ todo.content }} |  
            <a href="" hx-get="{% url 'app_htmx:delete' todo.id %}" hx-target="#todos" >Delete</a>  
        </li>    {% endfor %}  
</ul>

This Django template snippet generates a list of TODOs dynamically. Each TODO is displayed with its title and content, along with a "Delete" link. The delete link uses HTMX to send a request to the delete view and updates the #todos element without reloading the page.

💡
Note: If you are a subscriber of the 'Developer Tier', you can download the source code for free at the end of the article.

If not, you can get a copy of the source code here.

index.html