npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@xsolla/xui-select

v0.216.0

Published

A cross-platform React select component for choosing from a list of predefined options with dropdown menu. Works on both React (web) and React Native. <!-- BEGIN:xui-mcp-instructions:select --> A single-value dropdown input that displays the current selec

Readme

Select

A cross-platform React select component for choosing from a list of predefined options with dropdown menu. Works on both React (web) and React Native.

A single-value dropdown input that displays the current selection and opens a ContextMenu panel when clicked. Supports five sizes, an optional leading icon, a clear button, and an icon-only mode. The dropdown panel is powered by ContextMenu — see its documentation for option list composition, search, and behaviour.

When to use

  • When the user must pick exactly one value from a fixed, mutually exclusive list — country, currency, status, category
  • When the list of options is known, finite, and does not require typing to navigate
  • When vertical space is limited and showing every option (as with radio buttons) would be too large
  • When the list is too long to show inline — use Select instead of radio buttons when there are more than 5–6 options
  • In forms, filter bars, and settings panels where a compact single-line field is needed
  • As part of a larger form alongside other controls

When not to use

  • When the user can choose more than one value — use Multiselect instead
  • When the list is long or the user benefits from typing to filter — use Autocomplete / Combobox instead
  • When the value is freeform text the user must type — use TextArea or Input instead
  • When there are only 2–3 options that should always stay visible — use Radio buttons or ToggleButtonGroup instead
  • When the choice is a simple on/off — use a Switch or Checkbox instead
  • When the value is time or date — use InputTime or DatePicker instead

Content guidelines

  • Label text (placeholder) — use a concise instructional phrase: "Select language", "Choose currency", "Pick a category". Avoid generic placeholders like "Select…" alone when a more specific hint is possible.
  • Option labels — label each option clearly and concisely — one short phrase, no trailing punctuation. Use parallel structure across all options in the same list (all nouns, or all short phrases). Avoid negative options (e.g. "Do not notify me") — rephrase positively.
  • Option order — list options in a logical order: most common first, alphabetical, or by value. Never randomise.
  • Option descriptions — use the ContextMenu Description slot to add clarifying context when the label alone is not sufficient — e.g. label "Admin", description "Full access to all settings".
  • Selected value display — show the full option label as selected. If the label is very long and the field is narrow, truncate with ellipsis and show the full label in a tooltip on hover.
  • Field label — always provide a visible label above the Select via FieldGroup. Do not rely on the placeholder text alone as the field identifier.
  • Error messages — be specific:
  • "Please select a language" (required, empty on submit)
  • "Selected option is no longer available" (stale value)

Icon choice — use a left icon that represents the category being selected, not a generic dropdown indicator. The chevron already signals "this opens a list."

Behaviour guidelines

  • Single selection — only one value can be selected at a time. Selecting a new option automatically replaces the previous selection. The value cannot be "unselected" by clicking it again — to clear the selection, provide an explicit clear button (Clear button=true) or a "None" / "No selection" option in the list.
  • Opening — clicking the field or pressing Enter / Space / ↓ when focused opens the ContextMenu. The field transitions to State=Focus; the chevron rotates to point upward.
  • Closing — the ContextMenu closes on: selecting an option, pressing Escape, clicking outside, or pressing Tab past the last option. On close, focus returns to the Select field.
  • Selection — when the user selects an option from the ContextMenu, the field updates to Filled=True and the label shows the selected option's text. The dropdown closes immediately.
  • Clear — clicking the clear button (✕) clears the selection (Filled=False), closes the dropdown if open, and returns focus to the field.
  • Validation — validate on blur (when the field loses focus without a selection) or on form submit. Switch to State=Error with a specific message if a required field has no value. Clear the error when a value is selected.
  • Placeholder — show placeholder text in Filled=False using Label text. The placeholder should describe what to select (e.g. "Select a country", "Choose language") rather than repeating the field label.
  • Disabled — State=Disable prevents opening the dropdown. If a value is present, it remains visible in muted style. Provide a tooltip or nearby explanation if the reason for disabling is not obvious.
  • ContextMenu size matching — the ContextMenu panel width should match or exceed the Select field width. Never open a narrower ContextMenu than the trigger field — options would be cut off.

