Content Index
- Why Tailwind CSS has become so popular (and why I didn't use it in Django)
- What Tailwind CSS really is and how it works
- What is Tailwind CSS?
- Is Tailwind CSS a component framework?
- The bad side of Tailwind CSS
- The real problem between Tailwind CSS and Django
- Why using Tailwind via CDN is a bad idea in Django
- Real options for using Tailwind CSS in Django 6
- Using Tailwind CSS with Node and npm (classic approach)
- django-tailwind: traditional integration with Node
- django-tailwind-cli: Tailwind without Node in Django
- When to use django-tailwind and when to use django-tailwind-cli
- The technical problem between Tailwind CSS and Django: more detail
- How to avoid this problem?
- django-tailwind: the classic solution
- Extra: Tailwind CSS Typography
- Production configuration and CSS purging
- Useful plugins: Tailwind Typography and DaisyUI
- Is it worth using Tailwind CSS in Django 6?
- Conclusion
- Quick FAQs
Integrating Tailwind CSS into Django 6 is one of those tasks that seem simple… until you start preparing the project for production.
For a long time I opted for Bootstrap without thinking too much: it worked out of the box, came ready to use, and Django didn't complain. But when you need total control over the design, Bootstrap starts getting in the way more than helping.
That is where Tailwind CSS comes in. The problem isn't Tailwind itself, but how it fits —or doesn't— with the Django ecosystem.
In this article I explain how to use Tailwind CSS in Django 6 correctly, what errors to avoid, and what real options exist today, including a modern Node.js-free alternative that changes the landscape quite a bit.
After covering custom authentication, the natural next step is to incorporate a CSS framework that allows us to lay out the interface efficiently. Although I usually use Bootstrap, this time we are going to use Tailwind CSS, which is ideal for bringing life to our custom authentication backend system in Django.
Why Tailwind CSS has become so popular (and why I didn't use it in Django)
If you don't know Tailwind CSS, you've probably been away from the frontend ecosystem for a while .
Basically, Tailwind is not a component framework like Bootstrap, but a utility system: small, predefined classes that apply styles directly in the HTML, without needing to jump to the CSS file.
Instead of writing traditional CSS, you build the interface by combining classes like:
mt-6→ level 6 top marginp-2→ level 2 general paddingtext-xl,bg-gray-900,rounded-xl, etc.
In my experience, this approach makes design:
- Faster to prototype
- More consistent
- Much more flexible
But for years I avoided using Tailwind with Django. The reason? The workflow didn't fit well, and in production it was a headache.
What Tailwind CSS really is and how it works
Tailwind CSS works by generating a massive set of utility classes covering everything you might need: colors, margins, typography, shadows, opacities, layouts, etc.
That carries an important consequence:
If you use Tailwind via CDN, you bring in all the CSS, even what you don't use.
And that, in production, is unacceptable.
In real projects, Tailwind must:
- Analyze your templates
- Detect which classes you actually use
- Purge (remove) everything that is unused
- Generate a small, optimized final CSS file
That is where the problems with Django start.
What is Tailwind CSS?
In my own words: Tailwind CSS is a huge collection of utility classes with which you can build any interface directly from HTML, without writing custom CSS. Everything you can do with traditional CSS —vary text, size, color, background, margins, spacing, and more— is available in Tailwind through classes with predefined values.
More than arbitrary classes, they are rules with a predefined scale. For example, if you want to apply a top margin, you don't define an exact value in pixels, but rather use classes like mt-1, mt-4, or mt-6, which correspond to values in rem (the unit Tailwind handles internally). If you need a very specific value outside the scale, Tailwind v3+ allows arbitrary values with the syntax mt-[16px].
Here you can see some examples with margins and paddings:
mt-6 → level 6 top margin
p-2 → level 2 general padding
<div class="mt-6 ...">mt-6</div>
<div class="mr-4 ...">mr-4</div>
<div class="pt-6 ...">pt-6</div>
<div class="pr-4 ...">pr-4</div>It is a kind of standardized "CSS vocabulary" that makes code much more modular and predictable.
Is Tailwind CSS a component framework?
No. Tailwind CSS is not a component-based framework like Bootstrap or Bulma. You have to build components yourself by combining utility classes.
For example, this exact screen you are looking at is built with Tailwind CSS: colors, typography, spacing… all defined with classes. It may be more or less pretty, but that level of control is precisely what makes it so attractive.
Bootstrap, on the other hand, gives you a button, card, or navbar already defined, and if you want to change them you have to override styles or modify SASS variables. With Tailwind you don't have that issue: since everything is simple classes, you are the one defining the component from scratch.
That said, if you search online for "Tailwind CSS components" you'll find a massive amount of ready-to-copy-and-adapt examples (cards, avatars, navbars, etc.) from projects like Flowbite or DaisyUI.
The bad side of Tailwind CSS
The main criticism Tailwind receives is that it litters the HTML. And it's right: when you start accumulating classes, the markup can become verbose:
<div class="flex flex-col items-center p rounded-2xl">
<div>
<img class="size-48 shadow-xl" alt="" src="/img/cover.png" />
</div>
<div class="flex">
<span>Class Warfare</span>
<span>The Anti-Patterns</span>
<span class="flex">
<span>No. 4</span>
<span>·</span>
<span>2025</span>
</span>
</div>
</div>It's ugly at first, yes. What I usually do to mitigate it is extract those repeated class combinations into a custom class using the @apply directive in CSS:
/* styles.css */
.card {
@apply flex flex-col items-center rounded-2xl p-4 shadow-xl;
}That way you keep the HTML clean and Tailwind continues to generate optimized CSS for you. We will look at it in more detail in upcoming lessons.
The real problem between Tailwind CSS and Django
Django does not include any frontend build environment out of the box.
There is no Node, no npm, no Vite, nothing similar to what Laravel offers with its native Vite integration.
And Tailwind does require a build process if you want to use it properly in production.
That is why, for a long time, typical integration involved:
- Installing Node.js manually
- Configuring
npm - Compiling CSS separately
- Syncing that with
collectstatic
It works, yes. But it is unnecessary friction, especially if you come to Django looking for simplicity and a clean stack.
Why using Tailwind via CDN is a bad idea in Django
This is important to state clearly: using Tailwind via CDN in a real Django project is bad practice.
Why?
- You load a huge CSS file (several megabytes in older versions, or the full JIT engine in v3+)
- The browser compiles Tailwind in real time, adding latency
- There is no purging: all classes are included, used or not
- External dependency: if the CDN fails, your interface breaks
- Worse overall performance: impacts Core Web Vitals metrics
For a quick prototype or proof of concept it might pass, but it is not a valid option for production.
Real options for using Tailwind CSS in Django 6
Today we have three real approaches, each with its advantages and costs.
Using Tailwind CSS with Node and npm (classic approach)
It is the most documented approach on the internet:
- You install Node.js
- You configure Tailwind with
tailwind.config.js - You compile CSS with
npmscripts - You serve the resulting static file from Django
Advantages:
- Full control over the build process
- Access to the full Tailwind plugin ecosystem
Disadvantages:
- Adds Node to a project that didn't need it
- Greater setup and deployment complexity
- More failure points in CI/CD environments
This approach appears often in "production-grade" articles, but it is not the most elegant for a pure Django project.
django-tailwind: traditional integration with Node
Here enters django-tailwind, a fairly popular library that simplifies the previous process considerably by wrapping it inside manage.py commands.
The good:
- Integrates Tailwind directly into the Django project structure
- Allows purging CSS in the build process
- More Django-friendly workflow with commands like
python manage.py tailwind start
The bad (from my experience):
- Still depends on Node.js and
npm - If Node is not properly configured on the system, everything breaks
- Not always ideal in simple, educational, or Node-less server environments
For a long time, this was "the solution." But today it is no longer the only one.
django-tailwind-cli: Tailwind without Node in Django
This is where things get interesting.
django-tailwind-cli uses the standalone Tailwind CSS CLI, an independent binary that doesn't require npm, webpack, or Node.js. The package itself downloads it automatically the first time you use it.
And this completely changes the game.
Key features:
- ❌ Does not require Node.js
- ⚡ Setup in minutes with a single command
- Integrated hot reload during development
- Optimized build with automatic purging for production
- Support for DaisyUI as an optional plugin
- Designed specifically for the Django ecosystem
In modern Django 6 projects, this is my default recommendation if you don't want to bring in Node just for Tailwind.
When to use django-tailwind and when to use django-tailwind-cli
My practical criteria is this:
- Use
django-tailwind-cliif:- You don't want to rely on Node.js
- Your frontend lives entirely in Django templates
- You look for simplicity, speed of setup, and good performance
- Use
django-tailwindif:- You already use Node in the project for other reasons
- You have a more complex frontend tooling (Vite, Webpack, etc.)
- You need to share Tailwind configuration with other frameworks in the same repo
There are no dogmas. There is context.
The technical problem between Tailwind CSS and Django: more detail
Unlike Bootstrap, Tailwind CSS does not get along "out of the box" with frameworks like Django, and here I explain why in more detail:
Tailwind CSS defines in its stylesheet all possible combinations of classes: box-shadow, opacity, color variants, responsive breakpoints, hover/focus states… everything. If you use the CDN just as you do with Bootstrap, all those classes will be loaded in the browser, whether you use most of them or not. In recent versions, Tailwind's CDN uses an in-browser JIT engine, but that carries its own runtime cost.
And that is extremely inefficient for production.
How to avoid this problem?
The solution is to use build tools —like Node.js with npm, or Vite— to scan your project's files, identify which Tailwind classes you actually use, and generate a final CSS containing only those classes. This process is known as purging.
In Django, we don't have that natively. Unlike Laravel, which includes Vite by default and has the entire JavaScript build environment ready from day one, in Django you have to set up everything manually… or use a package that abstracts it.
That is why I normally don't recommend Tailwind with Django without a specific solution. But we have something special that we'll see in the next section.
django-tailwind: the classic solution
There is a package called django-tailwind that allows integrating Tailwind CSS in Django more efficiently than the CDN. You can easily find it by searching "Django Tailwind" on Google, and its official documentation covers the installation steps in detail.
The relevant part is that it allows purging the CSS during the build phase for production.
Let's see the steps to integrate it. First, we install the package:
$ pip install django-tailwindThis will download the package and its dependencies:
Collecting django-tailwind
Downloading django_tailwind-4.2.0-py3-none-any.whl.metadata (4.4 kB)
Requirement already satisfied: django>=4.2.20 in c:\users\andre\desktop\proy\django\curso\.venv\lib\site-packages (from django-tailwind) (5.2.4)
Requirement already satisfied: asgiref>=3.8.1 in c:\users\andre\desktop\proy\django\curso\.venv\lib\site-packages (from django>=4.2.20->django-tailwind) (3.9.1)
Requirement already satisfied: sqlparse>=0.3.1 in c:\users\andre\desktop\proy\django\curso\.venv\lib\site-packages (from django>=4.2.20->django-tailwind) (0.5.3)
Requirement already satisfied: tzdata in c:\users\andre\desktop\proy\django\curso\.venv\lib\site-packages (from django>=4.2.20->django-tailwind) (2025.2)
Downloading django_tailwind-4.2.0-py3-none-any.whl (19 kB)We register tailwind as an installed application in settings.py:
djangoshopping/djangoshopping/settings.py
INSTALLED_APPS = [
...
'tailwind',
]We initialize the Tailwind project inside Django:
$ python manage.py tailwind initThis command downloads some additional dependencies like cookiecutter (used to generate the Tailwind app from a template):
Collecting cookiecutter
Downloading cookiecutter-2.6.0-py3-none-any.whl.metadata (7.3 kB)
Collecting binaryornot>=0.4.4 (from cookiecutter)
Downloading binaryornot-0.4.4-py2.py3-none-any.whl.metadata (6.0 kB)
Collecting Jinja2<4.0.0,>=2.7 (from cookiecutter)
Downloading jinja2-3.1.6-py3-none-any.whl.metadata (2.9 kB)
Collecting click<9.0.0,>=7.0 (from cookiecutter)
Downloading click-8.2.1-py3-none-any.whl.metadata (2.5 kB)And creates a dedicated Django application for Tailwind. The suggested name —and the one we will use— is theme:
[1/2] Your Tailwind app/theme name (theme): themeIt also asks if you want to install DaisyUI as a plugin. For now we select no, although you can enable it if needed:
[2/2] Include daisyUI plugin?
1 - no
2 - yes
Choose from [1/2] (1): 1
Finally, you will get output like this:
Tailwind application 'theme' has been successfully created. Please add 'theme' to INSTALLED_APPS in settings.py, then run the following command to install Tailwind CSS dependencies: `python manage.py tailwind install`
Now we register both tailwind and the generated theme app, and configure the TAILWIND_APP_NAME variable in settings.py:
djangoshopping/djangoshopping/settings.py
INSTALLED_APPS = [
...
'tailwind',
'theme',
]
TAILWIND_APP_NAME = 'theme'We install the Node dependencies that Tailwind needs internally. This step requires having npm installed:
$ python manage.py tailwind installIf you see the following error:
It looks like node.js and/or npm is not installed or cannot be found.
Visit https://nodejs.org to download and install node.js for your system.
If you have npm installed and still getting this error message, set NPM_BIN_PATH variable in settings.py to match path of NPM executable in your system.
It means npm (Node's package manager) is not installed or Django cannot find it. You can download it from the official website:
https://nodejs.org/en/download
In case you already have it installed but the package cannot find it —for example, if you manage it with Laragon or Laravel Herd— you must locate the exact path of the npm executable on your system and configure it in settings.py:
djangoshopping/djangoshopping/settings.py
NPM_BIN_PATH = "C:/Users/andre/.config/herd/bin/nvm/v23.11.0/npm.cmd"This path is just an example. Replace it with the one corresponding to your operating system and your Node installation.
With everything configured, we can add the Tailwind tag in our base template. This tag is what injects the link to the generated CSS:
djangoshopping/blog/templates/master.html
{% load static tailwind_tags %}
...
<head>
...
{% tailwind_css %}
...
</head>You can inspect the page source code when running the application. If everything works well, you will see that the styling changes slightly and a tag like the following will appear in the generated HTML:
<link rel="stylesheet" type="text/css" href="/static/css/dist/styles.css?v=<V>">Which is the compiled Tailwind CSS served as static inside your project.
Finally, for production, you run the following command which purges the CSS and generates an optimized file:
$ python manage.py tailwind buildThis is exactly the reason why we performed all these steps: the ultimate goal is to purge the CSS and keep only the classes we actually use in the project. The CDN option would include all un-filtered Tailwind CSS, which is unacceptable in production. With this setup you have lean, efficient CSS with no dead weight.
Extra: Tailwind CSS Typography
The @tailwindcss/typography plugin makes applying typographic styles to blocks of text enormously easier: margins between paragraphs, sizes of headings h1 through h6, list styles, blockquotes, inline code… everything that in pure CSS requires defining rule by rule.
The interesting part of showing this variant is that thanks to django-tailwind we have an actual Node project embedded inside Django, allowing us to extend Tailwind with external plugins relatively easily. To install it, we first position ourselves inside the theme app we created earlier, specifically in its Node source directory:
djangoshopping/theme/static_src
Inside that folder you'll find package.json, which is the key file for any Node project and is where additional packages must be installed. We install the plugin:
$ npm install -D @tailwindcss/typographyWe add the plugin in the main Tailwind styles file:
djangoshopping/theme/static_src/src/styles.css
@import "tailwindcss";
+ @plugin "@tailwindcss/typography";From there, you only need to add the prose class to your text's container element and the plugin takes care of all typographic styling automatically. For example:
<article class="prose prose-lg mx-auto">
{{ post.content|safe }}
</article>Production configuration and CSS purging
This is where serious projects separate from experiments.
In production you need:
- Minimal CSS: only what is used
- Active purging of unused classes
- Static files correctly served via
collectstatic - Aggressive caching to maximize performance
With django-tailwind-cli, the command:
$ python manage.py tailwind buildGenerates optimized, purged CSS ready for production. No hacks, no strange external scripts.
Then, combined with collectstatic and tools like WhiteNoise, you get a clean, efficient asset pipeline without needing an external media server.
Useful plugins: Tailwind Typography and DaisyUI
One of my favorites is @tailwindcss/typography, as shown earlier.
Defining margins, sizes, and styles for long text —blog articles, documentation, dynamically generated content— is tedious with pure CSS. With the plugin's prose class, all of that is resolved in seconds.
And if you don't want to build all your components from scratch:
- DaisyUI gives you buttons, cards, layouts, and dozens of ready-to-use components
- Without leaving Tailwind or its utility system
- Without losing control over the final design
Both plugins are compatible with both django-tailwind and django-tailwind-cli, so you can use them regardless of the approach you choose.
Is it worth using Tailwind CSS in Django 6?
Yes.
But only if you use it well.
Tailwind isn't magical:
- It can clutter HTML if you don't abstract repeated classes with
@applyor components - It has an initial cognitive cost: you have to learn its scale system
- It requires team discipline to maintain consistency
But when integrated correctly, especially with modern solutions like django-tailwind-cli, the result is:
- Minimal CSS with no dead weight
- Flexible and fully customized design
- Excellent performance in production
- Clean and reproducible workflow
In my case, I no longer rule out Tailwind in Django. I simply pick the right tool depending on the project context.
Conclusion
Using Tailwind CSS in Django 6 is not about copying commands: it is about understanding the full flow from end to end:
- Development (with watcher and hot reload)
- Build (CSS compilation and purging)
- Production (serving statics correctly)
- Performance (caching and minimal CSS)
The emergence of django-tailwind-cli removes one of the biggest historical bottlenecks: Node as a mandatory dependency. If you seek control, performance, and an elegant integration without complicating your stack, today that is the option that makes the most sense.
Quick FAQs
- Can Tailwind CSS be used in Django without Node?
- Yes, using django-tailwind-cli.
- Is it advisable to use Tailwind via CDN in Django?
- No, only for quick tests.
- Does Tailwind replace Bootstrap in Django?
- It depends on the project, but it offers much more control.
- Does Tailwind affect performance?
- Only if you use it poorly. With purging, it's extremely efficient.
Next step: learn about the N+1 Problem in Django's ORM and how to avoid it.