Santa Fe Trail, Huaraz, Peru (2017)

Santa Fe Trail, Huaraz, Peru (2017)

Exploring Frontend Options for Django Applications

Going beyond React + DRF. But maybe not surpassing it.

(Updated )

Django is now 20 years old. And Django’s frontend development philosophy is also 20 years old.

Server-side rendering (SSR) with HTML templates worked great in 2003. But the requirements of modern web applications are increasingly difficult to achieve using Django’s default frontend templating engine.

Today, many (if not most) large-scale Django applications do away with using Django templates altogether. It’s much more common to see organizations build out decoupled React Single Page Applications (SPAs) and then connect them to Django backends which are solely responsible for serving data.

In the minds of some developers, this is overkill. And so a third philosophy has come into play, which attempts to marry the simplicity and rapid development of Django’s SSR with the dynamicism of modern JavaScript frameworks. This involves the use of lightweight frameworks like htmx and alpine.js to enhance the functionality of Django templates.

But even htmx can’t compete with the feature-completeness, community, and 3rd-party package support of a full SPA framework. With that in mind, I set out to explore a different hybrid approach, using Vue.js within Django templates. My theory was that this would truly be the best of both worlds: the ability to scale up beyond the feature boundaries of plain Django templates/htmx/alpine, while also reaping all the benefits that come from using the Django templating engine.

But before I do that deep dive into Vue + Django, I’d like to use this article to do an overview of these 4 competing philosophies. This should shed some light onto why people would choose one approach over another, and why I felt the need to explore a new option.

Table of Contents:

Comparison Chart

I’ve narrowed down the main differences between our 4 approaches to these 6 features. They’re listed in their rough order of importance to me. In the two areas where both htmx and vue both have yellow rankings, htmx edges out as the winner.

Legend:

Best (Green)
Somewhere in the middle (Yellow)
Worst (Red)
Feature \ ApproachDjango SSRReact SPAhtmx + SSRVue + SSR
Development SpeedBestWorstSomewhere in the middleSomewhere in the middle
Feature Completeness (Can it build moderately complex UIs?)WorstBestSomewhere in the middleBest
Learning Curve for a Backend Python DeveloperBestWorstSomewhere in the middleSomewhere in the middle
Integration with 3rd Party JavaScript LibrariesWorstBestWorstBest
Community SupportBestBestSomewhere in the middleWorst
Support for Django Template tagsBestWorstBestBest

Primarily, I want a frontend that can be developed quickly. But I also want a frontend that isn’t restricted in what features it can implement.

But there are other factors to consider. If I hand off the project to a mostly Django backend team, will they be able to easily maintain it? Can we plug in helpful 3rd Party JavaScript libraries easily? Is there a larger community out there that has solved some of the low hanging fruit issues that I’ll run into? And can I utilize the good parts of Django templates if I need to?

If you look at the zig-zagging greens and reds between Django SSR and React SPA, you’ll understand why people are motivated to explore new approaches. The strongest points of Django SSR frontends are the weakest points of React SPA frontends. And vis versa. You can’t get the benefit of one without a big tradeoff in another area.

htmx + Django SSR has more feature completeness than vanilla Django templates, but still won’t compare to the functionality of a full JavaScript SPA. I’ve found that using Vue + Django SSR can bridge that feature gap, but at the cost of other quality-of-life factors.

Let’s explore how each of these approaches work in practice.

1. Django Template Server-Side Rendering (SSR)

If you follow Django’s official tutorial, this is still how they’ll tell you to develop your frontend views. Render your template on the Django server and send it to the client. If your users have any changes to apply, they send an HTTP request back to the Django server, and Django serves back a new rendered template.

The flow looks like this:

Diagram outlining how server-side rendering works in Django. Step 1: Client makes a GET Request to Django View. Step 2: Django View responds with Django template HTML. Step 3: Client renders static HTML.

And if you do want to include any interactive elements, you’re free to load any JavaScript static assets that you want:

Diagram outlining how server-side rendering works in Django, with the addition of JS static assets. Step 1: Client makes a GET Request to Django View. Step 2: Django View responds with Django template HTML (including a JS script tag). Step 3: Client renders static HTML and loads JS script.

With this approach, you can add JavaScript/jQuery incrementally. But the majority of the rendering is going to be done with static Django HTML templates.

