Select
A native list of exclusive choices.
<selectbox.Select name="plan" label="Plan" class="w-48">
<selectbox.SelectOption value="free" selected>Free</selectbox.SelectOption>
<selectbox.SelectOption value="pro">Pro</selectbox.SelectOption>
<selectbox.SelectOption value="team">Team</selectbox.SelectOption>
</selectbox.Select>Installation
Run the command in the app module.
gx add selectThe command also installs icons.
The command writes these files.
| File | Path in the app |
|---|---|
Select.gx |
ui/select/Select.gx |
SelectGroup.gx |
ui/select/SelectGroup.gx |
SelectOption.gx |
ui/select/SelectOption.gx |
SelectSeparator.gx |
ui/select/SelectSeparator.gx |
Select.fixtures.go |
ui/select/Select.fixtures.go |
SelectGroup.fixtures.go |
ui/select/SelectGroup.fixtures.go |
SelectOption.fixtures.go |
ui/select/SelectOption.fixtures.go |
SelectSeparator.fixtures.go |
ui/select/SelectSeparator.fixtures.go |
styles.go |
ui/select/styles.go |
Install icons first.
Copy each file to its path in the app. Change each import of a registry package to the path of that package in the app.
package selectbox
import "github.com/alternayte/gx/registry/icons"
props {
// Name is the form field name.
Name string = ""
// Label is the accessible name of the select.
Label string = ""
// Size sets the height: Md or Sm.
Size Size = Md
// Placeholder is the text of a disabled first option that shows while no value is selected. An
// empty value renders no placeholder option.
Placeholder string = ""
// Class adds classes to the root element.
Class string = ""
// Children is the options of the select.
Children gx.Node
// Attrs adds HTML attributes to the select element.
Attrs gx.Attrs = nil
}
<span class={gx.Cx("relative inline-flex w-fit", p.Class)}>
<select name={p.Name} aria-label={p.Label} class={gx.Cx("w-full min-w-0 appearance-none rounded-md border border-input bg-transparent py-0 pr-9 pl-3 text-sm whitespace-nowrap shadow-xs outline-none transition-[color,box-shadow] focus-visible:border-ring focus-visible:ring-[3px] focus-visible:ring-ring/50 disabled:cursor-not-allowed disabled:opacity-50 aria-invalid:border-destructive aria-invalid:ring-destructive/20 has-[option[value='']:checked]:text-muted-foreground dark:bg-input/30 dark:hover:bg-input/50 dark:aria-invalid:ring-destructive/40", sizeClass[p.size()])} {...p.Attrs}>
if p.Placeholder != "" {
<option value="" disabled selected hidden>{p.Placeholder}</option>
}
{p.Children}
</select>
<icons.ChevronDown class="pointer-events-none absolute top-1/2 right-3 size-4 -translate-y-1/2 text-muted-foreground opacity-50" />
</span>package selectbox
props {
// Label is the visible name of the option group.
Label string
// Disabled disables every option of the group.
Disabled bool = false
// Children is the options of the group.
Children gx.Node
}
<optgroup label={p.Label} disabled={p.Disabled}>{p.Children}</optgroup>package selectbox
props {
// Value is the value the form submits when the option is selected.
Value string = ""
// Selected selects the option for the first render.
Selected bool = false
// Disabled disables the option.
Disabled bool = false
// Children is the option label.
Children gx.Node
}
<option value={p.Value} selected={p.Selected} disabled={p.Disabled}>{p.Children}</option>package selectbox
props {
// Attrs adds HTML attributes to the root element.
Attrs gx.Attrs = nil
}
<hr {...p.Attrs} />package selectbox
import "github.com/alternayte/gx"
func plans() gx.Node {
return gx.Frag(
SelectOption(SelectOptionProps{Value: "free", Selected: true, Children: gx.Text("Free")}),
SelectOption(SelectOptionProps{Value: "pro", Children: gx.Text("Pro")}),
SelectOption(SelectOptionProps{Value: "team", Children: gx.Text("Team")}),
)
}
var SelectFixtures = gx.Fixtures[SelectProps]{
"Plan": {Name: "plan", Label: "Plan", Class: "w-48", Children: plans()},
"Small": {Name: "plan", Label: "Plan", Size: Sm, Class: "w-48", Children: plans()},
"Fit": {Name: "plan", Label: "Plan", Children: plans()},
"Disabled": {Name: "plan", Label: "Plan", Class: "w-48", Attrs: gx.Attrs{gx.Bool("disabled", true)}, Children: SelectOption(SelectOptionProps{Value: "free", Children: gx.Text("Free")})},
"Invalid": {Name: "plan", Label: "Plan", Class: "w-48", Attrs: gx.Attrs{{Key: "aria-invalid", Value: "true"}}, Children: plans()},
"Placeholder": {Name: "fruit", Label: "Fruit", Placeholder: "Select a fruit", Class: "w-48", Children: gx.Frag(
SelectGroup(SelectGroupProps{Label: "Fruits", Children: gx.Frag(
SelectOption(SelectOptionProps{Value: "apple", Children: gx.Text("Apple")}),
SelectOption(SelectOptionProps{Value: "banana", Children: gx.Text("Banana")}),
)}),
SelectSeparator(SelectSeparatorProps{}),
SelectGroup(SelectGroupProps{Label: "Vegetables", Children: gx.Frag(
SelectOption(SelectOptionProps{Value: "carrot", Children: gx.Text("Carrot")}),
SelectOption(SelectOptionProps{Value: "leek", Disabled: true, Children: gx.Text("Leek")}),
)}),
)},
}package selectbox
import "github.com/alternayte/gx"
var SelectGroupFixtures = gx.Fixtures[SelectGroupProps]{
"Group": {Label: "Fruits", Children: gx.Frag(
SelectOption(SelectOptionProps{Value: "apple", Children: gx.Text("Apple")}),
SelectOption(SelectOptionProps{Value: "banana", Children: gx.Text("Banana")}),
)},
}
// SelectGroupWrap renders the group inside a select, as a page uses it.
func SelectGroupWrap(n gx.Node) gx.Node {
return Select(SelectProps{Label: "Fruit", Children: n})
}package selectbox
import "github.com/alternayte/gx"
var SelectOptionFixtures = gx.Fixtures[SelectOptionProps]{"Option": {Value: "free", Children: gx.Text("Free")}}
// SelectOptionWrap renders the option inside a select, as a page uses it.
func SelectOptionWrap(n gx.Node) gx.Node {
return Select(SelectProps{Label: "Plan", Children: n})
}package selectbox
import "github.com/alternayte/gx"
var SelectSeparatorFixtures = gx.Fixtures[SelectSeparatorProps]{"Default": {}}
// SelectSeparatorWrap renders the separator between two options of a select.
func SelectSeparatorWrap(n gx.Node) gx.Node {
return Select(SelectProps{Label: "Plan", Children: gx.Frag(
SelectOption(SelectOptionProps{Value: "free", Children: gx.Text("Free")}),
n,
SelectOption(SelectOptionProps{Value: "pro", Children: gx.Text("Pro")}),
)})
}package selectbox
import "github.com/alternayte/gx"
// Size is the height of a select.
type Size string
// The sizes of selectbox.Select.
const (
Md Size = "default"
Sm Size = "sm"
)
var sizeClass = gx.Enum[Size]{
Md: "h-9",
Sm: "h-8",
}
// size returns the size of one select; a zero value is Md.
func (p SelectProps) size() Size {
if p.Size == "" {
return Md
}
return p.Size
}The theme must define these tokens: --input, --ring, --muted-foreground, --destructive.
Usage
<selectbox.Select name="fruit" label="Fruit" placeholder="Select a fruit" class="w-48">
<selectbox.SelectGroup label="Fruits">
<selectbox.SelectOption value="apple">Apple</selectbox.SelectOption>
<selectbox.SelectOption value="banana">Banana</selectbox.SelectOption>
</selectbox.SelectGroup>
<selectbox.SelectSeparator />
<selectbox.SelectOption value="carrot" selected={true}>Carrot</selectbox.SelectOption>
</selectbox.Select>The component is a styled native <select>, so the keyboard, the option list and the mobile picker come from the browser. Size is selectbox.Md (default) or selectbox.Sm. Class sets the width of the control; without it the control is as wide as its longest option. Attrs go on the <select>.
Placeholder shows muted text until the user selects an option. SelectGroup is an <optgroup> and SelectSeparator is an <hr> between options.
Examples
Select: Plan
<selectbox.Select name="plan" label="Plan" class="w-48">
<selectbox.SelectOption value="free" selected>Free</selectbox.SelectOption>
<selectbox.SelectOption value="pro">Pro</selectbox.SelectOption>
<selectbox.SelectOption value="team">Team</selectbox.SelectOption>
</selectbox.Select>Select: Small
<selectbox.Select name="plan" label="Plan" size={selectbox.Sm} class="w-48">
<selectbox.SelectOption value="free" selected>Free</selectbox.SelectOption>
<selectbox.SelectOption value="pro">Pro</selectbox.SelectOption>
<selectbox.SelectOption value="team">Team</selectbox.SelectOption>
</selectbox.Select>Select: Fit
<selectbox.Select name="plan" label="Plan">
<selectbox.SelectOption value="free" selected>Free</selectbox.SelectOption>
<selectbox.SelectOption value="pro">Pro</selectbox.SelectOption>
<selectbox.SelectOption value="team">Team</selectbox.SelectOption>
</selectbox.Select>Select: Disabled
<selectbox.Select name="plan" label="Plan" class="w-48" attrs={gx.Attrs{gx.Bool("disabled", true)}}>
<selectbox.SelectOption value="free">Free</selectbox.SelectOption>
</selectbox.Select>Select: Invalid
<selectbox.Select name="plan" label="Plan" class="w-48" attrs={gx.Attrs{{Key: "aria-invalid", Value: "true"}}}>
<selectbox.SelectOption value="free" selected>Free</selectbox.SelectOption>
<selectbox.SelectOption value="pro">Pro</selectbox.SelectOption>
<selectbox.SelectOption value="team">Team</selectbox.SelectOption>
</selectbox.Select>Select: Placeholder
<selectbox.Select name="fruit" label="Fruit" placeholder="Select a fruit" class="w-48">
<selectbox.SelectGroup label="Fruits">
<selectbox.SelectOption value="apple">Apple</selectbox.SelectOption>
<selectbox.SelectOption value="banana">Banana</selectbox.SelectOption>
</selectbox.SelectGroup>
<selectbox.SelectSeparator />
<selectbox.SelectGroup label="Vegetables">
<selectbox.SelectOption value="carrot">Carrot</selectbox.SelectOption>
<selectbox.SelectOption value="leek" disabled>Leek</selectbox.SelectOption>
</selectbox.SelectGroup>
</selectbox.Select>SelectGroup: Group
<selectbox.SelectGroup label="Fruits">
<selectbox.SelectOption value="apple">Apple</selectbox.SelectOption>
<selectbox.SelectOption value="banana">Banana</selectbox.SelectOption>
</selectbox.SelectGroup>SelectOption: Option
<selectbox.SelectOption value="free">Free</selectbox.SelectOption>SelectSeparator: Default
<selectbox.SelectSeparator />API reference
A tag sets a prop by its name with a lower-case first letter: Class is class.
selectbox.Select
| Prop | Type | Default | Description |
|---|---|---|---|
Name |
string |
"" |
Name is the form field name. |
Label |
string |
"" |
Label is the accessible name of the select. |
Size |
Size |
Md |
Size sets the height: Md or Sm. |
Placeholder |
string |
"" |
Placeholder is the text of a disabled first option that shows while no value is selected. An empty value renders no placeholder option. |
Class |
string |
"" |
Class adds classes to the root element. |
Children |
gx.Node |
Required | Children is the options of the select. |
Attrs |
gx.Attrs |
nil |
Attrs adds HTML attributes to the select element. |
selectbox.SelectGroup
| Prop | Type | Default | Description |
|---|---|---|---|
Label |
string |
Required | Label is the visible name of the option group. |
Disabled |
bool |
false |
Disabled disables every option of the group. |
Children |
gx.Node |
Required | Children is the options of the group. |
selectbox.SelectOption
| Prop | Type | Default | Description |
|---|---|---|---|
Value |
string |
"" |
Value is the value the form submits when the option is selected. |
Selected |
bool |
false |
Selected selects the option for the first render. |
Disabled |
bool |
false |
Disabled disables the option. |
Children |
gx.Node |
Required | Children is the option label. |
selectbox.SelectSeparator
| Prop | Type | Default | Description |
|---|---|---|---|
Attrs |
gx.Attrs |
nil |
Attrs adds HTML attributes to the root element. |
Do and do not
Do
- Mark one option with
Selected, or set aPlaceholder. - Pair the select with a label, or set
Label. - Set
aria-invalidthroughAttrswhen the value fails validation.
Don't
- Do not use a select for fewer than four options. Use a radio group.
- Do not leave the select without a name in a form.
Keyboard
| Key | Action |
|---|---|
| Tab | Moves focus to the select. |
| Arrow keys | Moves through the options. |
| A letter | Selects the next option that starts with the typed text. |
| Enter, Space | Opens or commits the choice. |