Components

Components are reusable pieces of view template. They can contain native Django template syntax and can be used inside standard Django templates. This page walks through the ideas in the order you'll meet them. The exact rules for each live in the API section.

1. The basic building block: {{ slot }}

The {{ slot }} variable captures all content passed between a component's opening and closing tags.

cotton/box.html
<div class="box">
    {{ slot }}
</div>

Used in a parent template:

my_view.html
<c-box>
    <p>Some <strong>content</strong></p>
</c-box>
{% cotton box %}
    <p>Some <strong>content</strong></p>
{% endcotton %}
preview
Some content

2. Adding Component Attributes

We can further customize components with attributes, which allow you to pass specific data into the component as key-value pairs.

cotton/weather.html
<p>It's {{ temperature }}<sup>o</sup>{{ unit }} and the condition is {{ condition }}.</p>

view.html
<c-weather temperature="23" unit=unit condition="windy"></c-weather>
{% cotton weather temperature="23" unit=unit condition="windy" /%}
preview
It's 23oC and the condition is windy.

3. Using Named Slots

If we want to pass HTML instead of just a string (or another data type) into a component, we can pass them as named slots with the <c-slot name="...">...</c-slot> syntax.

So as with normal attributes, you reference the slot content like normal variables, as in:

cotton/weather_card.html
<div class="flex ...">
    <h2>{{ day }}:</h2> {{ icon }} {{ label }}
</div>

view.html
<c-weather-card day="Tuesday">
    <c-slot name="icon">
        <img src="sunny-icon.png" alt="Sunny">
    </c-slot>

    <c-slot name="label">
        <div class="yellow">Sunny</div>
    </c-slot>
</c-weather-card>
{% cotton weather-card day="Tuesday" %}
    {% cotton:slot icon %}
        <img src="sunny-icon.png" alt="Sunny">
    {% endcotton:slot %}

    {% cotton:slot label %}
        <div class="yellow">Sunny</div>
    {% endcotton:slot %}
{% endcotton %}
preview

Tuesday:

Sunny

Reference: c-slot.

4. Dynamic Attributes

By default, every attribute you pass to a component arrives as a string. To pass anything else, a variable from context, a boolean, a number, a list or a dict, prefix the attribute name with a colon.

view.html
<!--
context = { 'today': Weather.objects.get(...) }
-->
<c-weather :today="today" />
<!--
context = { 'today': Weather.objects.get(...) }
-->
{% cotton weather :today="today" /%}

cotton/weather.html
<p>It's {{ today.temperature }}<sup>o</sup>{{ today.unit }} and the condition is {{ today.condition }}.</p>

:today="today" and today="{{ today }}" do different things. The colon form hands the variable itself to the component, so it can read today.temperature. The template expression form renders in place first, so the component receives a string.

The colon form also takes Python literals. A typical use is a select component that takes its options from the parent:

view.html
<c-select :options="['no', 'yes', 'maybe']" />
{% cotton select :options="['no', 'yes', 'maybe']" /%}
preview
Are carrots tasty?

cotton/select.html
<select>
    {% for option in options %}
        <option value="{{ option }}">{{ option }}</option>
    {% endfor %}
</select>

Reference: Attributes covers every value type, unquoted values and the :attrs dictionary.

5. Working with {{ attrs }}

Every attribute passed to a component is collected into attrs, a dictionary the component can print, add to, pass on or trim. The simplest use is to reflect everything onto an element, which is what makes form inputs practical.

form_view.html
<c-input name="first_name" placeholder="First name" />
<c-input name="last_name" placeholder="Last name" value="Smith" readonly  />
{% cotton input name="first_name" placeholder="First name" /%}
{% cotton input name="last_name" placeholder="Last name" value="Smith" readonly /%}
preview

cotton/input.html
<input type="text" {{ attrs }} />

<!-- html output
<input type="text" name="first_name" placeholder="First name" />
<input type="text" name="last_name" placeholder="Last name" value="Smith" readonly />
-->