Accessibility

  • Select must render as a (or a with role="combobox") with aria-haspopup="listbox" and aria-expanded="true" / "false" reflecting the open state.
  • The field must have an accessible name via (from FieldGroup), aria-label, or aria-labelledby.
  • When Icon only=True, aria-label on the field is mandatory — the label is hidden and the icon alone conveys no text to screen readers.
  • When Filled=True, reflect the selected value in aria-label or ensure it is part of the field's visible text that screen readers can read: e.g. aria-label="Language: English".
  • The clear button must have aria-label="Clear selection" or aria-label="Clear [field name]".
  • When State=Error, the error message must be linked via aria-describedby and placed in an aria-live="polite" region.
  • Keyboard navigation:
  • Enter / Space / ↓ opens the dropdown
  • ↑ / ↓ navigate options inside the ContextMenu
  • Enter selects the focused option
  • Escape closes without selecting; focus returns to the field
  • Tab closes and moves to the next focusable element

The left icon must have aria-hidden="true" when a label is also visible.

Installation

npm install @xsolla/xui-select

Demo

Basic Select

import * as React from "react";
import { Select } from "@xsolla/xui-select";

export default function BasicSelect() {
  const [value, setValue] = React.useState("");

  return (
    <Select
      value={value}
      onChange={setValue}
      options={["Option 1", "Option 2", "Option 3"]}
      placeholder="Select an option"
    />
  );
}

Select with Label

import * as React from "react";
import { Select } from "@xsolla/xui-select";

export default function LabeledSelect() {
  const [country, setCountry] = React.useState("");

  return (
    <Select
      label="Country"
      value={country}
      onChange={setCountry}
      options={[
        "United States",
        "Canada",
        "United Kingdom",
        "Germany",
        "France",
      ]}
      placeholder="Select a country"
    />
  );
}

Select with Object Options

import * as React from "react";
import { Select } from "@xsolla/xui-select";

export default function ObjectOptionsSelect() {
  const [status, setStatus] = React.useState("");

  const options = [
    { label: "Active", value: "active" },
    { label: "Pending", value: "pending" },
    { label: "Inactive", value: "inactive" },
  ];

  return (
    <Select
      label="Status"
      value={status}
      onChange={setStatus}
      options={options}
      placeholder="Select status"
    />
  );
}

Select Sizes

import * as React from "react";
import { Select } from "@xsolla/xui-select";

export default function SelectSizes() {
  const options = ["Small", "Medium", "Large"];

  return (
    <div style={{ display: "flex", flexDirection: "column", gap: 16 }}>
      <Select size="xs" options={options} placeholder="Extra Small" />
      <Select size="sm" options={options} placeholder="Small" />
      <Select size="md" options={options} placeholder="Medium (default)" />
      <Select size="lg" options={options} placeholder="Large" />
      <Select size="xl" options={options} placeholder="Extra Large" />
    </div>
  );
}

Select with Icons

import * as React from "react";
import { Select } from "@xsolla/xui-select";
import { Globe } from "@xsolla/xui-icons-base";

export default function SelectWithIcons() {
  return (
    <Select
      iconLeft={<Globe />}
      options={["English", "Spanish", "French", "German"]}
      placeholder="Select language"
    />
  );
}

Anatomy

Import the component and use it directly:

import { Select } from "@xsolla/xui-select";

<Select
  label="Field Label" // Optional label above select
  value={value} // Controlled selected value
  onChange={setValue} // Value change handler
  options={["A", "B", "C"]} // Array of options (strings or objects)
  placeholder="Select..." // Placeholder text
  iconLeft={<Icon />} // Optional left icon
  iconRight={<Icon />} // Optional right icon (default: ChevronDown)
  filled // Whether to show filled background
  state="error" // State: "default" | "disable" | "error"
  errorMessage="Error message" // Error message (shown when state="error")
