A controlled option-picker component for single-choice selection.
Quickstart
Use options as data source and listen to value-change.
<rtgl-view d="v" g="sm" w="320">
<rtgl-text c="mu">Fruit</rtgl-text>
<rtgl-select id="fruit-select" placeholder="Choose fruit" w="f"></rtgl-select>
</rtgl-view>
<script>
const select = document.getElementById("fruit-select");
select.options = [
{ value: "apple", label: "Apple" },
{ value: "banana", label: "Banana" },
{ value: "orange", label: "Orange" },
];
select.addEventListener("value-change", (e) => {
console.log(e.detail.value);
});
select.render();
</script>
Core Decisions
Choose Data Binding Style
| Intent | Recommended |
|---|---|
| Define options list | set options as a property |
| Control selected item initially | set selectedValue property |
| Placeholder text | placeholder attribute or property |
Choose Clear Behavior
| Intent | Recommended |
|---|---|
| Allow clear icon | default |
| Lock selection until another option is chosen | no-clear |
Choose Add-Option UX
| Intent | Recommended |
|---|---|
| Standard select menu | omit addOption |
| Add action row at bottom | set addOption property |
Choose Search Behavior
| Intent | Recommended |
|---|---|
| Small option list | omit searchable |
| Filter long option lists by label | searchable |
API
| Name | Attribute / Property | Type | Default |
|---|---|---|---|
| Placeholder | placeholder |
string | Select an option |
| Selected Value | selected-value / selectedValue |
any | - |
| Options | options (property) |
{ type?: "item" | "section" | "separator", label?: string, value?: any, icon?: string, shortcut?: string, suffixText?: string, testId?: string }[] |
[] |
| No Clear | no-clear |
boolean | off |
| Add Option | addOption (property) |
{ label?: string } |
- |
| Disabled | disabled |
boolean | false |
| Searchable | searchable |
boolean | false |
| Search Placeholder | search-placeholder / searchPlaceholder |
string | Search options |
| Empty Search Label | empty-search-label / emptySearchLabel |
string | No matching options |
| Trigger Width | w |
number, %, xs-xl, f, CSS length/value |
content-based |
| Trigger Margin | m, mt, mr, mb, ml, mv, mh |
xs, sm, md, lg, xl |
- |
Events
| Event | Detail | Description |
|---|---|---|
value-change |
{ value, label, index, item } |
Fires when selection changes or is cleared |
add-option-click |
{} |
Fires when add-option row is clicked |
Options
Define selectable entries through the options property.
Behavior & precedence
type: "item"is the default row type.- Item rows should include
labelandvalue. type: "section"renders a non-selectable header row.type: "separator"renders a non-selectable divider row.valuecan be primitive or object.iconrenders a leading icon in the menu and selected trigger.shortcutrenders right-side trailing text and takes precedence oversuffixText.suffixTextrenders right-side trailing text whenshortcutis not provided.testIdis optional and useful for testing.- When
searchableis enabled, the popover filters item rows by case-insensitive label match.
<rtgl-select id="position-select" placeholder="Select position"></rtgl-select>
<script>
const select = document.getElementById("position-select");
select.options = [
{ value: { x: 0, y: 0 }, label: "Top Left" },
{ value: { x: 0.5, y: 0.5 }, label: "Center" },
{ value: { x: 1, y: 1 }, label: "Bottom Right" },
];
select.render();
</script>
<rtgl-select id="command-select" placeholder="Choose command"></rtgl-select>
<script>
const select = document.getElementById("command-select");
select.options = [
{ type: "section", label: "Clipboard" },
{ value: "copy", label: "Copy", icon: "text", shortcut: "Cmd+C" },
{ value: "paste", label: "Paste", icon: "threeDots", shortcut: "Cmd+V" },
{ type: "separator" },
{ type: "section", label: "Inspector" },
{ value: "info", label: "Details", icon: "info", suffixText: "Beta" },
];
select.selectedValue = "copy";
select.render();
</script>
Searchable
Use searchable for longer option lists. Search filters by option label; selected trigger display still resolves from the full options list.
<rtgl-select id="searchable-role" searchable search-placeholder="Search roles"></rtgl-select>
<script>
const select = document.getElementById("searchable-role");
select.options = [
{ type: "section", label: "Product" },
{ value: "designer", label: "Designer" },
{ value: "researcher", label: "Researcher" },
{ type: "separator" },
{ type: "section", label: "Engineering" },
{ value: "frontend", label: "Frontend Engineer" },
{ value: "backend", label: "Backend Engineer" },
];
select.render();
</script>
Selected Value
Set initial/current selection via selectedValue or selected-value.
Behavior & precedence
- If
selectedValuematches an option, that label is shown. - Matching options also surface their
iconand trailing text in the trigger. - If it does not match, placeholder label is shown.
- Clearing emits
value-changewithvalue: undefined.
<rtgl-select id="status-select" selected-value="active"></rtgl-select>
<script>
const select = document.getElementById("status-select");
select.options = [
{ value: "active", label: "Active" },
{ value: "paused", label: "Paused" },
];
select.render();
</script>
No Clear
Use no-clear when clear affordance must be hidden.
<rtgl-view d="v" g="md" w="320">
<rtgl-select id="clear-on"></rtgl-select>
<rtgl-select id="clear-off" no-clear></rtgl-select>
</rtgl-view>
<script>
const options = [
{ value: "1", label: "Option 1" },
{ value: "2", label: "Option 2" },
];
const clearOn = document.getElementById("clear-on");
clearOn.selectedValue = "2";
clearOn.options = options;
clearOn.render();
const clearOff = document.getElementById("clear-off");
clearOff.selectedValue = "2";
clearOff.options = options;
clearOff.render();
</script>
Add Option
Use addOption to render an extra action row at the bottom of the menu.
Behavior & precedence
addOption = {}shows a default add row.addOption = { label: "..." }customizes add row label.- Clicking that row emits
add-option-click.
<rtgl-select id="tag-select" placeholder="Select or add tag"></rtgl-select>
<script>
const select = document.getElementById("tag-select");
select.options = [
{ value: "bug", label: "Bug" },
{ value: "feature", label: "Feature" },
];
select.addOption = { label: "Add new tag" };
select.addEventListener("add-option-click", () => {
console.log("Add option clicked");
});
select.render();
</script>
Disabled
Use disabled to make the select non-interactive.
Behavior & precedence
- Trigger cannot be opened while disabled.
- Clear icon and add-option row are hidden while disabled.
- Programmatic value updates still work.
<rtgl-select id="disabled-select" disabled></rtgl-select>
<script>
const select = document.getElementById("disabled-select");
select.options = [
{ value: "1", label: "Option 1" },
{ value: "2", label: "Option 2" },
];
select.selectedValue = "2";
select.render();
</script>
Form Field
rtgl-form passes search props through to select fields.
- name: role
type: select
label: Role
searchable: true
searchPlaceholder: Search roles
options:
- label: Admin
value: admin
- label: Editor
value: editor
- label: Viewer
value: viewer
Events Example
<rtgl-select id="country-select" placeholder="Country"></rtgl-select>
<script>
const select = document.getElementById("country-select");
select.options = [
{ value: "us", label: "United States" },
{ value: "ca", label: "Canada" },
];
select.addEventListener("value-change", (e) => {
console.log("value-change", e.detail);
});
select.addEventListener("add-option-click", () => {
console.log("add-option-click");
});
select.render();
</script>