Using Webpack with Django: no plugins required!
April 19, 2020
This post explores setting up Webpack in a Django project with minimal friction. The defacto solution for this, django-webpack-loader
, is too heavy handed in my opinion. This post aims to provide a guide to setup Webpack in Django, without any plugins and using both Webpack's and Django's strengths.
Django-webpack-loader
django-webpack-loader seems to be the defacto way of setting up Webpack in Django projects. I tried using it but didn't like that:
- It requires a non-standard manifest plugin (that was also buggy for me).
- It provides a set of custom Django template helpers, instead of using the excellent Django built-in
static
functionality. - It required a bunch of configuration in Django, to make the custom helpers work.
It feels like a very heavy handed solution requiring both a Webpack plugin and a Django plugin.
Disclaimer: While I'm criticizing django-webpack-loader here this is not meant to knock on the hard work that the maintainer(s) have undoubtedly put into this.
Vanilla Django & Webpack
Making Django and Webpack play nice together turns out to be pretty straightforward, without needing any Webpack or Django plugins.
Django has built-in support for handling static assets. Django can serve static assets in development and compress files and hash filenames during production deployment. Webpack can do this too (and much more), but it's much more convenient to let Webpack only worry about producing your assets and Django about handling your assets.
In practise this works as follows:
1. Use Django's default Static support
Refer to the Django docs for details. Important settings are:
STATICFILES_DIRS
STATIC_URL
STATICFILES_STORAGE
- UseManifestStaticFilesStorage
to let Django hash filenames for cachability.
2. Configure Webpack to write files to STATICFILES_DIRS
You can use any Webpack configuration you like. Only a couple of settings are important:
- Have Webpack write to a directory in
STATICFILES_DIRS
- Configure
output.publicPath
to match Django'sSTATIC_URL
- Don't let Webpack hash filenames (except for chunks, see below)
- Make sure
webpack-dev-server
writes files to disk (so Django can serve them in development)
// Relevant parts of webpack.config.js
output: {
path: path.resolve(__dirname, "myapp/static"), // Should be in STATICFILES_DIRS
publicPath: "/static/", // Should match Django STATIC_URL
filename: "[name].js", // No filename hashing, Django takes care of this
chunkFilename: "[id]-[chunkhash].js", // DO have Webpack hash chunk filename, see below
},
devServer: {
writeToDisk: true, // Write files to disk in dev mode, so Django can serve the assets
},
Now, during development you run both webpack-dev-server
and the Django server. You can use a Procfile and a tool like Goreman to conveniently do this without having to open two terminals. Webpack writes files into STATICFILES_DIRS
and Django serves the files. Best thing is: you still get all the Webpack goodies like hot reloading and dynamic imports!
To include an asset produced by Webpack you use the Django static
template tag:
{% load static %}
<link rel="stylesheet" type="text/css" href="{% static 'app.css' %}">
<script type="text/javascript" src="{% static 'app.js' %}"></script>
For production deployments you first have Webpack build your assets and then use manage.py collectstatic as usual. Presto! Just plain Django and Webpack, nice and simple.
Webpack chunks (dynamic imports)
As I mentioned above it's important to do let Webpack hash chunk filenames. By default Webpack uses numeric chunk ids to refer to chunks at runtime. When Django's collectstatic
runs, it hashes the filenames and then looks for unhashed references in other files to replace them.
For example if you have a stylesheet that refers to a file foo.jpg
and Django renames foo.jpg
to foo.695e1b313f34.jpg
, it will replace the foo.jpg
reference in the stylesheet with foo.695e1b313f34.jpg
.
Webpack at runtime refers to chunks by their numeric chunk id
by default and as such collectstatic
will not correctly recognize the chunk filenames. By letting Webpack hash the chunk filenames we get properly hashed chunk filenames, so these files can be cached properly by the browser.
Example project
I've created a demo project for this setup on GitHub here so you can see it in action. It's also deployed on fly.io here.
If this was useful to you I'd love to hear from you!