@lattice-ui/combobox Part of the stable direction toward v1.0.
What It Is For
Use Combobox when the user needs to type into a field and still commit to one option from a registered list.
It sits between Select and TextField: the package owns list filtering, option registration, and value/input synchronization, while you own the rendered UI.
import { Combobox } from "@lattice-ui/combobox";
import { PortalProvider } from "@lattice-ui/layer";
import React from "@rbxts/react";
type Props = {
playerGui: PlayerGui;
};
function ComboboxDemo() {
const [open, setOpen] = React.useState(false);
const [value, setValue] = React.useState("alpha");
return (
<Combobox.Root
onOpenChange={setOpen}
onValueChange={setValue}
value={value}
>
<frame BackgroundTransparency={1} Size={UDim2.fromOffset(360, 220)}>
<Combobox.Trigger asChild>
<textbutton
AutoButtonColor={false}
BackgroundColor3={Color3.fromRGB(34, 41, 54)}
BorderSizePixel={0}
Size={UDim2.fromOffset(320, 40)}
Text=""
>
<uicorner CornerRadius={new UDim(0, 8)} />
<Combobox.Value asChild placeholder="Select option">
<textlabel
BackgroundTransparency={1}
Position={UDim2.fromOffset(12, 0)}
Size={UDim2.fromOffset(280, 40)}
TextColor3={Color3.fromRGB(236, 240, 248)}
TextSize={14}
TextXAlignment={Enum.TextXAlignment.Left}
/>
</Combobox.Value>
</textbutton>
</Combobox.Trigger>
<Combobox.Input asChild placeholder="Type alpha, beta, gamma">
<textbox
BackgroundColor3={Color3.fromRGB(22, 28, 39)}
BorderSizePixel={0}
Position={UDim2.fromOffset(0, 52)}
Size={UDim2.fromOffset(320, 36)}
TextColor3={Color3.fromRGB(236, 240, 248)}
TextSize={14}
TextXAlignment={Enum.TextXAlignment.Left}
>
<uicorner CornerRadius={new UDim(0, 8)} />
<uipadding
PaddingLeft={new UDim(0, 10)}
PaddingRight={new UDim(0, 10)}
/>
</textbox>
</Combobox.Input>
<Combobox.Portal>
<Combobox.Content
asChild
offset={new Vector2(0, 8)}
placement="bottom"
>
<frame
BackgroundColor3={Color3.fromRGB(22, 28, 39)}
BorderSizePixel={0}
Size={UDim2.fromOffset(320, 118)}
>
<uicorner CornerRadius={new UDim(0, 8)} />
<uipadding
PaddingLeft={new UDim(0, 8)}
PaddingRight={new UDim(0, 8)}
PaddingTop={new UDim(0, 8)}
PaddingBottom={new UDim(0, 8)}
/>
<uilistlayout
FillDirection={Enum.FillDirection.Vertical}
Padding={new UDim(0, 4)}
/>
{[
["alpha", "Alpha"],
["beta", "Beta"],
["gamma", "Gamma"],
].map(([itemValue, label]) => (
<Combobox.Item
asChild
key={itemValue}
textValue={label}
value={itemValue}
>
<textbutton
AutoButtonColor={false}
BackgroundColor3={Color3.fromRGB(34, 41, 54)}
BorderSizePixel={0}
Size={UDim2.fromOffset(304, 30)}
Text={label}
TextColor3={Color3.fromRGB(236, 240, 248)}
TextSize={14}
TextXAlignment={Enum.TextXAlignment.Left}
>
<uicorner CornerRadius={new UDim(0, 6)} />
<uipadding PaddingLeft={new UDim(0, 10)} />
</textbutton>
</Combobox.Item>
))}
</frame>
</Combobox.Content>
</Combobox.Portal>
<textlabel
BackgroundTransparency={1}
Position={UDim2.fromOffset(0, 104)}
Size={UDim2.fromOffset(320, 20)}
Text={`open=${tostring(open)} value=${value}`}
TextColor3={Color3.fromRGB(172, 181, 196)}
TextSize={13}
TextXAlignment={Enum.TextXAlignment.Left}
/>
</frame>
</Combobox.Root>
);
}
export function ComboboxExample(props: Props) {
return (
<PortalProvider container={props.playerGui}>
<ComboboxDemo />
</PortalProvider>
);
} Live demo is experimental and may contain bugs.
Install
Global CLI command: lattice add combobox
Monorepo local script
Use your package manager wrapper when running the local lattice command.
pnpm lattice add comboboxPublic Exports
-
Combobox -
Combobox.Root -
Combobox.Trigger -
Combobox.Input -
Combobox.Value -
Combobox.Portal -
Combobox.Content -
Combobox.Item -
Combobox.Group -
Combobox.Label -
Combobox.Separator -
defaultComboboxFilter -
filterComboboxOptions -
resolveComboboxInputValue -
resolveForcedComboboxValue
State Model
Combobox.Rootcan control three values independently: selectedvalue, typedinputValue, andopen.- When the list closes, the input is synchronized back to the selected option text.
- Opening logic, filtering, and item registration all live in the root context, and disabled or read-only roots reject writes.
Key API
Combobox.Root
Use value, inputValue, open, and their default/onChange pairs when integrating with external form or search state.
Combobox.Input
This is the typed query surface; placeholder, disabled, and readOnly are the key integration points.
Combobox.Content
Use placement, offset, padding, and outside-interaction callbacks for overlay positioning and dismissal behavior.
filterFn
Override filterFn when case-insensitive substring matching is not enough for your search rules.
Composition Patterns
Searchable settings picker
Compose Trigger, Value, and Input together so the currently selected value and the live filter query are both visible.
Strict option selection
Use Combobox.Item for every valid value and let the package re-sync the input text to the chosen option when the list closes.
Cautions / Limits
- This package is still a selection control, not a freeform text field; the stable committed value comes from registered items.
- Because content is portal-based, use
PortalProviderwhen you need predictable overlay mounting and display order.
Decision Guides
- Choosing between controls
Compare Select vs Combobox vs Radio Group before committing to typed filtering.
- Overlay and state pitfalls
Review PortalProvider, focus, disabled items, and controlled/uncontrolled transitions.