Django REST Framework and Vue versus Django and HTMX

Last updated February 5th, 2024

This article details the differences between developing an application with Vue and Django REST Framework (DRF) versus HTMX and Django. We'll look into a simple example of how both combinations work and compare their strengths and weaknesses.

Vue and Django REST Framework

Vue, while less popular than Angular and React according to the StackOverflow Developer Survey, stands out for its simplicity and focus on the view layer of web applications. It's lightweight and can be started from just a single JS file. This makes for a gentler learning curve and smoother integration into existing projects compared to React or Angular.

While it's quite powerful on its own, Vue's functionality is greatly enhanced by additional libraries and tools. Tools like Vite for project management, Pinia for state management, and Vue Router for client-side routing, all contribute to a more robust Vue development experience.

One of Vue's standout features is the Single File Component. This approach encapsulates HTML templates, JavaScript logic, and scoped CSS within a single file for better organization and maintainability. This structure not only makes Vue components self-contained and easy to understand, but also demonstrates Vue's core principle of reactive data binding, which is essential for interactive web applications.

Since Vue works in the browser, it can hold the data only temporarily. To preserve the data beyond that, Vue needs to be paired with a backend server. Due to the nature of Vue, where Vue sends a request to the backend server and then processes the response, the framework used for the backend has little impact. While this article uses Django REST Framework (along with Django, of course), you can easily use FastAPI or a framework from another language.

Let's look at an example of a simple page that generates a password.

DRF provides an endpoint that, upon a GET request on a particular URL, returns a response containing a password:

# view
class GeneratePassword(APIView):
    def get(self, request):
        password = "".join(
            secrets.choice(string.ascii_letters + string.digits + string.punctuation)
            for _ in range(12)
        )
        return Response(password)


# URL
urlpatterns = [
    path("generate-password/", views.GeneratePassword.as_view(), name="generate_password"),
]

Vue, a standalone app, sends a request to the password endpoint and processes the response. While the Vue application has a more complex setup, the part that directly interacts with the password endpoint is the password component:

<template>
  <p>{{password}}</p>
  <br>
  <button @click="getPassword">Generate a password</button>
</template>

<script>
import axios from 'axios';

export default {
  data() {
    return {
      password: ''
    };
  },
  methods: {
    getPassword() {
      const path = 'http://localhost:8000/generate-password/';
      axios.get(path)
        .then((res) => {
          this.password = res.data;
        })
        .catch((error) => {
          console.error(error);
        });
    }
  }
}
</script>

<style scoped>

</style>

The component is self-sufficient and allows you to put HTML, JS, and CSS in the same file. When the page is loaded, an initial (empty) value is displayed for the password. When the user clicks on the button, getPassword is called. That method then evokes a call to the API endpoint with the help of the Axios library. The data it gets back is then saved in a data property named message. Since the two-way-binding data was changed, the part of the page displaying it gets updated.

drf and vue diagram

HTMX and Django

HTMX has a reputation of a being "JavaScript framework for people who hate JavaScript". It's rapidly gaining popularity due to its lightweight and ease of use. If you've used any frontend framework before, it has an entirely different feeling to it -- it's less a framework and more of HTML on steroids. When using HTMX, you can easily achieve dynamic page rendering just by using a handful of HTMX-specific attributes directly in HTML.

The core attributes allow you to set an HTTP method and URL for the request (e.g., hx-put), set what event is going to trigger it (hx-trigger), and set the element that's going to be altered with the response (hx-target).

The easiest way to work with HTMX and Django is to heavily intertwine them. Unlike the Vue and DRF pairing, where each framework can be swapped out independently, you can't just unplug Django or HTMX and replace it with something else (at least not easily).

On the one hand, the Django code intended for HTMX interactions differs from regular Django code. Conversely, HTMX is embedded directly within Django templates, not existing as separate files. Once you decide to use that combination, replacing either tool with something else would require a complete rewrite.

On the other hand, using plain Django and deciding you need a little more interactivity along the way poses no problem for the lightweight HTMX. Your whole project could rely solely on Django, and adding a single HTMX functionality after a few months would require little overhead.

Let's see what accomplishing the same thing -- a simple password generator with Django and HTMX -- looks like.

HTMX is simply included in Django, meaning there are no specific HTMX files. Views and URLs look exactly like you're used to in plain Django:

# views
class GeneratePasswordPage(TemplateView):
    template_name = "generate_password_page.html"


class GeneratePassword(APIView):
    def get(self, request):
        password = "".join(
            secrets.choice(string.ascii_letters + string.digits + string.punctuation)
            for _ in range(12)
        )
        return Response(password)


# URLs
urlpatterns = [
    path("password/", views.GeneratePasswordPage.as_view(), name="generate_password_page"),
    path("password/generate", views.GeneratePasswordPartial.as_view(), name="generate_password_partial")
]

The template is a typical Django template with a bit of HTMX sprinkled in:

{% extends 'base.html' %}

{% block content %}
  <p id="password">{{ password }}</p>
  <br>
  <button hx-get="{% url 'generate_password_partial' %}"
          hx-target="#password"
  >
    Generate a password
  </button>
{% endblock %}

As you can see, the template is simpler than the Vue counterpart. It almost completely relies on Django -- HTMX sends the request and includes the response in the HTML.

HTMX does not have built-in state management; it fully depends on the backend (Django, in our case) to manage the state. In this case, Django provides two endpoints because, unlike the Vue case where Vue manages the initial page, here it's Django that's responsible for serving the initial page. When the button is clicked, the request is made to the second endpoint. That endpoint returns a response with a password. That password is then included in the page by HTMX.

