# django-crispy-forms: DRY form rendering without HTML in templates

> django-crispy-forms renders Django forms from programmatic layouts instead of hand-written template markup. It is a rendering layer, not a validator, and the template pack you pick determines the markup you get.

**django-crispy-forms/django-crispy-forms** — The best way to have DRY Django forms. The app provides a tag and filter that lets you quickly render forms in a div format while providing an enormous amount of capability to configure and control the rendered HTML.

- Repository: https://github.com/django-crispy-forms/django-crispy-forms
- Website: http://django-crispy-forms.rtfd.org
- Stars: 5,153 · Forks: 730
- Language: Python
- License: MIT
- Published: 2026-09-23 · Updated: 2026-09-23 · Language: en
- Canonical page: https://hysenlabs.com/projects/django-crispy-forms-django-crispy-forms

## What django-crispy-forms actually removes from your templates

Django already gives you three ways to dump a form into a page: as_table, as_ul and as_p. They are one-liners and they are also dead ends. The output is fixed, and the moment a designer wants a field wrapped in a specific div, or two fields on one row, you start writing {{ form.field }} by hand, plus the label, plus the error block, plus the help text, and you repeat that for every field of every form. The README describes the |crispy filter as the equivalent of those built-in methods, with the same trade-off: "You cannot tune up the output, but it is easy to start using it." The {% crispy %} tag is the other half, and it is where the project earns its place: you describe the form's structure once in Python and the tag renders it.

The audience is Django developers who have already chosen a CSS framework and are tired of maintaining form markup. The README lists Twitter Bootstrap (versions 2, 3 and 4), tailwind, Bulma and Foundation as supported frontends, and notes that you can write your own pack for an in-house design system. That last point is the honest measure of scope. django-crispy-forms does not style anything. It decides which classes and wrappers appear around your fields so that your stylesheet has something to attach to.

## The filter, the tag, and the template pack that sits between them

There are two entry points and one setting connecting them to markup. The filter is a template-level shortcut: you load the library and pipe a form through it. The tag reads a FormHelper attached to the form, walks the Layout you defined, and renders each field through a template pack. CRISPY_TEMPLATE_PACK selects that pack by name, which is why the README can say you "can easily switch among them" without touching form code.

A Layout is a tree of components. Field, Row, Column, Div and HTML are the building blocks named in the documentation; nesting them is how you express a two-column row or a fieldset. Because the layout lives on the form class or is built in the view, it is testable Python rather than template text. The trade-off is that the layout is a second representation of the form, and it can drift from the fields it references. A Field naming a field that no longer exists fails at render time, not at import time.

Template pack resolution is the part that catches people. The core package does not bundle Bootstrap 5. The README says plainly: "Looking for Bootstrap 5 support? See the crispy-bootstrap5 package." So the version of Bootstrap in your project, the pack you install, and the value of CRISPY_TEMPLATE_PACK are three separate decisions that must agree.

## Installing django-crispy-forms and rendering a first form

The README gives no install commands of its own. The development path in the repository's Makefile is the closest thing to a recipe: the develop target installs requirements.txt and then installs the package itself in editable mode, and the test target runs pytest against tests.test_settings.

```bash
pip install -q -r requirements.txt
pip install -q -e .
```

```bash
DJANGO_SETTINGS_MODULE=tests.test_settings py.test tests --cov=crispy_forms
```

For an application rather than a checkout, the README points at the documentation for installation and for moving from django-uni-form. What the repository does fix is the import name and the app label: the package directory at the top level is crispy_forms, and pyproject.toml names the distribution django-crispy-forms with a single runtime dependency, django>=5.2. Add the app to INSTALLED_APPS under that import name.

Once installed, the README describes two ways in. The |crispy filter renders div-based forms and needs no configuration. The {% crispy %} tag renders a form according to a FormHelper and a Layout you define, which is where the control lives. The README points at a gist for a worked example of the tag's output rather than reproducing the code, so treat the docs as the place to copy a first layout from. The one setting the README names by hand is CRISPY_TEMPLATE_PACK, used to switch between frontend packs; it does not state a default, so confirm the value against the docs for the pack you install.

## Where django-crispy-forms stops: validation, JavaScript and version drift

This is a rendering library. It does not validate anything. Validation stays in Django's form and field classes, and the README's claim that it does not break "the standard way of doing things in Django" means exactly that: crispy forms are still forms, and is_valid() behaves as it always did. If your problem is server-side validation, this package is not the answer, and no amount of Layout configuration changes that.

Version compatibility is a real constraint rather than a footnote. The README states that django-crispy-forms supports Django 5.2+ with Python 3.10+, and pyproject.toml requires django>=5.2 and requires-python>=3.10, with classifiers for Django 5.2, 6.0 and 6.1. A project pinned to an older Django cannot use current releases. The MIT licence places no restriction on that upgrade decision, but the dependency floor does.