A parent can add to attrs with a dictionary, :attrs="widget_attrs", and a wrapper component can pass its whole attrs on to the component it wraps. See attribute proxying for that pattern.

Reference: Template variables.

6. Claiming attributes with c-vars

Some attributes are for the component to use, not for its HTML. Declare those in a single <c-vars /> at the top of the component. A declared key is removed from attrs and becomes an ordinary template variable, with a default if you give one.

cotton/alert.html
<c-vars type="success" />

<div class="{% if type == 'success' %} .. {% elif type == 'danger' %} .. {% endif %}">
    {{ slot }}
</div>
{% cotton:vars type="success" %}

<div class="{% if type == 'success' %} .. {% elif type == 'danger' %} .. {% endif %}">
    {{ slot }}
</div>

form_view.html
<c-alert>All good!</c-alert>
<c-alert type="danger">Oh no!</c-alert>
{% cotton alert %}All good!{% endcotton %}
{% cotton alert type="danger" %}Oh no!{% endcotton %}
preview
All good!
Oh no!

Declaring a key with no value claims it without setting a default. Here label and errors are claimed, so they never reach the input, while placeholder is not, so it does.

cotton/input_group.html
<c-vars label errors />

<label>{{ label }}</label>

<input type="text" class="border ..." {{ attrs }} />

{% if errors %}
    {% for error in errors %}
        {{ error }}
    {% endfor %}
{% endif %}
{% cotton:vars label errors %}

<label>{{ label }}</label>

<input type="text" class="border ..." {{ attrs }} />

{% if errors %}
    {% for error in errors %}
        {{ error }}
    {% endfor %}
{% endif %}

form_view.html
<c-input-group label="First name" placeholder="First name" :errors="errors.first_name" />
<c-input-group label="Last name" placeholder="Last name" :errors="errors.last_name" />
{% cotton input-group label="First name" placeholder="First name" :errors="errors.first_name" /%}
{% cotton input-group label="Last name" placeholder="Last name" :errors="errors.last_name" /%}
preview



Last name is required

For class you usually want the parent's classes added to the component's own rather than replacing them. class:merge="btn btn-primary" does that.

Reference: c-vars covers dynamic defaults, template expressions in defaults and merging.

7. Boolean attributes

Sometimes you just want to pass a simple boolean to a component. Cotton supports providing the attribute name without a value which will provide a boolean True to the component.

cotton/input.html
<input type="text" {{ attrs }} />

{% if required is True %}
    <span class="text-red-500">*</span>
{% endif %}

form_view.html
<c-input name="telephone" required />
{% cotton input name="telephone" required /%}
preview
*

8. Dynamic Components

There can be times where components need to be included dynamically. For these cases we can reach for a special <c-component> tag with an is attribute:

cotton/icon_list.html
{% for icon in icons %}
    <c-component is="icons.{{ icon }}" />
{% endfor %}
{% for icon in icons %}
    {% cotton component is="icons.{{ icon }}" /%}
{% endfor %}

The is attribute is similar to other attributes so we have a number of possibilities to define it:

cotton/icon_list.html
<!-- as variable -->
<c-component :is="icon_name" />

<!-- as an expression -->
<c-component is="icon_{{ icon_name }}" />
<!-- as variable -->
{% cotton component :is="icon_name" /%}

<!-- as an expression -->
{% cotton component is="icon_{{ icon_name }}" /%}

Reference: Component tag, which also covers only and context isolation.

Where next

That's the whole model: a slot, attributes, named slots and <c-vars /> to say which attributes are the component's own. The tutorials build real components with it, and the API section has the precise rules for the component tag, attributes, c-vars, c-slot and the template variables, including Alpine.js support and context isolation.

View this page as markdown, handy as context for your AI tools. Full index at /llms.txt. Spotted a mistake? Edit this page on GitHub.