In contrast to plain Django, combining it with HTMX requires a lot of fragmented Django views. Using HTMX to achieve a Vue-like app with many changeable components can quickly become hard to manage.

django and htmx diagram

In both cases, we created an app that generates a password upon clicking the button without reloading the whole page. But we got there in two very different ways.

HTMX versus Vue

It's important to understand that directly comparing HTMX and Vue is like comparing apples and oranges. They address entirely different aspects of web development.

HTMX focuses on server-driven interactions, passing the data to the server and updating the page based on its response. Vue's primary purpose is to build and manage dynamic user interfaces on the client side. This main difference ripples through every aspect of the development process.

One of the most significant differences between HTMX and Vue is how they manage state. HTMX is stateless and relies on the server side for state management, while Vue handles state right in the browser, which is crucial for creating more dynamic user experiences.

HTMX is undoubtedly less complex -- it's an enhancement for HTML and Vue is a fully-fledged frontend framework. While adding Vue to an existing project is easier than, let's say, React, it's still more complicated than adding HTMX. However, Vue's design keeps that complexity manageable, even when you scale up your project. HTMX, while straightforward for small additions, can get trickier with larger, more complex implementations.

No matter which option you choose, it's unlikely to be the only tool you'll need. Vue has a rich ecosystem, offering integrations with CSS frameworks, routing, state management, and more. There's even a browser dev tool to aid in development. HTMX offers a handful of extensions and can be complemented by other third-party tools, yet it lacks official integration options. While commonly paired with tools such as Alpine.js and Tailwind CSS, these remain separate, without a dedicated integration tool to seamlessly connect them.

When deciding which framework to use, two aspects that often get overlooked are testing and deployment.

Having two separate projects requires two development environments and a different approach to testing. Vue can be tested either in isolation, where you need to mock backend responses, or with actual responses from backend, in which case you'd need a dedicated testing environment. Tests with a real API tend to be slower and more flaky but closer to reality. HTMX, when added to Django, can be mostly tested using Django's testing tools and supplemented with something like Cypress for the most critical user flows.

Regarding deployment, when using DRF/Vue, the most common option is to deploy them separately -- e.g., two Docker containers. Then you need to either serve them from different domains and set up CORS headers or deploy them behind a path-based proxy -- e.g., route all requests starting with the /api/ prefix to the DRF container; the rest goes to Vue -- on the same domain. In such case, you also need to handle what can be accessed/shown to authenticated/unauthenticated users on both ends -- e.g., block API requests for unauthenticated users, redirect to the login page inside Vue when the API rejects the request as unauthenticated. While this might sound like a lot of overhead, it makes perfect sense whe you need to build client-rich applications or when you have different clients. For example, if you need to build an API for an iOS app, you might as well just reuse it for your web Vue app.

If you still can't decide which one's better for you, check the table below:

Vue with Django REST Framework HTMX with Django
Learning Curve Steeper learning curve. Lower learning curve.
Development Approach SPA (Single Page Application). Requires a separate REST API. Multi-page application approach. Enhances Django templates with AJAX capabilities.
Interactivity High level of interactivity. Great for dynamic and complex user interfaces with real-time data updates. Moderate interactivity. Great for adding dynamic behavior to pages without the complexity of a JavaScript framework.
SEO Optimization Requires additional considerations for SEO, as SPAs are not inherently SEO-friendly. Better SEO out of the box since content is server-rendered.
Ecosystem Large ecosystem -- official core libraries and third-party plugins. It has few extensions and can be paired with other libraries, but the ecosystem is much smaller.
Code structure An established way of structuring code. HTMX increases the complexity of Django code. Since it's combination-specific, there's no established way of structuring it.
Scalability Better suited for large-scale, complex applications where a rich client-side experience is essential or when you're supporting multiple clients. More suitable for smaller to medium-scale applications where full-scale SPA complexity is not required.
Testing Requires separate testing strategies for frontend and backend. Testing can be primarily focused on the Django side with standard Django testing tools.

Conclusion

This article offered insights into the differences between developing an application with Vue and Django REST Framework (DRF) versus HTMX and Django.

Depending on your needs and preferences, you can now make a more informed decision on which combo to use.

The choice between Vue with Django REST Framework and HTMX with Django depends on your project's specific requirements and goals as well as your development team's expertise.

--

If you decide that HTMX suits you needs best, you can learn in detail how to combine Django and HTMX in our Full-stack Django with HTMX and Tailwind course.

If you decide that your project requires a separate front and back-end, we've got you covered as well:

  1. Developing RESTful APIs with Django REST Framework
  2. Learn Vue by Building and Deploying a CRUD App Course

Špela Giacomelli (aka GirlLovesToCode)

Špela Giacomelli (aka GirlLovesToCode)

GirlThatLovesToCode is passionate about learning new things -- both for herself and for teaching others. She's a fan of Python and Django and wants to know everything there is about those two. When she’s not writing code or a blog, she’s probably trying something new, reading, or spending time outside with her family.

Share this tutorial

Featured Course

Full-stack Django with HTMX and Tailwind

Modernize your Django application with the agility of HTMX and the elegance of Tailwind CSS.

Featured Course

Full-stack Django with HTMX and Tailwind

Modernize your Django application with the agility of HTMX and the elegance of Tailwind CSS.