cotton
for

Components

Components are reusable pieces of view template. They can contain native Django template syntax and can be used inside standard Django templates.

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>
preview
Some content

2. Adding Component Attributes

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

cotton/weather.html
<p>It's {{ temperature }}<sup>{{ unit }}</sup> and the condition is {{ condition }}.</p>
<c-weather temperature="23" unit="{{ unit }}" condition="windy"></c-weather>
preview
It's 23c 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>
preview

Tuesday:

Sunny

4. Dynamic Attributes with ":"

We saw how by default, all attributes that we pass to a component are treated as strings. If we want to pass HTML, we can use named slots. But what if we want to pass another data type like a template variable, boolean, integer, float, dictionary, list, dictionary?

Passing objects from context

Sometimes you'll want to pass a variable from the parent's context 'as is' for the child component to perform what it wants.

view.html
<!--
context = { 'today': Weather.objects.get(...) }
-->
<c-weather :today="today"></c-weather>
cotton/weather.html
<p>It's {{ today.temperature }}<sup>{{ today.unit }}</sup> and the condition is {{ today.condition }}.</p>

Passing python types

Integers & Floats
<c-mycomp :prop="1" />
<!-- {% prop == 1 %} -->
None
<c-mycomp :prop="None" />
<!-- {% prop is None %} -->
Lists
<c-mycomp :items="['item1','item2','item3']" />
<!-- {% for item in items %} -->
Dicts
<c-mycomp :mydict="{'name': 'Thom', 'products': [1,2]}" />
<!-- {{ mydict.name }}, {{ mydict.products }} -->
Parent variable
<c-mycomp :product="product" />
<!-- {{ product.title }} -->
With template expressions
<c-mycomp :slides="['{{ image1 }}', '{{ image2 }}']" />
<!-- {% for images in slides %} -->
Generated with template expressions
<c-mycomp :is_highlighted="{% if important %}True{% endif %}" />
<!-- {% is_valid is False %} -->
Note: You can use the same dynamic attribute patterns in c-vars to apply dynamic defaults to your components.

A quick example of this is a select component that you want to fill with options:

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

5. Pass all attributes with {{ attrs }}

Sometimes it's useful to be able to reflect all attributes provided in the parent on to an HTML element in the component. This is particularly powerful when you are building form inputs.

form_view.html
<c-input name="first_name" placeholder="First name" />
<c-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 />
-->

6. <c-vars />: Defining Local Variables

<c-vars /> gives components local state and default behavior, making them more self-sufficient and reducing the need for repetitive attribute declarations and maintaining UI state in the backend.

Vars are defined using a <c-vars /> tag at the top of a component file. It can either contain key="value" pairs or just standalone keys (keep reading to understand why).

Use <c-vars /> to set attribute defaults

You may design a component that will often have a default behaviour and rarely needs overriding. In this case, you may opt to give a default value to your component.

cotton/alert.html
<c-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>
preview
All good!
Oh no!

<c-vars /> are excluded from {{ attrs }}

Keys defined in <c-vars /> will not be included in {{ attrs }}. This is useful when some of the properties you pass down to a component are for configuration purposes only and not intended as attributes.

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 %}
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" />
preview



Last name is required
Specifying an attribute as a 'var' will remove the item from {{ attrs }}. It can also be a single key without a default value, this is when you know a particular attribute should not end up in {{ attrs }}, whether it's defined in a parent or not.

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 />
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 %}

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 }}" />

9. Extras

Similar to Django's {% include %} tag you can add an "only" attribute which will prevent the component from inheriting the parent's context.

Summary of Concepts

  • {{ slot }} - Default content injection.
  • Attributes - Simple, straightforward customization.
  • Named Slots - Provide HTML or template partial as a variable in the component.
  • `:` Dynamic Attributes - Pass variables and other data types other than strings.
  • {{ attrs }} - Prints attributes as HTML attributes.
  • <c-vars /> - Set default values and other component state.
  • Boolean attributes - Attributes without values are passed down as `:bool = True`
  • <c-component is=".." /> - Dynamically insert a component where the name is generated by a variable or template expression
back Quickstart
next Layouts