Introducing Django Cotton: Revolutionizing UI Composition in Django! 🚀
Goodbye {% extends, block, include, custom_tag %}?
Hello <c-component />
I’m excited to introduce Django Cotton, a game-changer for those who love Django but crave a more modern, component-based design for templates.
Here’s why I built it and what it offers:
🌟 Key Features:
- Modern UI Composition: Efficiently compose and reuse UI components.
- HTML-like Syntax: HTML-like tags for better editor support, semantic structure and expressive coding.
- Interoperable with Django: Enhances Django’s existing template system.
- Minimal Overhead: Compiles to native Django components with dynamic caching.
- Ideal for Tailwind Usage: Encapsulates content and style in one file.
- Complements HTMX: Reduces repetition and enhances maintainability.
The problem with Django’s native tags
Whilst you can build frontends with Django’s native tags, there are a few things that hold us back when we want to apply modern practices:
- Django’s {% block %} and {% extends %} system strongly couples child and parent templates. This makes it hard to create a truly re-usable component that can be used in places without it having a related base template.
- What about {% include %}? Modern libraries allow components to be highly configurable, whether it’s by attributes, passing variables, passing HTML with default and named slots. {% include %} tags, whilst they have the ability to pass simple variables and text, they will not allow you to easily send HTML blocks with template expressions let alone other niceties such as boolean attributes, named slots etc.
- We can only get so far using {% with %} tags. Whilst it allows us to provide variables and strings it quickly busies up your code and has the same limitations about passing more complex types.
- Can custom templatetags help us? They get us so far in being able to create something like a re-usable component but managing things like variable scoping, nesting, slots, dynamic attributes can quickly increase complexity. You’re also still writing that verbose syntax that comes with Django templates.
Your first component
Cotton allows you to craft your interfaces using HTML-like tags.
A component saved in cotton/button.html:
<a href="#" class="bg-blue-500 text-white">
{{ slot }}
</a>
Can be used in a view or other component like:
<c-button>Click!</c-button>
This is already covering the slot mechanism which simply outputs the content between the opening and closing tags. This means we’re already able to pass HTML to the component.
<c-button>
<svg id="pointer" ... />
Click!
</c-button>
Named slots
What if we wanted to position content in a predefined area and style within the component? Consider a ‘card’ component for example and inside the file cotton/card.html we have:
<div class="border rounded p-5">
<div class="border-b">{{ title }}</div>
{{ slot }}
</div>
We can target this content in a couple of ways, either as a title attribute:
<c-card title="Furniture">
Hand made
</c-card>
Or should the title contain HTML or require some Django template logic, then we can use a named slot:
<c-card>
<c-slot name="title">
<strong>Really</strong> awesome furniture
{% if on_sale %}
<span class="text-red-500">SALE!</span>
{% endif %}
</c-slot></c-card>
Native tags inside attributes
We are free to use {{ and {% tags inside attributes like this:
<a href="{{ url }}" class="rounded p-5">
{{ slot }}
</a>
<c-button url="{% url 'product' product.id %}">
Product title
</c-button>
Be more flexible with {{ attrs }}
{{ attrs }} will provide a string of all key=”value” items specified on the component as attributes. This is useful for increasing versatility of your components:
<a {{ attrs }} class="rounded p-5">
{{ slot }}
</a>
Then we change the implementation like:
<c-button href="{% url 'product' product.id %}" target="_blank">
Product title</c-button>
This will add the href and target attributes to the underlying element without having to specifically declare them in the component template.
An example using HTMX
<c-button
hx-get="/product-list/?page=2"
hx-target="#replaceMe"
hx-swap="outerHTML"
>Next page</c-button>
There are many more topics to cover, including:
- c-vars to define in-component variables to create default attributes
- Dynamic Attributes to pass python types and variables by reference
- Boolean Attributes to simplify boilerplate
For more, checkout the docs site + repo:
Why I Built Cotton
During ~20 years of building for the web, I’ve found that tools like Svelte, Vue.js, and Laravel’s blade components — with their expressive, semantic HTML-like syntax — coupled with utility-first styling tools like Tailwind CSS, provide an unbeatable combination for spinning up modular, reusable UIs. Recently, moving to Python and Django, I couldn’t find a similar package, so I built Cotton to fill that gap.