A Vue.js workflow for Django

A Vue.js workflow for Django

Pairing JavaScript tooling like Vue CLI and create-react-app with traditional frameworks like Django is notoriously hard.

There isn't a "right way" to do this stuff, but thanks to Vue.js configurability you can at least choose where to put the resulting bundle.

In the following notes I present a Vue.js workflow for Django which I found out working well for most use cases.

Book in progress!

Stay tuned on my upcoming book for Apress "Decoupled Django". Understand and build decoupled Django architectures for JavaScript frontends. Follow on Twitter and Telegram for news and sneak peaks.

Configuring Vue.js

Suppose you have a Django app named billing, and you want to make the frontend of this app a single-page.

You also want to serve this single-page from within Django's umbrella to use Django built-in authentication.

First off, we generate a Vue project inside the app with Vue CLI, let's say in repo-root/billing/billing_spa Then, we set up vue.config.js, in the same Vue project folder, with the following configuration:

const path = require("path");

module.exports = {
publicPath: process.env.VUE_APP_STATIC_URL,
outputDir: path.resolve(__dirname, "../static", "billing"),
indexPath: path.resolve(__dirname, "../templates/", "billing", "index.html"),
};

With this configuration we say to Vue:

  • put static assets inside billing/static/billing
  • put the index.html inside billing/templates/billing

Django is highly configurable in regard to static files and template structure, but this setup respects Django expectations on where to find static files and templates. Your mileage may vary.

Next up, in the Vue project folder billing/billing_spa, we create an environment file named .env.staging with the following content:

VUE_APP_STATIC_URL=/static/billing/

For the setup to work, Django's STATIC_URL in development should match STATIC_URL=/static/.

For production, we create another environment file named .env.production with the following content:

VUE_APP_STATIC_URL=https://static.your-domain.com/billing/

With this configuration we move to setting up the view.

Serving Vue.js under a TemplateView

To serve the single-page, we create a view in billing/views.py as a subclass of TemplateView:

from django.views.generic import TemplateView


class Index(TemplateView):
template_name = "billing/index.html"

We then proceed to wire up the url configuration as usual (not shown here for brevity).

Once everything is in place, we can run the project in development, and in production.

How about the base template?

If you have a base template let's say in project_name/templates/base.html with some navigation, you can extend it in billing/billing_spa/public/index.html:

{% extends "base.html" %}
{% block someblock %}
<body>
<noscript>
<strong>We're sorry but <%= htmlWebpackPlugin.options.title %> doesn't work properly without JavaScript enabled. Please enable it to continue.</strong>
</noscript>
<div id="app"></div>
<!-- built files will be auto injected -->
</body>
{% endblock %}

Serving the single-page with Django in development

In development, you run the Vue.js build as follows:

npm run build -- --mode staging

This will place all the files in the appropriate folders. Once done you can run Django development server and have your app served:

python manage.py runserver

Serving Vue.js single-page in development with Django

With this setup you lose hot reloading and instant changes, but you're always free to npm run serve from Vue CLI to work in instant mode before building the project.

I consider this setup a "pre-staging" environment where I can test how Vue works within Django, with the built-in authentication system for example.

Note that I've tested this setup only with the Vue router in hash mode, though it shouldn't be hard to make it work in history mode too.

Serving the single-page with Django in production

In production, you run the Vue.js build as follows:

npm run build -- --mode production

This will place all the files in the appropriate folders again, the only difference being that publicPath will now point to the complete domain.

It's your turn now to deploy the code on your production machine, run python manage.py collectstatic and have the reverse proxy serve the static folder.

Serving Vue.js single-page in production with Django

Would love to know what's your approach, feel free to reach me out on Twitter @gagliardi_vale.

Thanks for reading!

Valentino Gagliardi

Hi! I'm Valentino! I'm a freelance consultant with a wealth of experience in the IT industry. I spent the last years as a frontend consultant, providing advice and help, coaching and training on JavaScript, testing, and software development. Let's get in touch!