Select (Native)
Thymeleaf source
<sl:label for="status">Status</sl:label><sl:native-select th:field="*{status}"> <option value="">Select status</option> <option th:each="status : ${statuses}" th:value="${status}" th:text="#{|status.${status}|}">Todo</option></sl:native-select>Rendered HTML
<label class="label" for="status">Status</label><div class="native-select-wrapper"> <select class="native-select" id="status" name="status"> <option value="">Select status</option> <option value="todo">Todo</option> <option value="in-progress">In progress</option> <option value="done">Done</option> </select> <svg xmlns="http://www.w3.org/2000/svg" class="icon native-select-icon" viewBox="0 0 24 24" width="24" height="24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true" focusable="false"> <path d="m6 9 6 6 6-6"></path> </svg></div>Native select or select?
Section titled “Native select or select?”Both are a real <select> underneath, bind with th:field and submit with the form. This one styles only the closed
control and leaves the list to the operating system: on a phone that is the system’s own picker, which many people
find easier to use. Use it for long lists (countries, years), in dense forms, and for multiple.
<sl:select> draws its list in the page’s style, like shadcn/ui’s Select, with a check mark on the
chosen item and room for an icon; use it where the look matters and the list is short enough to scan.
Options
Section titled “Options”Write plain <option> and <optgroup> elements as the content, with th:each for options from the model. With
th:field on the select, Spring marks the option that matches the field’s value as selected.
Thymeleaf source
<sl:label for="select-department">Department</sl:label><sl:native-select id="select-department" name="department"> <optgroup label="Engineering"> <option value="frontend">Frontend</option> <option value="backend">Backend</option> </optgroup> <optgroup label="Sales"> <option value="sales-rep">Sales representative</option> <option value="account-manager">Account manager</option> </optgroup></sl:native-select>Rendered HTML
<label class="label" for="select-department">Department</label><div class="native-select-wrapper"> <select class="native-select" id="select-department" name="department"> <optgroup label="Engineering"> <option value="frontend">Frontend</option> <option value="backend">Backend</option> </optgroup> <optgroup label="Sales"> <option value="sales-rep">Sales representative</option> <option value="account-manager">Account manager</option> </optgroup> </select> <svg xmlns="http://www.w3.org/2000/svg" class="icon native-select-icon" viewBox="0 0 24 24" width="24" height="24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true" focusable="false"> <path d="m6 9 6 6 6-6"></path> </svg></div>Thymeleaf source
<sl:native-select size="sm" name="priority" aria-label="Priority (small)"> <option>Low</option> <option>High</option></sl:native-select><sl:native-select name="priority" aria-label="Priority"> <option>Low</option> <option>High</option></sl:native-select>Rendered HTML
<div class="native-select-wrapper" data-size="sm"> <select class="native-select" name="priority" aria-label="Priority (small)"> <option>Low</option> <option>High</option> </select> <svg xmlns="http://www.w3.org/2000/svg" class="icon native-select-icon" viewBox="0 0 24 24" width="24" height="24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true" focusable="false"> <path d="m6 9 6 6 6-6"></path> </svg></div><div class="native-select-wrapper"> <select class="native-select" name="priority" aria-label="Priority"> <option>Low</option> <option>High</option> </select> <svg xmlns="http://www.w3.org/2000/svg" class="icon native-select-icon" viewBox="0 0 24 24" width="24" height="24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true" focusable="false"> <path d="m6 9 6 6 6-6"></path> </svg></div>size picks the height, as in shadcn/ui. It replaces HTML’s own size attribute, which turns a select into a list box;
multiple still works, and hides the chevron.
The select sits in a wrapper that also holds the chevron. class goes to that wrapper, so class="w-full" widens
both; every other attribute goes to the <select>.
Invalid
Section titled “Invalid”As with the input, a field with errors marks the select aria-invalid="true".
Thymeleaf source
<sl:native-select th:field="*{status}" aria-label="Status">…</sl:native-select>Rendered HTML
<div class="native-select-wrapper"> <select class="native-select" name="status" aria-label="Status" aria-invalid="true"> <option value="">Select status</option> <option value="todo">Todo</option> </select> <svg xmlns="http://www.w3.org/2000/svg" class="icon native-select-icon" viewBox="0 0 24 24" width="24" height="24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true" focusable="false"> <path d="m6 9 6 6 6-6"></path> </svg></div>Disabled
Section titled “Disabled”The select fades with its chevron; the lyra skin also gives it a grey background, as it does a disabled input. HTML has
no read-only select: readonly does nothing on one. To show a choice that cannot be changed, use a read-only
input with the option’s text, and a hidden input if the value has to be submitted.
Thymeleaf source
<sl:native-select name="status" aria-label="Status" disabled> <option>Todo</option></sl:native-select>Rendered HTML
<div class="native-select-wrapper"> <select class="native-select" name="status" aria-label="Status" disabled> <option>Todo</option> </select> <svg xmlns="http://www.w3.org/2000/svg" class="icon native-select-icon" viewBox="0 0 24 24" width="24" height="24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true" focusable="false"> <path d="m6 9 6 6 6-6"></path> </svg></div>Attributes
Section titled “Attributes”| Attribute | Values | Default | Description |
|---|---|---|---|
size | sm default | default | Height of the select. It replaces HTML's size attribute, which turns a select into a list box. |
Every prop can also be set from an expression: th:size="${...}". Any other attribute passes through to the rendered element, and class is appended.