Koala logo Design
No matches for “
↑↓ navigate open Esc close
Components Dropdowns

Dropdowns

Alpine custom dropdown — trigger button (label + chevron) + panel that opens on click and closes on click.outside. Options go inside as child content using koala-dropdown-option.

<koala-dropdown>

Canonical

<koala-dropdown placeholder="Select a partner">
    <button type="button" koala-dropdown-option x-on:click="open = false">Acme Legal</button>
    <button type="button" koala-dropdown-option x-on:click="open = false">Bramble &amp; Co</button>
    <button type="button" koala-dropdown-option x-on:click="open = false">Carter Conveyancing</button>
</koala-dropdown>

Use for any small, finite list of options. Options pair with x-on:click="open = false" to dismiss the panel after selection.

Variants

3 variants
Placeholder
placeholder="Select a partner"
With selection
selected-label="Acme Legal"
Form-post options
submits on click
<koala-dropdown placeholder="Pick a branch">
    <form method="post" asp-page-handler="SelectBranch">
        <input type="hidden" name="BranchId" value="@branch.Id" />
        <button type="submit" koala-dropdown-option>@branch.Name</button>
    </form>
</koala-dropdown>

States

3 states
Closed
Open
Click the trigger to open the panel.
Selected

Props

4 attributes
Attribute Values Notes
placeholder string Defaults to Select…. Shown in muted colour until a selection is made.
selected-label string? When present, replaces the placeholder with the current selection in default text colour.
trigger-icon IconName Icon inside the trigger. Defaults to ChevronDown; override to Funnel for filter dropdowns.
trigger-id string? Optional id on the trigger <button>. Set when other elements need aria-controls or label-for relationships.
class string? Extra classes appended to the wrapping <div>.
koala-dropdown-option attribute on option Applies hover + rounded styling. Pair with x-on:click="open = false" for non-form options to dismiss the panel.

Do & don't

Do Use a dropdown when the list fits on one screen and the user just needs to pick one. Show the current value in the trigger.
Don't Don't use a dropdown for a large unbounded set — reach for an autocomplete (typeahead) component instead.

Multi-select variant

<koala-multi-dropdown> uses the same chrome (trigger, click-outside close, Escape, aria-expanded) but the panel renders checkbox-style options that don't close it on click. Use for filter rows where the user picks multiple values.

<koala-multi-dropdown label="Status" count="@selectedCount" trigger-icon="CircleDot">
    <label class="flex items-center px-3 py-2 hover:bg-surface-dim cursor-pointer">
        <input type="checkbox" name="Status" value="New" class="w-4 h-4 mr-2 text-primary border-outline focus:ring-primary" />
        <span class="text-on-surface">New</span>
    </label>
</koala-multi-dropdown>
Attribute Values Notes
label String Trigger label (e.g. "Status"). Default Filter.
count int? Number of values selected — appears in the trigger as "Status (3)" when > 0.
trigger-icon IconName Default Funnel. Pick something more specific where it helps (CircleDot for status filters).
trigger-id String Optional. Useful for x-target swaps that re-render the trigger with the new count.

Form-bound (enum)

Set for to bind an enum model property. The dropdown then renders a hidden input (so server-side model binding + validation pick up the value), auto-generates its options from the enum values' [Display(Name = "…")] names, and shows a validation message. The Unknown sentinel is filtered out by default (override via exclude-values). This replaces the former <koala-enum-field> helper.

<koala-dropdown for="Input.Crm" label="CRM" placeholder="Select CRM" optional />

Bound to Input.Crm (a DemoCrm? enum). Click to see each value's [Display(Name)] label.

Attribute Values Notes
for Model expression Switches on form-bound mode. Must resolve to an enum type (nullable supported).
label String Optional. Renders a <label> above the dropdown.
optional Boolean Appends (optional) to the label.
exclude-values Comma-separated identifiers Default Unknown. Override to filter additional values out of the option list.
class Tailwind classes In form-bound mode, defaults to mb-5. Override for tight layouts.

Sort (form-submit)

Set submit-options (an IReadOnlyList<ListSortOption>) + submit-name to turn the panel into a sort control. This is a sort control whose options submit the list filter form so the active search + filters travel with the sort — each option renders as a <button type="submit"> inside the enclosing <form method="get">, posting the submit-name query param with the option's token. Set the outer-wrapper id and list it in the form's x-target.push so the active label re-renders after the AJAX submit. Pair with variant="Chip" and trigger-icon="ArrowUpDown". Absorbs the former <koala-dropdown-sort>.

@{
    var sort = new SortDropdownModel(new[]
    {
        new ListSortOption("newest", "Newest first", IsActive: Model.Sort == "newest"),
        new ListSortOption("oldest", "Oldest first", IsActive: Model.Sort == "oldest"),
    });
}
<form method="get" x-target.push="client-results sort-dropdown">
    <koala-dropdown variant="Chip" trigger-icon="ArrowUpDown"
                    id="@sort.Id" submit-name="@sort.FieldName" submit-options="@sort.Options"
                    align="right"
                    selected-label="@(sort.Options.FirstOrDefault(o => o.IsActive)?.Label ?? "Sort")" />
</form>

Show the active sort in the trigger label. Cursor pagination resets to page 1 because ?after= is intentionally not a form field.

Attribute Values Notes
submit-options IReadOnlyList<ListSortOption> Switches on form-submit (sort) mode. The token / label / is-active set rendered as submit buttons.
submit-name String The query-string field each option submits (e.g. Sort, QuoteSort).
id String Set on the outer wrapper. List it in the form's x-target.push so the active label repaints after submit.
align left | right Panel alignment. right for sort controls anchored at the right of the list header.

Chip trigger (variant)

Set variant="Chip" for a compact, inline outlined trigger that sits next to a heading or inline value — rather than the full-width, input-like default (variant="Input"). The panel still opens on click and closes on click-outside / Escape. Pair with align="right" when the chip sits at the right edge of its row so the panel doesn't overflow the viewport.

<koala-dropdown variant="Chip" selected-label="London office">
    <button type="button" koala-dropdown-option x-on:click="open = false">London office</button>
    <button type="button" koala-dropdown-option x-on:click="open = false">Bristol office</button>
</koala-dropdown>

The same child-content / items / submit-options options work in either variant.

Model-driven items (version picker)

Pass items (an IReadOnlyList<KoalaDropdownItem>) to build the menu from data instead of child markup. Each item becomes a link (carrying x-target.push for Alpine-AJAX nav); the one marked IsActive renders as a highlighted, non-link current item. This is what the quote and terms version pickers use — a Chip trigger showing the current version with the full version history in the panel.

@{
    var versionItems = Model.AllVersions
        .OrderByDescending(v => v.Version)
        .Select(v => new KoalaDropdownItem(
            $"Version {v.Version}",
            Href: ViewQuote.Route(v.Id),
            IsActive: v.Id == Model.Quote.Id))
        .ToList();
}
<koala-dropdown variant="Chip"
                selected-label="@($"Version {Model.Quote.Version}")"
                items="versionItems" />

Options stack as block links — the active version is highlighted at the top.

Attribute Values Notes
items IReadOnlyList<KoalaDropdownItem> Switches on model-driven options. Each KoalaDropdownItem is (Label, Href?, IsActive, XTarget).
variant Input | Chip Trigger style. Input (default) is the full-width input-like button; Chip is the compact inline trigger.
align left | right Chip-variant panel alignment. right when the trigger sits at the edge of its row.