cotton
for
docs syntax
HTML: Snippets will be shown in HTML-like syntax.
Native: Snippets will be shown in native Django template syntax.

Building a re-usable form input field

Componentizing form fields enable you to easily maintain a consistent style across your whole app. This approach is particularly favourable when using a utility first CSS design system like Tailwind where every aspect of the style is detailed and maintained in isolated component files.

For this example, we're using Tailwind for styling. Let's start with a standard input:

form_view.html
<input type="text" name="shoe_size" placeholder="Shoe Size" class="border rounded shadow px-3 py-1.5">
preview

Having to repeat this code more than once may get tedious and difficult to maintain. Let's componentize.

cotton/input.html
<input type="text" name="{{ name }}" placeholder="{{ placeholder }}" class="border rounded shadow px-3 py-1.5">
form_view.html
<c-input name="shoe_size" placeholder="Shoe Size" />
<c-input name="country" placeholder="Country" />
<c-input name="age" placeholder="Age" />
{% cotton input name="shoe_size" placeholder="Shoe Size" %}{% endcotton %}
{% cotton input name="country" placeholder="Country" %}{% endcotton %}
{% cotton input name="age" placeholder="Age" %}{% endcotton %}
preview

Change the input type

You will probably need more than just a text input in your project. So let's declare an attribute `text` in <c-vars />. Adding it as a var will allow us to set "text" as the default. Additionally, it will be excluded from {{ attrs }}:

cotton/input.html
<c-vars type="text" />

<input type="{{ type }}" {{ attrs }} class="border rounded shadow px-3 py-1.5">
{% cotton:vars type="text" %}

<input type="{{ type }}" {{ attrs }} class="border rounded shadow px-3 py-1.5">
form_view.html
<c-input name="some_text" placeholder="Just a text field" />
<c-input type="email" name="email" placeholder="Email" />
<c-input type="password" name="password" placeholder="Password" />
<c-input type="number" name="Count" placeholder="Password" />
{% cotton input name="some_text" placeholder="Just a text field" %}{% endcotton %}
{% cotton input type="email" name="email" placeholder="Email" %}{% endcotton %}
{% cotton input type="password" name="password" placeholder="Password" %}{% endcotton %}
{% cotton input type="number" name="Count" placeholder="Password" %}{% endcotton %}
preview

Adding validation state

Let's also handle displaying errors when we need to.

cotton/input.html
<c-vars type="text" errors />

<input type="{{ type }}" {{ attrs }} class="border rounded shadow px-3 py-1.5 {% if errors %}border-red-500{% endif %}">

{% if errors %}
    <div class="text-red-500 text-sm mt-1">{{ errors.as_text }}</div>
{% endif %}
{% cotton:vars type="text" errors %}

<input type="{{ type }}" {{ attrs }} class="border rounded shadow px-3 py-1.5 {% if errors %}border-red-500{% endif %}">

{% if errors %}
    <div class="text-red-500 text-sm mt-1">{{ errors.as_text }}</div>
{% endif %}
form_view.html
<c-input name="surname" placeholder="Surname" :errors="form.surname.errors" />
{% cotton input name="surname" placeholder="Surname" :errors="form.surname.errors" %}{% endcotton %}
preview
Your surname is required.

Bonus - Adding icons

To take customization a step further and to demonstrate the flexibility of cotton, let's now give our input component the ability to display icons.

cotton/input.html
<c-vars type="text" leading_icon trailing_icon />

<div class="border rounded shadow flex items-center">
    {% if leading_icon %}
        <div class="pl-3">{{ leading_icon }}</div>
    {% endif %}

    <input type="{{ type }}" {{ attrs }} class="px-3 py-1.5 w-full">
</div>
{% cotton:vars type="text" leading_icon trailing_icon %}

<div class="border rounded shadow flex items-center">
    {% if leading_icon %}
        <div class="pl-3">{{ leading_icon }}</div>
    {% endif %}

    <input type="{{ type }}" {{ attrs }} class="px-3 py-1.5 w-full">
</div>
form_view.html
<c-input name="text" name="username" placeholder="Username">
    <c-slot name="leading_icon">
        <svg>...</svg>
    </c-slot>
</c-input>

<c-input type="password" name="password" name="password" placeholder="Password">
    <c-slot name="leading_icon">
        <svg>...</svg>
    </c-slot>
</c-input>
{% cotton input name="text" name="username" placeholder="Username" %}
    {% cotton:slot leading_icon %}
        <svg>...</svg>
    {% endcotton:slot %}
{% endcotton %}

{% cotton input type="password" name="password" name="password" placeholder="Password" %}
    {% cotton:slot leading_icon %}
        <svg>...</svg>
    {% endcotton:slot %}
{% endcotton %}
preview