Skip to content

Input

Input
Thymeleaf source
<sl:label for="email">Email</sl:label>
<sl:input type="email" th:field="*{email}" placeholder="you@example.com"/>
Rendered HTML
<label class="label" for="email">Email</label>
<input class="input" type="email" id="email" name="email" placeholder="you@example.com">

Inside a form with th:object, write th:field as on a plain <input>. Spring sets the input’s id, name and value, so a label with for set to the field name points at it:

<form th:object="${profile}" th:action="@{/profile}" method="post">
<sl:label for="email">Email</sl:label>
<sl:input type="email" th:field="*{email}"/>
</form>

An explicit id wins over the one th:field gives. In a field, put th:field on the field instead: it adds the label, description and error messages and wires them to the input.

type defaults to text and takes the text-like input types. Checkboxes, radio buttons and switches get components of their own.

Types
Thymeleaf source
<sl:label for="input-password">Password</sl:label>
<sl:input type="password" id="input-password" name="password"/>
<sl:label for="input-date">Delivery date</sl:label>
<sl:input type="date" id="input-date" name="deliveryDate"/>
<sl:label for="input-file">Invoice</sl:label>
<sl:input type="file" id="input-file" name="invoice"/>
Rendered HTML
<label class="label" for="input-password">Password</label>
<input class="input" type="password" id="input-password" name="password">
<label class="label" for="input-date">Delivery date</label>
<input class="input" type="date" id="input-date" name="deliveryDate">
<label class="label" for="input-file">Invoice</label>
<input class="input" type="file" id="input-file" name="invoice">

When the field has errors after a @Valid submit, the input marks itself aria-invalid="true", which the skins draw in the destructive colour. A field shows the error message and links it with aria-describedby; outside one, an aria-describedby you write passes through.

InvalidWith th:field, a field that has errors marks the input aria-invalid by itself.
Thymeleaf source
<sl:label for="email">Email</sl:label>
<sl:input type="email" th:field="*{email}"/>
Rendered HTML
<label class="label" for="input-invalid">Email</label>
<input class="input" type="email" id="input-invalid" name="email" value="wim@" aria-invalid="true">

Without th:field, write aria-invalid="true" yourself.

A disabled input fades and cannot be focused, as in shadcn; the lyra skin also gives it a grey background. Its value is not submitted, and its label fades with it. In Windows’ high-contrast mode it turns the system’s grey text colour instead of fading.

Disabled
Thymeleaf source
<sl:label for="input-disabled">Customer number</sl:label>
<sl:input id="input-disabled" name="customerNumber" value="C-1042" disabled/>
Rendered HTML
<label class="label" for="input-disabled">Customer number</label>
<input class="input" type="text" id="input-disabled" name="customerNumber" value="C-1042" disabled>

readonly keeps the value but stops the user from changing it. Unlike a disabled input, a read-only one still takes focus, its value can be selected and copied, and it is submitted with the form: use it for a value the user needs to see or quote, such as a reference number. It gets a muted background and loses its shadow, but keeps its full text colour, so it reads differently from a disabled input and its text keeps its contrast (a read-only input is not exempt from WCAG’s contrast minimum, a disabled one is). Errors still show: the destructive border and ring win over the read-only look. shadcn has no read-only style.

Read-onlyThe value cannot be changed, but it can be focused, selected and copied, and it is submitted.
Thymeleaf source
<sl:label for="input-reference">Order reference</sl:label>
<sl:input id="input-reference" name="reference" value="ORD-2026-0042" readonly/>
Rendered HTML
<label class="label" for="input-reference">Order reference</label>
<input class="input" type="text" id="input-reference" name="reference" value="ORD-2026-0042" readonly>

Every attribute that is not a prop lands on the <input>:

With htmxEvery attribute that is not a prop lands on the input.
Thymeleaf source
<sl:input type="search" name="q" aria-label="Search orders"
th:hx-get="@{/orders}" hx-trigger="input changed delay:300ms" hx-target="#orders"/>
Rendered HTML
<input class="input" type="search" name="q" aria-label="Search orders" hx-get="/orders" hx-trigger="input changed delay:300ms" hx-target="#orders">
AttributeValuesDefaultDescription
typetext email password number search tel url date datetime-local month week time filetextType of the input. Checkboxes and radio buttons have components of their own.

Every prop can also be set from an expression: th:type="${...}". Any other attribute passes through to the rendered element, and class is appended.