How to Integrate Tailwind CSS 4 into Your Django Project

Table of Contents

In this tutorial, I will show you how to integrate Tailwind CSS 4 into your Django project.

Objectives

  1. We use python-webpack-boilerplate to create frontend project for Django.
  2. We dropped SCSS and use PostCSS to work with Tailwind CSS 4.
  3. We use webpack-dev-server to bring hot-reload feature to the Django project.

Create Django Project

Before we start, let's quickly create Django project.

$ mkdir django_tailwind_example && cd django_tailwind_example
$ python3 -V
Python 3.12.4

# create virtualenv and activate it
$ python3 -m venv venv
$ source venv/bin/activate

You can also use other tools such as Poetry or Pipenv

Create requirements.txt

Django==5.1.5
(venv)$ pip install -r requirements.txt
(venv)$ django-admin startproject django_tailwind_app .

The last . in the command means creating the Django project in the current directory.

You will see structure like this

.
├── django_tailwind_app
├── venv
├── manage.py
└── requirements.txt

Now, let's get the project running on local env.

# create db tables
(venv)$ python manage.py migrate
(venv)$ python manage.py runserver

Check on http://127.0.0.1:8000/, and you should be able to see the Django welcome page

If it is working, press Ctrl + C to terminate the server.

Install python-webpack-boilerplate

Next, we will add modern frontend tooling to the Django project, so we can use the latest frontend technologies in a bit.

python-webpack-boilerplate can help you jump start a frontend project that is bundled by Webpack

You can see python-webpack-boilerplate as a bridge to the modern frontend world.

After setting it up, we can import Tailwind CSS and other modern frontend libraries via npm install without adding CDN links to the Django template, and I will show you how to do it in a bit.

Add python-webpack-boilerplate to the requirements.txt

python-webpack-boilerplate==1.0.3  # new

And then install the package

(venv)$ pip install -r requirements.txt

Update django_tailwind_app/settings.py to add 'webpack_boilerplate' to INSTALLED_APPS

INSTALLED_APPS = [
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',

    'webpack_boilerplate',               # new
]

Let's run Django command to create frontend project from the python-webpack-boilerplate

$ python manage.py webpack_init

[1/2] project_slug (frontend): frontend
[2/2] run_npm_command_at_root (y):
[SUCCESS]: Frontend app 'frontend' has been created.

Here run_npm_command_at_root is y so we can run npm command directly at the root of the Django project

If we check the project structure, we will see something like this

.
├── db.sqlite3
├── frontend
├── django_tailwind_app
├── manage.py
├── package-lock.json
├── package.json
├── postcss.config.js
├── requirements.txt
└── venv

Notes:

  1. A new frontend directory is created which contains predefined files for our frontend project.
  2. package.json and some other config files are placed at the root directory.

Run Frontend Project

If you have no nodejs installed, please install it first by using below links

  1. On nodejs homepage
  2. Using nvm or fnm I recommend this way.
$ node -v
v20.18.2
$ npm -v
10.8.2
# install frontend dependency packages
$ npm install

# launch webpack dev server
$ npm run start

If the command run without error, that means the setup works, let's terminate the npm run start by pressing Ctrl + C

That is it, now a modern frontend tooling has been added to the Django project.

Drop SCSS support

In python-webpack-boilerplate==1.0.3, the default style solution is the classic SCSS, which can works well with Tailwind CSS 3.

However, in Tailwind V4, it is not supposed to work well with SCSS anymore.

Tailwind CSS v4.0 is a full-featured CSS build tool designed for a specific workflow, and is not designed to be used with CSS preprocessors like Sass, Less, or Stylus.

So let's drop SCSS support and use PostCSS instead.

