Lattice components

Select

Single-value selection primitive that owns open and value state, registers items in order, and anchors popper-positioned content in a portal while you own the visuals.

@lattice-ui/select Feature limited import Select depends on core , focus , layer , motion , popper

Select is the primitive for picking one value from a list: difficulty pickers, region menus, sort dropdowns, and any “choose one” control. It coordinates open state, the selected value, item registration, popper positioning, and outside-press dismissal so your component only has to render a trigger, a value label, and the items.

Reach for Select when a control needs to hold a single value, open an anchored popup over a trigger, and dismiss predictably on selection or an outside interaction.

Import

import { Select } from "@lattice-ui/select";

Anatomy

Root, Trigger, Portal, Content, and at least one Item form the minimum useful select. Value, Group, Label, and Separator are optional and help you structure the trigger label and the list.

Select anatomy

Select anatomy
<Select.Root>
<Select.Trigger>
<Select.Value />
</Select.Trigger>
<Select.Portal>
<Select.Content>
<Select.Group>
<Select.Label />
<Select.Item value="..." />
<Select.Separator />
<Select.Item value="..." />
</Select.Group>
</Select.Content>
</Select.Portal>
</Select.Root>
PartRequiredResponsibility
Select.RootyesOwns open + value state and the item registry, shared through context.
Select.TriggeryesA button that toggles the content open and closed.
Select.ValuenoRenders the selected item’s text, or a placeholder when nothing is chosen.
Select.PortalyesRenders the content into a ScreenGui outside the local tree.
Select.ContentyesThe popper-positioned, dismissable list surface.
Select.ItemyesA selectable option that registers itself and sets the value on activation.
Select.GroupnoA non-semantic container for grouping related items.
Select.LabelnoA heading for a group.
Select.SeparatornoA thin divider between items or groups.

Example

A controlled select for a match difficulty, with a placeholder, a labelled group, and a disabled option.

DifficultySelect.tsx
import { useState } from "@rbxts/react";
import { Select } from "@lattice-ui/select";
export function DifficultySelect() {
const [value, setValue] = useState<string>();
return (
<Select.Root value={value} onValueChange={setValue}>
<Select.Trigger>
<Select.Value placeholder="Choose difficulty" />
</Select.Trigger>
<Select.Portal>
<Select.Content placement="bottom" sideOffset={6}>
<frame
AutomaticSize={Enum.AutomaticSize.Y}
BackgroundColor3={Color3.fromRGB(47, 53, 68)}
BorderSizePixel={0}
Size={UDim2.fromOffset(220, 0)}
>
<uilistlayout SortOrder={Enum.SortOrder.LayoutOrder} />
<Select.Group>
<Select.Label>
<textlabel
BackgroundTransparency={1}
Size={UDim2.fromOffset(220, 20)}
Text="Standard"
TextColor3={Color3.fromRGB(168, 176, 191)}
TextSize={13}
/>
</Select.Label>
<Select.Item value="easy" textValue="Easy" />
<Select.Item value="normal" textValue="Normal" />
<Select.Separator />
<Select.Item value="hard" textValue="Hard" />
<Select.Item value="nightmare" textValue="Nightmare" disabled />
</Select.Group>
</frame>
</Select.Content>
</Select.Portal>
</Select.Root>
);
}

How it behaves

Open state

Select.Root is controllable. Pass open and onOpenChange to control it, or defaultOpen to run uncontrolled (defaults to false). Select.Trigger toggles open on activation and on Return/Space, and selecting an item closes it. When the root is disabled, the trigger cannot open the content.

Value and selection

The selected value is a single string. Select.Root is controllable via value/onValueChange, or uncontrolled via defaultValue. Each Select.Item registers itself with the root on mount — recording its value, textValue, disabled state, and document order — so the root always knows the full ordered set of options. Activating an item (click, Return, or Space) sets the value and closes the content. Selecting a disabled item is ignored, and if the current value ever points at a disabled or missing item, the root falls back to the first enabled item.

The value label

Select.Value reads the current value from context and renders the matching item’s textValue (falling back to the raw value, then to placeholder when nothing is selected). Because the label resolves through the item registry, it stays correct without you wiring text manually.

