Skip to content

Conversation

@IanLuan
Copy link

@IanLuan IanLuan commented Jun 20, 2025

About this proposal

This is a feature proposal introducing Cotton Directives — an HTML-based syntax for control flow in templates (c-if, c-elif, c-else, c-for). Let me know if you'd prefer this to be discussed in a separate thread first.

Thanks for this great library — it's been a lot of fun to work with!

Description

This PR introduces Cotton Directives, a better way to write control flow statements (if, elif, else, for) following django_cotton’s philosophy. These directives use HTML attributes such as c-if and c-for, which can be applied to any HTML element, not limited to Django Cotton components.

Currently, writing control flow in Django templates requires verbose template tags like {% if %}, {% endif %}, which can disrupt the HTML structure and developer experience.

This feature adds a cleaner, attribute-based syntax that aligns better with the design of django_cotton, enabling more readable and maintainable templates.

Note: Cotton directives are completely optional. Developers can choose to stick with block tags, use directives, or combine both approaches. This feature simply adds a new possibility.

How It Works

Cotton Directives are implemented as special HTML attributes (c-if, c-elif, c-else, c-for) that are parsed and translated into the equivalent Django template tag during the loader phase.

When the template is processed:

  • The parser searches for elements with a directive.
  • The element is wrapped with the appropriate {% %} Django template tag.
  • The directive attribute is removed from the final HTML output to prevent duplication and avoid it being treated as a regular cotton attribute.

Example

<div c-if="user.is_authenticated">
  <h1>Welcome, {{ user.username }}</h1>
</div>

Will be rendered as:

{% if user.is_authenticated %}
<div>
  <h1>Welcome, {{ user.username }}</h1>
</div>
{% endif %}

Note: Nested directives are fully supported. You can safely nest c-if inside c-for, or even multiple c-if blocks within each other.

Example

<c-avatars>
  <c-avatar c-for="user in users">
    <c-image c-if="user.photo" :src="user.photo" />
    <c-icon c-elif="user.icon" :icon="user.icon" />
    <c-initials c-else :name="user.full_name" />
  </c-avatar>
</c-avatars>

Which renders to:

<c-avatars>
  {% for user in users %}
  <c-avatar>
    {% if user.photo %}
    <c-image :src="user.photo" />
    {% elif user.icon %}
    <c-icon :icon="user.icon" />
    {% else %}
    <c-initials :name="user.full_name" />
    {% endif %}
  </c-avatar>
  {% endfor %}
</c-avatars>

Note: Cotton directives are processed before cotton components, which means they do not affect the cotton compiler’s behavior.

Under the hood, the feature extends Python’s built-in HTMLParser to parse the template HTML string.

The parser detects elements containing the available directives, manages a stack-based structure to properly handle nested directives, and constructs the corresponding Django template tags around the matched HTML blocks.

Extensibility

This initial implementation adds Cotton Directives specifically for control flow statements such as conditionals (c-if, c-elif, c-else) and loops (c-for). However, the design is intentionally extensible.

In the future, additional Cotton Directives can be introduced easily, since any Django template block tag could potentially be represented as a Cotton Directive.

Performance

The Cotton Directives parser is blazing fast, thanks to Python’s built-in HTMLParser, which powers the feature.
In a test where 100 block tags were replaced with their respective directives (50 c-if and 50 c-for), the parser processed all 100 directives and transformed them into the corresponding Django block tags, adding only ~4ms to the final render time (measured on a notebook with an Intel i5-10210U processor).

@wrabit
Copy link
Owner

wrabit commented Jun 25, 2025

Hi @IanLuan interesting use case of the pre compiler! Very vue-like so Im not sure everyone will appreciate it but it could be complimentary. When I have time I'll take a closer look and try it out.

@IanLuan
Copy link
Author

IanLuan commented Jun 25, 2025

I’m glad you liked it @wrabit! Personally, the Vue-like style is one of my favorite things about Django Cotton, so I thought it’d be a good idea to explore it. I’m happy to answer any questions if you have them!

@twentyforty
Copy link

I really like this! Would be great to have even cleaner templates. Hopefully you'll get some time soon to review this @wrabit

@jasalt
Copy link

jasalt commented Oct 10, 2025

While browsing through tools in this area I note similarity in this and the dj-angles package, specifically the attributes features it has https://dj-angles.adamghill.com/en/latest/tag-attributes.html. It promotes being compatible with component packages such as django-bird, so would assume it could work with Django Cotton too. I didn't look at the implementations to know about possible drawbacks but thought to just link it as reference.

@IanLuan
Copy link
Author

IanLuan commented Oct 11, 2025

While browsing through tools in this area I note similarity in this and the dj-angles package, specifically the attributes features it has https://dj-angles.adamghill.com/en/latest/tag-attributes.html. It promotes being compatible with component packages such as django-bird, so would assume it could work with Django Cotton too. I didn't look at the implementations to know about possible drawbacks but thought to just link it as reference.

Hi @jasalt, thanks for sharing it, I didn’t know about that package! I still think Cotton Directives could be a better fit than using dj-angles for a few reasons:

  1. It’s "native" to Django Cotton, using the c- prefix, which makes it simpler and more consistent.
  2. dj-angles is a separate HTML transpiler library, meaning users would need to install the entire package just to use attribute features.
  3. From what I’ve seen dj-angles doesn’t support for loops, and Cotton directives can be easily extended to any template block tag in the future.

But it’s definitely a nice package, thanks again for sharing it!

@wrabit
Copy link
Owner

wrabit commented Oct 26, 2025

@IanLuan I see now there has been reasonable support for the feature which is great and providing we have some caveat around it being entirely optional I think we can merge. Docs would require updating, new page and left menu “Flow Control Directives” would suffice. Due to lack of time in this moment I would welcome a PR, if not I will get round to it eventually just not sure when (new arrival in family).

@radusuciu
Copy link

I'm considering django-cotton for a new project and find this to be a really elegant syntax. I usually reach for Vue so that definitely is part of the appeal for me. Cool PR!

@jasalt
Copy link

jasalt commented Nov 5, 2025

@radusuciu I’m also interested how this could play with clientside libraries with Vue-like features like Datastar or AlpineJS, former having these as builtins like https://alpinejs.dev/directives/for and Datastar supporting it as custom attribute plugins.

@IanLuan
Copy link
Author

IanLuan commented Nov 5, 2025

@IanLuan I see now there has been reasonable support for the feature which is great and providing we have some caveat around it being entirely optional I think we can merge. Docs would require updating, new page and left menu “Flow Control Directives” would suffice. Due to lack of time in this moment I would welcome a PR, if not I will get round to it eventually just not sure when (new arrival in family).

Congrats, man! I just had a new arrival as well, I understand you, haha. I'll try to create the documentation this weekend and push it to this PR so you can review it. Congrats again, enjoy the moment!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

6 participants