Since Django 4, form rendering with Tailwind can be used to render beautiful forms without using Node or any other packages like django-crispy-forms. All you need is the Tailwind CLI binary and django-widget-tweaks .

Tailwind without Node

The pytailwindcss package bundles the Tailwind CLI as a self-contained binary: not need to install Node! The downside is, that it’s not possible to use any Tailwind plugins except for the built-in ones like @tailwindcss/forms.

pip install pytailwindcss

or put

pytailwindcss==0.1.4  # https://github.com/timonweb/pytailwindcss

into your requirements.txt (you should do that anyway).

Create an entry point for Tailwind in static/css/project.css:

@tailwind base;
@tailwind components;
@tailwind utilities;

Create a Tailwind configuration file in your project root to tell Tailwind where your templates are:

/** @type {import('tailwindcss').Config} */
module.exports = {
  content: ["./<project>/**/*.{html,js}"],
  theme: {
    extend: {},
  },
  plugins: [require("@tailwindcss/forms")],
};

Whenever you run the local development server, also run the Tailwind compiler:

tailwindcss --watch -i <project>/static/css/project.css -o <project>/static/css/dist/styles.css

Include the compiled CSS as static file in your base template templates/base.html:

<link href="{% static 'css/dist/styles.css' %}" rel="stylesheet">

Custom Django 4 form rendering

In order to customize the form rendering, define and configure a custom form renderer in your settings.py:

from django.forms.renderers import TemplatesSetting

class FormRenderer(TemplatesSetting):
    form_template_name = "form_snippet.html"


FORM_RENDERER = "config.settings.base.FormRenderer"

Use django-widget-tweaks to add custom Tailwind classes to the input elements. Implement your own form rendering template form_snipper.html.

Following snippet covers the most basic input elements, feel free to implement your own.

{% load widget_tweaks %}
<div class="grid grid-cols-1 gap-6">
  {% for field in form %}
    <label class="block">
      <span class="text-gray-700">{{ field.label }}</span>
      <!-- Checkbox -->
      {% if field|field_type == 'booleanfield' %}
        {{ field|add_class:"rounded border-gray-300 text-indigo-600 shadow-sm focus:border-indigo-300 focus:ring focus:ring-offset-0 focus:ring-indigo-200 focus:ring-opacity-50" }}
        <!-- Date -->
      {% elif field|field_type == 'datefield' %}
        {{ field|add_class:"mt-1 block w-full rounded-md border-gray-300 shadow-sm focus:border-indigo-300 focus:ring focus:ring-indigo-200 focus:ring-opacity-50" }}
        <!-- Email -->
      {% elif field|field_type == 'emailfield' %}
        {{ field|add_class:"mt-1 block w-full rounded-md border-gray-300 shadow-sm focus:border-indigo-300 focus:ring focus:ring-indigo-200 focus:ring-opacity-50"|attr:"placeholder:" }}
        <!-- Password -->
      {% elif field|field_type == 'passwordfield' %}
        {{ field|add_class:"mt-1 block w-full rounded-md border-gray-300 shadow-sm focus:border-indigo-300 focus:ring focus:ring-indigo-200 focus:ring-opacity-50"|attr:"placeholder:" }}
      {% else %}
        <!-- Fallback -->
        {{ field }}
      {% endif %}
    </label>
  {% endfor %}
</div>

Tailwind is great with components. Django templates are not great with components. It’s a good idea to define custom classes for highly re-usable elements like buttons and links. Define them in static/css/project.css.

Note that if you are using Tailwind with a framework like React, you should not define your custom CSS classes but instead heavily break down your UI into components.

@tailwind base;
@tailwind components;
@tailwind utilities;

@layer components {
  .btn-link {
    @apply font-medium text-indigo-600 hover:text-indigo-500;
  }

  .btn-primary {
    @apply items-center justify-center rounded-md border border-transparent bg-indigo-600 px-4 py-2 text-base font-medium text-white shadow-sm hover:bg-indigo-700;
  }
}

Now all your {{form}}s will automatically look nice!

All your forms look good

<form class="login" method="POST" action="{% url 'account_login' %}">
  {% csrf_token %}
  {{ form }}
  {% if redirect_field_value %}
    <input type="hidden" name="{{ redirect_field_name }}" value="{{ redirect_field_value }}" />
  {% endif %}
  <a class="btn-link" href="{% url 'account_reset_password' %}">{% trans "Forgot Password?" %}</a>
  <button class="btn-primary" type="submit">{% trans "Sign In" %}</button>
</form>

A Django form styled with Tailwind

Don’t forget to run the Tailwind CLI with the --minify argument in your deployment step in order to optimize the CSS:

tailwindcss --minify -i mentionedd/static/css/project.css -o mentionedd/static/css/dist/styles.css