Positioning

Select.Content is positioned with popper, anchored to the trigger. Control the side with placement ("top" | "bottom" | "left" | "right", defaulting to "bottom"), nudge it with sideOffset and alignOffset, and keep it inside the viewport with collisionPadding. The content is measured and flipped automatically when it would collide with an edge.

Dismissal

Select.Content participates in dismissable-layer behavior in non-modal mode: interaction behind it is not blocked, but an outside press closes it. Use onPointerDownOutside and onInteractOutside to observe those interactions before the content dismisses.

Motion and presence

Select.Content runs a default popper entrance/exit recipe — a canvas-group reveal by default, or a transform-based reveal when asChild is set. Override it with transition, and pass forceMount to keep the content mounted through its exit animation.

API reference

Select.Root

Prop Type Description
value string Controlled selected value. Pair with onValueChange.
defaultValue string Initial value for uncontrolled usage.
onValueChange (value: string) => void Called when the selected value changes.
open boolean Controlled open state. Pair with onOpenChange.
defaultOpen boolean Initial open state for uncontrolled usage. Defaults to false.
onOpenChange (open: boolean) => void Called when the open state changes.
disabled boolean Disables the whole select: the trigger cannot open and values cannot change. Defaults to false.
required boolean Marks the select as required; surfaced through context for consumer use. Defaults to false.
children React.ReactNode The select parts.

Select.Trigger

Prop Type Description
asChild boolean Merge trigger behavior onto the single child element instead of rendering the default textbutton.
disabled boolean Prevents this trigger from opening the select, in addition to the root's disabled state. Defaults to false.
children React.ReactElement The element to render. Required when asChild is set; otherwise rendered inside the default button (e.g. a Select.Value).

Select.Value

Prop Type Description
asChild boolean Merge the resolved text onto the single child element instead of rendering the default textlabel.
placeholder string Text shown when no value is selected. Defaults to an empty string.
children React.ReactElement The element to render. Required when asChild is set.

Select.Portal

Prop Type Description
container BasePlayerGui Target PlayerGui to render the content into. Defaults to the surrounding portal context's container.
displayOrderBase number Base DisplayOrder for the generated ScreenGui, used to order it against other layers. Defaults to the surrounding portal context's value.
children React.ReactNode The content part.

Select.Content

Prop Type Description
placement "top" | "bottom" | "left" | "right" Preferred side to anchor the content against the trigger. Defaults to "bottom".
sideOffset number Gap between the trigger and the content along the placement axis.
alignOffset number Offset along the cross axis from the aligned edge.
collisionPadding number Minimum distance to keep from the viewport edges when repositioning.
transition PresenceMotionConfig Overrides the default popper reveal/exit recipe.
forceMount boolean Keeps the content mounted while exit motion runs.
asChild boolean Render onto the single child element instead of the default canvasgroup; also switches the default recipe to a transform-based reveal.
onPointerDownOutside (event: LayerInteractEvent) => void Called when a pointer press occurs outside the content, before dismissal.
onInteractOutside (event: LayerInteractEvent) => void Called for any other outside interaction, before dismissal.
children React.ReactNode The list surface contents.

Select.Item

Prop Type Description
value * string The value this item selects when activated.
textValue string Text used for the value label and the default item button. Defaults to value.
disabled boolean Prevents selection and removes the item from value resolution. Defaults to false.
asChild boolean Merge item behavior onto the single child element instead of rendering the default textbutton.
children React.ReactElement The element to render. Required when asChild is set.

Select.Group

Prop Type Description
asChild boolean Merge onto the single child element instead of rendering the default frame container.
children React.ReactElement The grouped items (and optional label).

Select.Label

Prop Type Description
asChild boolean Merge onto the single child element instead of rendering the default textlabel.
children React.ReactElement The label element to render. Required when asChild is set.

Select.Separator

Prop Type Description
asChild boolean Merge onto the single child element instead of rendering the default 1px divider frame.
children React.ReactElement The divider element to render. Required when asChild is set.