Docs
Select

Select

A native (<select>), hydration-free select. It renders correct, functional markup with JavaScript turned off and emits native change events — which is why it works in pure static SSG (renderToStaticMarkup, zero application JS), where InputSelect (based on react-select) does not.

The native <select> is encapsulated inside the component: you pass typed options, never raw <option>/<select>.

When to use

✅ Use when…🚫 Avoid when…
  • The surface has no client runtime (static SSG, SSR-inert pages).
  • A simple native select (short list) is enough.
  • You want a control driven by a vanilla change listener on the document.
  • You need search, multi-selection, async loading, or custom option rendering — use InputSelect.
  • It is a single immediate-effect boolean — use Switch.
  • It is an exclusive choice displayed inline as a group — use Radio.

Example

<Select
  name="theme"
  aria-label="Theme"
  defaultValue="light"
  options={[
    { value: 'light', label: 'Light' },
    { value: 'dark', label: 'Dark' },
    { value: 'grayscale', label: 'Grayscale' },
  ]}
/>

Sizes

<Select size="small" options={options} />
<Select size="medium" options={options} />
<Select size="large" options={options} />

small is the smallest step. There is no micro size — in Apollion micro is a fontSize token, not a control size.

Border radius

borderRadius accepts the theme's radius scale. straight keeps the corners square; circular, fully rounded (pill).

<Select borderRadius="straight" options={options} />
<Select borderRadius="xs" options={options} />
<Select borderRadius="circular" options={options} />

Accessibility & SSR

  • Always provide an aria-label (or an associated <label>), since the control does not render a visible label of its own.
  • Keyboard focus shows a visible :focus-visible outline.
  • Native option colors are forced (dark text on the lightest surface) so the system popup remains readable on dark surfaces.
  • No useEffect, no hydration: the rendered markup is fully functional with JavaScript turned off.

Interactive Demo

loading

Properties

Prop
Type
Default
Description
options *
SelectOption[]
Typed options rendered as native `<option>` elements.
size
"small" | "medium" | "large"
medium
Control size token (height + horizontal padding).
variant
"default" | "success" | "error"
default
Visual variant reflecting validation state, consistent with the other form controls (`Input`, `InputSelect`).

In addition to the props above, every component accepts the layout props (spacing, color, flex/grid, sizing, border) — not repeated here.

Theming (theme.component.select)

The size scale is a component token: theme.component.select.size.{small,medium,large}, each carrying height (px) and px (spacing alias). Override one field of one size without forking the component:

createTheme({
  component: {
    select: {
      size: {
        medium: { height: 44 },
      },
    },
  },
});

See also