lotusui
A Go design system for desktop and mobile — one codebase, native apps. Web when you want it.
GitHub

Select

Trigger + chevron, a floating panel of options, check-marked selection.

A bordered trigger showing the current choice; clicking it opens a floating panel over the content beneath — built on the shared portal primitive (Floating), so it escapes any parent clipping, paints above everything, and wins the pointer. Picking an option, pressing anywhere else, or Escape closes it; the panel opens aligned to the current selection. Web-specific composition (SelectTrigger/SelectValue sub-components) and RTL are "from the web": Go structs replace composition, and RTL layout is not yet supported by the underlying toolkit.

Installation

Use the module import (the default), or own the source: lotusui add vendors the component into your app and lotusui update keeps the copy mergeable — see Registry.

Go
go get github.com/ikaito-com/lotusui

# or vendor the source into your app:
go run github.com/ikaito-com/lotusui/cmd/lotusui add select

Usage

Options carry a Label (what the user reads) and a Value (what your app stores) — SelectItem / SelectOpts build lists. Call Clear() for the placeholder state; read the choice with Value(), never an index. Open the demo's panel: it floats over whatever is beneath.

open demo ↗

Align item with trigger

AlignItemWithTrigger overlays the open panel so the SELECTED row sits directly over the trigger — the native-select feel — instead of dropping below. Long lists still scroll; the alignment accounts for scrolled-away rows.

open demo ↗

Groups

Groups renders options under muted labels with separators between groups. Value() reads the choice whichever group it came from — grouping is presentation, not identity.

open demo ↗

Scrollable

The panel caps at seven default rows; longer lists scroll inside it, and the panel opens scrolled to the current selection — the align-with-trigger behavior, transposed. Variable-height Content rows share the same height budget.

open demo ↗

Meta

SelectOption.Meta is optional secondary text on the far right of an option row (and on the closed trigger) — a count, shortcut, … Empty omits it. The selected check still sits after Meta.

open demo ↗

Icons

SelectOption.Icon paints a leading icon on the row and closed trigger (when Content is nil).

open demo ↗

Subscription plan

Build-time Content replaces Icon+Label with arbitrary widgets — the shadcn plan card (title + description). The same Content paints in the closed trigger. Keep Label/Value for the choice contract; build Content when the options list is built, not every frame in hot paths.

open demo ↗

Disabled

Freezes the control on its current choice — identity fields that cannot change after creation.

open demo ↗

Invalid

Danger chrome on the trigger — pair it with a Field error message.

open demo ↗

Sizes

All seven shared Size presets on the trigger frame.

open demo ↗

API

OptionTypeDescription
Options[]SelectOptionThe choices: Label is what the user reads, Value what your app stores (HTML's <option value>). Empty Value = the Label is the value; SelectItem / SelectOpts build lists.
SelectOption.IconstringLeading icon on the option row and closed trigger when Content is nil.
SelectOption.Contentlayout.WidgetBuild-time rich row body (multiline plan cards, …). Replaces Icon+Label in the panel and trigger; Label/Value still own identity.
SelectOption.MetastringOptional secondary text on the far right of the option row and closed trigger. Empty omits it; the selected check still sits after Meta. Hidden when Content is set.
SelectItem / SelectItemValue / SelectItemsctorsBuild-time composition: one option, valued option, pack into a slice.
SelectGrouped / SelectGroupsctorsBuild-time groups (the type remains SelectGroup — Go cannot also export func SelectGroup).
Groups[]SelectGroupWins over Options: options under muted labels, separators between groups, flattened in order.
SizeSizeThe shared size presets for the trigger frame.
Value() / SetValue(v)stringRead and write the CHOICE — never an index, so reordering or rewording the options cannot change what stored data means. An unknown SetValue clears the choice.
Clear() / Chosen()Back to the placeholder state; whether anything is chosen. The zero value selects the first option, like a <select> with no selected attribute.
PlaceholderstringShown in muted ink while nothing is chosen.
AlignItemWithTriggerboolOpen the panel with the selected row over the trigger (uniform string rows; Content rows drop below).
InvalidboolDanger chrome on the trigger.
DisabledboolFreezes the control: no pointer cursor, no opening, dimmed value.

lotusui is a Go design system for native desktop and mobile — and the web when you want it. Docs demos are the real components (WebAssembly) — one gallery app, each Preview an iframe addressed by URL hash.