Remove `"sass-loader" from below webpack config files:

  1. frontend/webpack/webpack.config.dev.js
  2. frontend/webpack/webpack.config.prod.js
  3. frontend/webpack/webpack.config.watch.js

Update .stylelintrc.json

{
  "extends": "stylelint-config-standard",
  "rules": {
    "at-rule-no-unknown": null
  }
}

Tailwind has some custom directive so at-rule-no-unknown is set to null to avoid linting error.

Now our frontend project will use PostCSS to process CSS files and we are ready to install Tailwind CSS 4.

Install Tailwind

We will install Tailwind CSS 4 using PostCSS, according to Tailwind CSS official documentation.

In the root directory, run command below

# install packages
$ npm install tailwindcss @tailwindcss/postcss postcss

You should see something like this in the package.json

"@tailwindcss/postcss": "^4.0.3",
"postcss": "^8.5.1",
"tailwindcss": "^4.0.3"

Next, let's edit postcss.config.js

module.exports = {
  plugins: {
    "@tailwindcss/postcss": {},
  }
}

Write Tailwind CSS

Rename frontend/src/styles/index.scss to frontend/src/styles/index.css, please note the file extension is changed from .scss to .css

@import "tailwindcss";

.jumbotron {
  // should be relative path of the entry scss file
  background-image: url("../../vendors/images/sample.jpg");
  background-size: cover;
}

@layer components{
  .btn-blue {
    @apply inline-flex items-center;
    @apply px-4 py-2;
    @apply font-semibold rounded-lg shadow-md;
    @apply text-white bg-blue-500;
    @apply hover:bg-blue-700 focus:outline-none focus:ring-2 focus:ring-blue-400/50;
  }
}

Notes:

  1. We use @import to import Tailwind CSS at the top
  2. We use classic CSS syntax to write style for jumbotron class
  3. We use @apply to apply Tailwind CSS utility classes to the btn-blue class

Update frontend/src/application/app.js

// This is the scss entry file
import "../styles/index.css";                    // update to index.css

// We can import other JS file as we like
import Jumbotron from "../components/jumbotron";

window.document.addEventListener("DOMContentLoaded", function () {
  window.console.log("dom ready");

  // Find elements and initialize
  for (const elem of document.querySelectorAll(Jumbotron.selector())) {
    new Jumbotron(elem);
  }
});

Let's test again.

$ npm run start

# webpack 5.89.0 compiled successfully

Now the tailwindcss can be compiled successfully, let's test in the Django template and see if it works.

Note:

You might see the Webpack keeps recompiling, I will talk about it later.

Test in Django Template

Add code below to django_tailwind_app/settings.py

STATICFILES_DIRS = [
    str(BASE_DIR / "frontend/build"),
]

WEBPACK_LOADER = {
    'MANIFEST_FILE': str(BASE_DIR / "frontend/build/manifest.json"),
}
  1. We add the above frontend/build to STATICFILES_DIRS so Django can find the static assets built by our Webpack (img, font and others)
  2. We add MANIFEST_FILE location to the WEBPACK_LOADER so our custom loader can help us load the JS and CSS.

Update django_tailwind_app/urls.py

from django.contrib import admin
from django.urls import path
from django.views.generic import TemplateView             # new

urlpatterns = [
    path('', TemplateView.as_view(template_name="index.html")),     # new
    path('admin/', admin.site.urls),
]

Create a folder for templates

$ mkdir django_tailwind_app/templates

├── django_tailwind_app
│   ├── __init__.py
│   ├── asgi.py
│   ├── settings.py
│   ├── templates               # new
│   ├── urls.py
│   └── wsgi.py

Update TEMPLATES in django_tailwind_app/settings.py, so Django can know where to find the templates

TEMPLATES = [
    {
        'BACKEND': 'django.template.backends.django.DjangoTemplates',
        'DIRS': ['django_tailwind_app/templates'],                             # new
        'APP_DIRS': True,
        'OPTIONS': {
            'context_processors': [
                'django.template.context_processors.debug',
                'django.template.context_processors.request',
                'django.contrib.auth.context_processors.auth',
                'django.contrib.messages.context_processors.messages',
            ],
        },
    },
]

Add index.html to the above django_tailwind_app/templates

{% load webpack_loader static %}

<!DOCTYPE html>
<html>
<head>
  <title>Index</title>
  <meta charset="utf-8" />
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  {% stylesheet_pack 'app' %}
</head>
<body>

<div class="jumbotron py-5">
  <div class="w-full max-w-7xl mx-auto px-4">
      <h1 class="text-4xl mb-4">Hello, world!</h1>
      <p class="mb-4">This is a template for a simple marketing or informational website. It includes a large callout called a
        jumbotron and three supporting pieces of content. Use it as a starting point to create something more unique.</p>

      <p><a class="btn-blue mb-4" href="#" role="button">Learn more »</a></p>

      <div class="flex justify-center">
        <img src="{% static 'vendors/images/webpack.png' %}"/>
      </div>
  </div>
</div>

{% javascript_pack 'app' %}

</body>
</html>
  1. We load webpack_loader at the top of the template, which comes from the python-webpack-boilerplate
  2. We can still use Django static template tag to import images from the frontend project.
  3. We use stylesheet_pack and javascript_pack to load CSS and JS bundle files to Django
# please make sure 'npm run start' is still running
(venv)$ python manage.py migrate
(venv)$ python manage.py runserver

Now check on http://127.0.0.1:8000/ and you should be able to see a welcome page.

Here we can notice:

  1. The button css btn-blue is working.
  2. Some styles in the Django templates such as w-full max-w-7xl mx-auto px-4 is not working.

How Tailwind Know Which CSS Classes Are Used

If you check the css files built by Tailwind, you will find that not all css classes are included.

Tailwind CSS works by scanning all of your HTML, JavaScript components, and any other template files for class names, then generating all of the corresponding CSS for those styles.

In old version of Tailwind, we can config content of tailwind.config.js to tell Tailwind search css classes in which files.

In Tailwind V4, we do not need to do this anymore, it can AUTO scan all project files in the project directory, so in the above example, the css classes in the Django templates can be detected and compiled without any configuration.

However, it also has side effect, which is the Webpack keeps recompiling.

Let's fix this issue.

Then update 'frontend/src/styles/index.css'

/*import tailwindcss and disable automatic source detection*/
@import "tailwindcss" source(none);

/*register frontend directory*/
@source "../";

/*register django templates*/
@source "../../../django_tailwind_app/**/*.html";

.jumbotron {
    /*should be relative path of the entry css file*/
    background-image: url("../../vendors/images/sample.jpg");
    background-size: cover;
}

@layer components{
    .btn-blue {
        @apply inline-flex items-center;
        @apply px-4 py-2;
        @apply font-semibold rounded-lg shadow-md;
        @apply text-white bg-blue-500;
        @apply hover:bg-blue-700 focus:outline-none focus:ring-2 focus:ring-blue-400/50;
    }
}

Notes:

  1. We add source(none) to disable automatic source detection, so Tailwind CSS will not scan all files in the project directory.
  2. We use @source to explicitly register the frontend directory and Django templates directory, so Tailwind CSS can scan the files in these directories.

Restart the webpack and try.

Now the css classes in Django templates can be detected and compiled, and the Webpack will not keep recompiling, it will only compile when the code changes.

Setup Live Reload

With webpack-dev-server, we can use it to auto reload the web page when the code of the project changes.

Update frontend/webpack/webpack.config.dev.js

devServer: {
  // add this
  watchFiles: [
    Path.join(__dirname, '../../django_tailwind_app/**/*.py'),
    Path.join(__dirname, '../../django_tailwind_app/**/*.html'),
  ],
},

Let's restart webpack dev server.

$ npm run start
  1. Here we tell webpack-dev-server to watch all .py and .html files under the django_tailwind_app directory.
  2. Now if we change code in the editor, the web page will auto reload automatically, which is awesome!

More details can be found on Python Webpack Boilerplate Doc

Conclusion

When importing frontend technologies into Django, I recommend utilizing frontend tooling and using npm to install the packages.

While using the Django-XXX package may seem easy initially, things can become more complex as the project grows, particularly when attempting to merge multiple frontend technologies or perform major upgrades.

Launch Products Faster with Django

Unlock the power of Django combined with Hotwire through SaaS Hammer. Supercharge productivity, tap into Python's rich ecosystem, and focus on perfecting your product!

Michael Yin

Michael Yin

Michael is a Full Stack Developer who loves writing code, tutorials about Django, and modern frontend techs.

He has published some tech course on testdriven.io and ebooks on leanpub.

© 2025 SaaS Hammer