This means that by default, nothing is dynamic. If, for example, you submit a form request, the response that gets returned will have to reload an entirely separate Django template. This is where the 20-year-old frontend development philosophy starts to show its age.

Diagram illustrating how updates work with Django server-side rendering. It shows a POST request to the Django View which results in a redirect to a new page utilizing a new template.

Pros:

  • Rapid Development: Django SSR can quickly scaffold a basic web application. This works great for very simple CRUD applications.
  • Django Templates have their place: There are some ways that Django templates are actually really good! Limited though they may be, creating automated ModelForms out of Models is a blessing.
  • Ease of Use: Requires minimal (if any) JavaScript. This is great for backend developers who know Python and HTML, but not necessarily React/Vue/Angular.

Cons:

  • Limited Interactivity: Dynamic content requires additional JavaScript or AJAX, potentially leading to complex and unmanageable code. There comes a point where it would have been simpler to just write everything in React.
  • Static UX: Every user interaction typically results in a full page reload, contrasting sharply with the fluid experience offered by modern web apps.

Notable Open Source Examples:

So then the question is, how complex does my application need to be before I need to make the switch to a React SPA? Like, I’m not trying to build an airBnB or Google Docs or whatever. What if I’m just building a totally basic, totally utilitarian, business CRUD application?

In my experience, the answer is that you’ll hit that limit faster than you’d think. Even business users expect a level of interactivity that Django templates weren’t designed to handle. Do you want autocomplete in your form fields? Conditional form fields? Instantaneous filtering? Updating a single row of data without triggering an entire page reload? These are basic CRUD features that in 2024 every business user takes for granted. But to implement them in Django templates you’ll have to either build them out from scratch or use a patchwork of spuriously-maintained 3rd party packages designed to work around Django SSR’s limitations.

It’s no wonder why so many teams opt to just use React instead.

2. Decoupled React SPA

“React” is used as a stand-in for any JavaScript SPA framework (Vue, Angular, etc.). The developer experience and pros/cons are fundamentally going to be the same.

With a decoupled React SPA approach, we take the “View” responsibilities away from Django and delegate them a separate React application with its own build, deployment, and development process. Django is responsible only for serving the data to populate that React application, probably using a package like django-rest-framework (DRF) or graphene-django.

This is a diagram outlining how Django interacts with a decoupled React Single Page Application. Step 0: The Frontend is generated by a separate React App. Step 1: Client makes a GET request to Django Rest Framework View. Step 2: DRF View responds with JSON. Step 3: Client loads data into React jsx template.

It requires a bit more upfront effort to get running — developers now need to maintain a React application and all of its associated friends (Webpack, Babel, TypeScript, npm, etc.).

Which is fine! It’s not as bad as the memes make it out to be. I wouldn’t discourage anyone from learning React development. But the point is, you’ve now got to add a capacity to your team well beyond the bounds of Django development.

But once you’ve gone through the trouble of building out your React application, a lot of nice UX features are made a lot easier. Such as updating pages without requiring reloads:

This diagram illustrates how updates work for a decoupled React Single Page Application interacting with a Django Rest Framework server. A POST request goes to the Django server. A JSON response is returned to the same React Application Page, updating it in place.

Pros:

  • Dynamic UX: Your application can be fully interactive with no page reloads required. You may not get the benefits of Django’s own intergration with its templates (like ModelForms) but you won’t be hindered by their limitations either. Pretty much any requirement that a modern web application could require can be handled by your SPA.
  • Modern JavaScript Ecosystem: Tap into the full React ecosystem of UI frameworks and libraries: no need to ever reinvent the datepicker wheel.

Cons:

  • Complexity: Requires proficiency in React and JavaScript tooling, increasing the learning curve for developers.
  • Longer Development Time: Managing two separate codebases can complicate the development and deployment process.

Notable Open Source Examples:

  • Wagtail — A robust open-source CMS platform, and a leader in the open-source Django community.
  • Sentry — One of the most popular application monitoring tools happens to be built in Django.
  • Posthog — This is a great example of using DRF (with typing!)

Now let’s explore some experimental options that are attempting to get the best of both worlds.

3. htmx with Django SSR

Full disclosure: I haven’t tried this stack yet. I’m still wary. If the theory holds, and it can address all of my business CRUD application feature requirements without needing additional JavaScript, then I’d be happy to adopt it. But right now, I’m concerned that it’ll just push the feature-completeness boundary slightly farther than Django SSR templates, but I’ll still end up hitting a dead end. If you have some notable examples of feature-complex htmx apps, please send them my way!

