3 weeks ago

Using Tailwind CSS with Django 3.x

tkrugg avatar


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

├── 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 %}


    <link href="{% static 'theme/style.css' %}" />



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 lean how to add Vite to Django, go over to Using Vite with Django 3.x.


© 2021 Highscore Studio. Component copyrights belongs to their authors.