Introduction
django-crispy-forms is a popular third-party library that lets you control form rendering through Python code rather than template markup. It supports Bootstrap, Tailwind CSS, and other CSS frameworks out of the box.
Key Concepts
- FormHelper: A class that controls the form's layout, CSS classes, and submit button.
- Layout: Defines the visual structure of form fields using Python objects.
- Template pack: A set of templates for a specific CSS framework (bootstrap5, tailwind, etc.).
Real World Context
Most Django projects use a CSS framework. Without crispy-forms, you end up writing repetitive template code to wrap each field in framework-specific div classes. Crispy-forms lets you define this once in Python and reuse it everywhere.
Deep Dive
Installation and Setup
Install the library and a template pack:
bashpip install django-crispy-forms crispy-bootstrap5
Configure in settings:
python# settings.py INSTALLED_APPS = [ # ... 'crispy_forms', 'crispy_bootstrap5', ] CRISPY_ALLOWED_TEMPLATE_PACKS = 'bootstrap5' CRISPY_TEMPLATE_PACK = 'bootstrap5'
Basic Usage with the Template Tag
The simplest approach uses the crispy template filter:
html{% load crispy_forms_tags %} <form method="post"> {% csrf_token %} {{ form|crispy }} <button type="submit" class="btn btn-primary">Submit</button> </form>
This renders every field with proper Bootstrap 5 classes automatically.
Using FormHelper for Layout Control
For more control, attach a FormHelper to your form:
pythonfrom crispy_forms.helper import FormHelper from crispy_forms.layout import Layout, Submit, Row, Column, Field class ContactForm(forms.Form): name = forms.CharField(max_length=100) email = forms.EmailField() phone = forms.CharField(required=False) message = forms.CharField(widget=forms.Textarea) def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) self.helper = FormHelper() self.helper.form_method = 'post' self.helper.form_class = 'form-horizontal' self.helper.layout = Layout( Row( Column('name', css_class='col-md-6'), Column('email', css_class='col-md-6'), ), 'phone', Field('message', rows=5), Submit('submit', 'Send Message', css_class='btn-primary mt-3'), )
Render in the template with the crispy tag:
html{% load crispy_forms_tags %} {% crispy form %}
The helper controls the entire form including the submit button, so no extra template markup is needed.
Tailwind CSS with Crispy
For Tailwind, use the crispy-tailwind package:
bashpip install crispy-tailwind
python# settings.py INSTALLED_APPS += ['crispy_tailwind'] CRISPY_ALLOWED_TEMPLATE_PACKS = 'tailwind' CRISPY_TEMPLATE_PACK = 'tailwind'
The form renders with Tailwind utility classes automatically.
Common Pitfalls
- Missing template pack — Installing django-crispy-forms without a template pack (like crispy-bootstrap5) causes a TemplateDoesNotExist error. Always install both.
- Mixing crispy with manual rendering — If you use FormHelper with a submit button, the template tag renders the full form including <form> tags. Don't wrap it in another <form> tag.
Best Practices
- Use FormHelper for complex layouts — Row/Column layout objects replace complex template markup with clean Python code.
- Set form_tag = False for partial forms — When embedding a crispy form inside an existing form tag, set self.helper.form_tag = False to prevent nested form elements.
Summary
- django-crispy-forms renders forms with CSS framework classes automatically.
- Use the |crispy filter for quick rendering or FormHelper for full layout control.
- Install a template pack matching your CSS framework (bootstrap5, tailwind).
- Layout objects like Row, Column, and Field give you grid control from Python.