Frontend version drift is the more common failure. Bootstrap 5 support lives in a separate package, and the README's supported list for the core project is Bootstrap 2, 3 and 4. If you upgrade Bootstrap in your project and leave CRISPY_TEMPLATE_PACK pointing at the old pack, nothing raises an error. You get class names your new stylesheet does not define. The same silence applies to a Field that names a renamed form field: the failure surfaces when the page renders.

## How django-crispy-forms compares with plain Django form rendering

The alternative most teams weigh is not another library. It is Django's own rendering plus a hand-written partial. With {{ form.field }}, {{ field.label_tag }}, {{ field.errors }} and {{ field.help_text }} you can produce any markup you want, and you pay for that freedom once per field per form. For a project with three forms, that is a reasonable bill. For a project with thirty forms that all share a field wrapper, it is thirty places to update when the wrapper changes.

The difference is where the structure lives. Crispy forms put it in a Layout object in Python; the hand-written approach puts it in template files. Python gives you loops, conditionals and reuse across forms; templates give you designer-editable markup and no abstraction to learn. There is a second alternative worth naming: a form-rendering helper you write yourself, which is the same idea with none of the component vocabulary. django-crispy-forms is worth adopting when you want that vocabulary already built and already mapped to a CSS framework you use. It is not worth adopting to save a few lines in a small project.

## Maintenance, releases and what the MIT licence covers

The repository is not archived. The last push was on 2026-07-29, and release 2.7 carries the same date, with 2.6 on 2026-03-01 and 2.5 on 2025-11-06. The pyproject.toml classifier is "Development Status :: 5 - Production/Stable", and Django 5.2, 6.0 and 6.1 classifiers indicate the maintainers track Django's release cycle rather than pinning to one version.

Upgrade cost has two parts. The package itself is a pip install away, and the Makefile shows the development path: pip install -q -r requirements.txt followed by pip install -q -e ., with tests run through pytest against tests.test_settings. The part that costs time is the template pack. A major Bootstrap jump means a different pack package and a different CRISPY_TEMPLATE_PACK value, and any custom pack you wrote against the old markup conventions needs revisiting.

The licence is MIT, declared in pyproject.toml as license = "MIT" with license-files = ["LICENSE.txt"]. That is permissive and imposes no copyleft obligation on your application. It says nothing about the licence of the frontend framework whose CSS you are targeting, which is a separate question your project already answers.

## Conclusion

Adopt django-crispy-forms if your project already ships a CSS framework and you want form markup generated from Python rather than copied into templates; the {% crispy %} tag with a Layout object is the reason to be here, and the |crispy filter is a reasonable first step. Skip it if you have no framework CSS to target, or if your forms are so few and so unusual that a hand-written template is cheaper than learning Layout, Field and the template pack resolution rules. Before committing, check the CRISPY_TEMPLATE_PACK setting against the pack you actually install, confirm the pack supports your Django version, and read the docs page for the pack rather than assuming the core package ships it.

## FAQ

### What is django-crispy-forms?

It is a Django application that renders forms from programmatic layouts instead of hand-written template markup. It provides a |crispy filter for quick div-based output and a {% crispy %} tag that renders a form according to a Layout you define in Python.

### How do I install django-crispy-forms?

The README points at the documentation for installation and for moving from django-uni-form, and the repository's Makefile installs the package in editable mode with pip install -q -e . after requirements.txt. The distribution is named django-crispy-forms and the import name is crispy_forms.

### How do I use django-crispy-forms in a template?

Load the tag library, then either pipe the form through the |crispy filter for default div output or pass it to the {% crispy %} tag to render a Layout. The tag is the one that reads a FormHelper attached to the form.

### Is django-crispy-forms an alternative to Bootstrap?

No. It targets a CSS framework rather than replacing one, and the README lists Bootstrap 2, 3 and 4, tailwind, Bulma and Foundation as supported frontends. Bootstrap 5 support is in a separate package, crispy-bootstrap5.

### What can I use instead of django-crispy-forms?

Django's built-in as_table, as_ul and as_p methods, or hand-written field markup in your templates. The difference is where structure lives: crispy forms put it in a Python Layout, while the alternatives put it in template files.

## Sources

- [django-crispy-forms/django-crispy-forms on GitHub](https://github.com/django-crispy-forms/django-crispy-forms)
- [License: MIT](https://github.com/django-crispy-forms/django-crispy-forms/blob/main/LICENSE)
- [Project website](http://django-crispy-forms.rtfd.org)
- [README](https://github.com/django-crispy-forms/django-crispy-forms/blob/main/README.md)
- [Releases](https://github.com/django-crispy-forms/django-crispy-forms/releases)

---

Hysen Labs editorial analysis, written from the project's own repository and release notes. Cite the canonical page: https://hysenlabs.com/projects/django-crispy-forms-django-crispy-forms