/>;

Examples

Error State

import * as React from "react";
import { Select } from "@xsolla/xui-select";

export default function ErrorSelect() {
  return (
    <Select
      label="Required Field"
      state="error"
      errorMessage="Please select an option"
      options={["Option 1", "Option 2", "Option 3"]}
      placeholder="Select an option"
    />
  );
}

Disabled Select

import * as React from "react";
import { Select } from "@xsolla/xui-select";

export default function DisabledSelect() {
  return (
    <Select
      label="Disabled Field"
      value="Option 1"
      state="disable"
      options={["Option 1", "Option 2", "Option 3"]}
    />
  );
}

Unfilled/Transparent Background

import * as React from "react";
import { Select } from "@xsolla/xui-select";

export default function UnfilledSelect() {
  return (
    <Select
      filled={false}
      options={["Option 1", "Option 2", "Option 3"]}
      placeholder="Transparent background"
    />
  );
}

Icon Only Select

import * as React from "react";
import { Select } from "@xsolla/xui-select";
import { Settings } from "@xsolla/xui-icons";

export default function IconOnlySelect() {
  return (
    <Select
      iconOnly
      iconLeft={<Settings />}
      options={["Settings", "Preferences", "Advanced"]}
    />
  );
}

Full Width Select

import * as React from "react";
import { Select } from "@xsolla/xui-select";

export default function FullWidthSelect() {
  return (
    <div style={{ display: "flex", gap: 16 }}>
      <Select
        fullWidth
        options={["Option 1", "Option 2", "Option 3"]}
        placeholder="Stretches to fill container"
      />
      <Select
        fullWidth={false}
        options={["Option 1", "Option 2", "Option 3"]}
        placeholder="Intrinsic width"
      />
    </div>
  );
}

Custom Popover Width

By default the dropdown popover matches the trigger width. Use popoverWidth to size the popover independently — handy when option labels are wider than the field. Pass a number (pixels) or any CSS width string.

import * as React from "react";
import { Select } from "@xsolla/xui-select";

export default function CustomPopoverWidth() {
  return (
    <div style={{ width: 200 }}>
      <Select
        popoverWidth={320}
        options={[
          "Account overview and billing details",
          "Subscription management",
          "Payment history and transactions",
        ]}
        placeholder="Custom popover width"
      />
    </div>
  );
}

Form with Select

import * as React from "react";
import { Select } from "@xsolla/xui-select";
import { Input } from "@xsolla/xui-input";
import { Button } from "@xsolla/xui-button";

export default function FormWithSelect() {
  const [form, setForm] = React.useState({
    name: "",
    category: "",
    priority: "",
  });

  return (
    <div
      style={{ display: "flex", flexDirection: "column", gap: 16, width: 300 }}
    >
      <Input
        label="Task Name"
        value={form.name}
        onChangeText={(name) => setForm((prev) => ({ ...prev, name }))}
        placeholder="Enter task name"
      />
      <Select
        label="Category"
        value={form.category}
        onChange={(category) => setForm((prev) => ({ ...prev, category }))}
        options={["Development", "Design", "Marketing", "Sales"]}
        placeholder="Select category"
      />
      <Select
        label="Priority"
        value={form.priority}
        onChange={(priority) => setForm((prev) => ({ ...prev, priority }))}
        options={[
          { label: "High", value: "high" },
          { label: "Medium", value: "medium" },
          { label: "Low", value: "low" },
        ]}
        placeholder="Select priority"
      />
      <Button>Create Task</Button>
    </div>
  );
}

Dropdown Positioning

By default the dropdown always opens on the side (default "bottom"). Pass autoFlip to let it flip to the opposite side when there isn't enough viewport space.

import * as React from "react";
import { Select } from "@xsolla/xui-select";

const options = ["Option 1", "Option 2", "Option 3"];