The Django + htmx stack is a emerging trend that’s designed to get the Dynamic UX benefits of a React SPA, without the Complexity downsides that comes with it.

htmx is a lightweight web framework designed for making dynamic things easy to do without writing your own JavaScript. No build process required!

How this works is you inject an htmx script into your Django templates, which now enable you to use special htmx attributes like hx-post. You’ll have to make receivers on the Django server to handle any update actions, but you can do that without necessarily decoupling all data operations from your Django Template View.

A diagram outlining how an htmx-based frontend works. A django template contains a script tag loading htmx.min.js, which enables special html attributes such as 'hx-post'.

Now, instead of returning raw data from your Django server, htmx returns HTML partials that update your template.

A diagram showing how updates work in htmx. A POST request to the Django server results in a response containing the HTML partial that will be swapped by htmx.

Pros:

  • A Simpler way to get Dynamic UX: Easy to integrate with existing Django templates. Static content stays static, dynamic content gets to be dynamic.
  • No Heavy Frameworks: You get that interactivity without requiring React or any complex JavaScript build tools.

Cons:

  • Scalability: I’m still not sure what the limit is for htmx. Can it handle all of the moderately complex user interactions that are required for modern web applications? Where’s the cutoff point where React becomes required?
  • Limited Ecosystem: You don’t get access to the excellent community packages offered by the modern React / JavaScript ecosystem. I mean, there’s nothing stopping you from using them. But they’ll integrate with your forms + apps as well as they would in vanilla Django templates (i.e. not terribly well).

Notable Open Source Examples:

  • realworld — realworld is a project that every web developer should know about. It’s the same demo app, but reimplemented in just about every combination of frontend and backend that you can think of. This particular implementation uses Django + htmx + alpine.js. A great resource for anyone looking to learn this stack.

By all accounts, the early adopters of htmx love it. But I still have my reservations about going completely without JavaScript.

Which leads us to my foolish attempt to truly get the best of all worlds.

4. Vue with Django SSR

My thinking was, if people are injecting htmx into Django templates, why not just inject Vue instead?

A diagram outlining how a Vue frontend works. A Django template contains script tags loading vue.min.js and app.js, which enables custom vue components such as the example my-vue-component.

The setup uses the same conceit as htmx. Add a vue script to your Django template, and now your components are as interactive as you need them to be.

If needed, your application features can scale up to the level of complexity offered by a full React and/or Vue SPA. And you get to use any JavaScript library you want, including the world’s greatest datepicker.

Pros:

  • Island Architecture: You can inject dynamic components anywhere without commiting to writing your whole Frontend application in React/Vue.
  • Access to the modern JavaScript ecosystem: You have the ability to use all modern JavaScript UI frameworks and packages as you see fit, just as you would with a decoupled SPA.
  • No limits to functionality: Scale up to the full functionality of an SPA, all within a Django Template.

Cons:

  • Still requires knowledge of JavaScript development: You’ll have to learn Vue. And you’ll have to learn a JavaScript build tool like vite. Fortunately, there’s this supergood site that can help you.
  • Trapped between two worlds: It gets complicated moving from vue-controlled elements to Django template-controlled elements. During my work-in-progress exploration, there were many times when it felt like it would’ve been easier to either fully commit to an SPA or fully commit to Django templates.
  • It’s the Wild West: There are no established open-source best practices for handling all the edge cases you’d need to support a production application. There are a handful of trailblazers that we’ll shout out in further reading, but there is not really community support to speak of.

Notable Open Source Examples:

Conclusion

Despite all the work I’ve put into trying to get Vue to interoperate with Django templates, if I had to build a production Django application today, I’d just go with React + DRF. It’s slower, but I know it’ll work, and I know I’ll never hit a wall with what features I can deliver.

I’m keeping an eye on htmx. It would be great to be able to speed up my development time and use more of Django’s built-in template utilities in my frontends.

And I haven’t given up on using Vue with Django templates. It may not have been the silver bullet I had hoped, but I think there may be a place for it. Stay tuned for a deeper dive into my explorations with Vue and Django templates!

🔆

Further Reading

On Vue + Django:

On the SPA + DRF approach:

On the htmx approach: