When it comes to using elements of modern front-end stacks in Django, there is more than one way. The key is to find the configuration that combines best practices, and leverages each tool for what it's good at.
Many examples online show how to add Tailwind inside a full-fledged front-end stack, often with a complex build system setup to handle things such as ES6, Typescript, Vue or React, Sass/Less, etc.
But you don't need a complex build system to leverage the full power of Tailwind. In this tutorial, I'm going to describe a simple and straightforward way to use Tailwind with Django.
Create a new Django app
I prefer to keep my front-end code contained to a single Django app, which is convenient for most Django websites.
Create a new app called theme
.
django-admin startapp theme
In your settings.py
, make sure you add the newly created app to your INSTALLED_APPS
INSTALLED_APPS = [
...
'django.contrib.staticfiles',
'theme',
]
<root>
├── db.sqlite3
├── manage.py
├── requirements.txt
├── theme
├── __init__.py
├── apps.py
├── views.py
├── models.py
└── templates/
└── theme
└── index.html
Install Tailwind & necessary dependencies
npm init -y
npm install tailwindcss autoprefixer cssnano postcss postcss-cli
# or
yarn init -y
yarn add tailwindcss autoprefixer cssnano postcss postcss-cli
For an optimal setup, we'll be using Tailwind as a postcss plugin. This will allow us to combine it with a few other postcss plugins for automatically prefixing and minifying our CSS, namely: autoprefixer and cssnano.
Run npx tailwindcss init -p
which will create tailwind.config.js
and postcss.config.js
Let's tell tailwind where to find the used CSS classes: in tailwind.config.js
set purge: ["templates/**/*.html"]
:
// tailwind.config.js
module.exports = {
purge: ["templates/**/*.html"],
darkMode: false,
theme: {
extend: {},
},
variants: {
extend: {},
},
plugins: [],
};
Now let's make sure we run cssnano only in production, keeping our code readable and our build fast in development
// postcss.config.js
module.exports = {
plugins: {
tailwindcss: {},
autoprefixer: {},
cssnano: ctx.env === "production" ? {} : false,
},
};
Create a styles.css
inside your theme
app:
@tailwind base;
@tailwind components;
@tailwind utilities;
/* custom classes */
.btn {
@apply font-bold py-2 px-4 rounded;
}
.btn-blue {
@apply bg-blue-500 text-white;
}
We're ready to compile our styles.css
.
1, 2, 3 build!
In your package.json
, add the following scripts:
// package.json
"scripts": {
"theme:dev": "postcss styles.css -w -d static/theme/ --verbose",
"theme:build": "NODE_ENV=production postcss styles.css -d static/theme/ --verbose",
},
Assuming you're serving your app static files theme/static/theme/
to comply with Django's best practices.
Now running the build should be as simple as:
$ npm run theme:build
Processing styles.css...
Finished styles.css in 1.02 s
We've told the postcss
compiler to put all compiled assets under theme/static/theme/
.
Although having theme
twice in the path feels redundant, it helps avoid accidental static files collisions when gathering all static files with collectstatic
before going to production.
Use anywhere
We've done that hardest part, all you CSS is compiled to your static folder, prefixed, minified and purged of unused CSS classes. Now you can use it like any other static file:
<!-- index.html -->
{% load static %}
<html>
<head>
<link href="{% static 'theme/style.css' %}" />
...
</head>
<body>
...
</body>
</html>
Improvements
For a better developer experience, it's worth looking into setting up Hot Module Replacement.
As you change your CSS (or JS), HMR will detect and reinject your new code without needing to reload your page.
Most bundling solutions offer a form HMR, but Vite is one of the fastest. If you'd like to learn how to add Vite to Django, go over to Using Vite with Django 3.x.