// Always opens below (default — no surprises for existing layouts)
<Select options={options} placeholder="Default" />

// Always opens above
<Select side="top" options={options} placeholder="Opens above" />

// Opens below, flips above when near the bottom of the viewport
<Select autoFlip options={options} placeholder="Auto-flip enabled" />

// Opens above, flips below when near the top of the viewport
<Select side="top" autoFlip options={options} placeholder="Prefers top, flips if needed" />

API Reference

Select

The main select component. Renders a button trigger with dropdown menu.

Select Props:

| Prop | Type | Default | Description | | :---------------- | :-------------------------------------------------------- | :---------------- | :------------------------------------------------------------------------------------------------------------ | | testID | string | — | Test ID for testing frameworks. On web this renders as data-testid; on React Native it renders as testID. | | value | string | - | The controlled selected value. | | placeholder | string | "Select" | Placeholder text when no value selected. | | onPress | () => void | - | Callback when select trigger is pressed. | | size | "xl" \| "lg" \| "md" \| "sm" \| "xs" | "md" | The size of the select. | | state | "default" \| "hover" \| "focus" \| "disable" \| "error" | "default" | The visual state of the select. | | label | string | - | Label text displayed above the select. | | disabled | boolean | false | Whether the select is disabled. Also triggered when state="disable". | | errorMessage | string | - | Error message displayed below the select (also sets error styling). | | iconLeft | ReactNode | - | Icon displayed on the left side. | | iconRight | ReactNode | <ChevronDown /> | Icon displayed on the right side. | | filled | boolean | true | Whether to show filled background. | | iconOnly | boolean | false | Whether to show only icon without text. | | options | (string \| SelectOption)[] | [] | Array of options to display. | | onChange | (value: string) => void | - | Callback when value changes. | | searchable | boolean | false | Whether to show a search input in the dropdown. | | searchPlaceholder | string | "Search" | Placeholder text for the search input. | | noOptionsMessage | string | "No results" | Message shown when no options match the search. | | clearable | boolean | false | Show a clear button to reset the selected value (field variant only). | | onClear | () => void | - | Callback when the clear button is pressed. | | maxHeight | number | 300 | Maximum height of the dropdown in pixels. | | popoverWidth | number \| string | "100%" | Width of the dropdown popover/menu. Defaults to the trigger width; pass a number (px) or CSS width string to size it independently of the trigger. | | fullWidth | boolean | true | Whether the select should stretch to fill the full width of its container. | | overlayThemeMode | ThemeMode | themeMode | Theme mode for the dropdown overlay. | | overlayThemeProductContext | ProductContext | themeProductContext | Product context for the dropdown overlay. | | side | "top" \| "bottom" | "bottom" | Preferred side for the dropdown. | | autoFlip | boolean | false | When true, the dropdown flips to the opposite side when there isn't enough viewport space on the preferred side. |

SelectOption Type:

interface SelectOption {
  label: string; // Display text
  value: any; // Value to be selected
  disabled?: boolean; // Whether this option is disabled
}

React Native Notes

The Select component works on React Native with the following differences:

| Feature | Web | React Native | | --------------------- | ----------------------------- | ----------------------------------- | | Searchable dropdown | Supported (searchable prop) | Not available — prop is ignored | | Dropdown shadow | CSS box-shadow | Android elevation: 4 | | Scroll behavior | overflowY: auto | Native ScrollView | | Cursor styles | pointer / not-allowed | Ignored | | Click-outside dismiss | document.addEventListener | Not active (guarded by isNative) |

Accessibility

  • Trigger has role="combobox", aria-haspopup="listbox", and aria-expanded (updates on open/close)
  • Trigger links to its label via aria-labelledby when the label prop is set
  • Dropdown has role="listbox"; each option has role="option" and aria-selected
  • Disabled trigger and disabled options expose aria-disabled="true"
  • Search input has aria-label matching searchPlaceholder
  • Selected item auto-scrolls into view when the dropdown opens (web only)
  • Disabled state properly communicated to assistive technology