Skip to content

Select

A select dropdown with label, validation states, large lists, Alpine.js binding for dependent selects, and disabled/readonly modes.

Default Rendered example
Usage Example
@selectfield.Select(selectfield.Config{
    ID:    "os",
    Name:  "os",
    Label: "Operating System",
    Options: []selectfield.Option{
        {Value: "mac", Label: "Mac"},
        {Value: "windows", Label: "Windows"},
        {Value: "linux", Label: "Linux"},
    },
})

Built-in text: the development API provides expressions.Select for application defaults, request preferences, and individual component overrides. Read the expression guide.

HTMX Filter

InputAttrs accepts hx-trigger=change. Mouse and keyboard picks emit one bubbling change after the submitted value is current. Initial rendering, same-value picks, and parent Alpine.Model updates are silent. External input/change synchronization emits no extra event. Shell mode leaves value and events to its children.

HTMX Filter Rendered example

Showing all types

HTMX Filter
<form>
    @selectfield.Select(selectfield.Config{
        ID: "allocation-type", Name: "type", Label: "Allocation type",
        Options: []selectfield.Option{
            {Value: "", Label: "All types", Selected: true},
            {Value: "allocated", Label: "Allocated"},
            {Value: "reserved", Label: "Reserved"},
        },
        InputAttrs: templ.Attributes{
            "hx-get": "/api/components/select/filter", "hx-trigger": "change",
            "hx-target": "#allocation-result", "hx-include": "closest form",
        },
    })
    <p id="allocation-result" aria-live="polite">Showing all types</p>
</form>

Error State

State: selectfield.StateError recolors the field and shows HelperText as an error.

Error State Rendered example
Error: Please select an operating system
Error State
@selectfield.Select(selectfield.Config{
    ID:         "os-error",
    Name:       "os",
    Label:      "Operating System",
    State:      selectfield.StateError,
    HelperText: "Error: Please select an operating system",
    Options:    options,
})

Success State

State: selectfield.StateSuccess applies the success styling. Mark an Option Selected for the initial value.

Success State Rendered example
Success State
@selectfield.Select(selectfield.Config{
    ID:    "os-success",
    Name:  "os",
    Label: "Operating System",
    State: selectfield.StateSuccess,
    Options: []selectfield.Option{
        {Value: "mac", Label: "Mac", Selected: true},
        {Value: "windows", Label: "Windows"},
    },
})

Large List

Selects handle long option lists (e.g. a country picker) with native scrolling.

Large List Rendered example
Large List
@selectfield.Select(selectfield.Config{
    ID:           "country",
    Name:         "country",
    Label:        "Country",
    Autocomplete: "country",
    Options:      countryOptions(),
})

Dependent Selects (Alpine.js)

Bind Alpine.Model to share state; Alpine.BindDisabled keeps the second select disabled until the first has a value.

Dependent Selects (Alpine.js) Rendered example
Dependent Selects (Alpine.js)
<div x-data="{ firstValue: '', secondValue: '' }">
    @selectfield.Select(selectfield.Config{ID: "modelName", Alpine: &selectfield.AlpineConfig{Model: "firstValue"}, Options: models})
    @selectfield.Select(selectfield.Config{ID: "year", Alpine: &selectfield.AlpineConfig{Model: "secondValue", BindDisabled: "!firstValue"}, Options: years})
</div>

External Draft Restoration

Restore persisted state by setting the hidden submission input identified by Config.ID, then dispatching a bubbling input or change event. Select synchronizes the visible label and live ARIA selection.

External Draft Restoration Rendered example
External Draft Restoration
@selectfield.Select(selectfield.Config{
    ID:      "draft-os",
    Name:    "draft-os",
    Label:   "Draft operating system",
    Options: options,
})

<button
    type="button"
    x-on:click="
        const input = document.getElementById('draft-os');
        input.value = 'linux';
        input.dispatchEvent(new Event('change', { bubbles: true }));
    "
>Restore Linux draft</button>

Disabled

Disabled: true blocks interaction.

Disabled Rendered example
Disabled
@selectfield.Select(selectfield.Config{
    ID:       "os-disabled",
    Name:     "os",
    Label:    "Operating System",
    Disabled: true,
    Options:  options,
})

Readonly

Readonly: true keeps the selected value submittable but prevents the user from changing it.

Readonly Rendered example
Readonly
@selectfield.Select(selectfield.Config{
    ID:       "os-readonly",
    Name:     "os-readonly",
    Label:    "Operating System",
    Readonly: true,
    Options:  options,
})

Shell Mode

Set Shell: true when Select should provide trigger and dropdown chrome while custom templ children own the value. Use ValueExpr for the trigger text and dispatch select-close after a pick.

Shell Mode Rendered example
Shell Mode
<div x-data="{ access: 'Viewer' }">
    @selectfield.Select(selectfield.Config{
        ID:           "access-shell",
        Label:        "Access level",
        Shell:        true,
        TriggerLabel: "Role",
        ValueExpr:    "access",
    }) {
        <button type="button" x-on:click="access = 'Admin'; $dispatch('select-close')">Admin</button>
        <button type="button" x-on:click="access = 'Viewer'; $dispatch('select-close')">Viewer</button>
    }
</div>

Go API

v0.4.0

The examples above cover behavior and composition. pkg.go.dev is the canonical reference for exported types, functions, methods, and Go documentation.

github.com/araihu/goshtoso/components/select
Open v0.4.0 API