Skip to main content

A select displays a collapsible list of options and allows a user to select one of them.

Import

import { Select } from "heroui-solid";

Usage

import { Label, ListBox, Select } from "heroui-solid"
import { For } from "solid-js"

const states = [
  { id: "florida", name: "Florida" },
  { id: "delaware", name: "Delaware" },
  { id: "california", name: "California" },
  { id: "texas", name: "Texas" },
  { id: "new-york", name: "New York" },
  { id: "washington", name: "Washington" }
]

export function Default() {
  return (
    <Select class="w-[256px]" placeholder="Select one">
      <Label>State</Label>
      <Select.Trigger>
        <Select.Value />
        <Select.Indicator />
      </Select.Trigger>
      <Select.Popover>
        <ListBox>
          <For each={states}>
            {(state) => (
              <ListBox.Item id={state.id} textValue={state.name}>
                {state.name}
                <ListBox.ItemIndicator />
              </ListBox.Item>
            )}
          </For>
        </ListBox>
      </Select.Popover>
    </Select>
  )
}

Anatomy

Import the Select component and access all parts using dot notation.

import { Select, Label, Description, ListBox } from "heroui-solid";

export default () => (
  <Select>
    <Label />
    <Select.Trigger>
      <Select.Value />
      <Select.Indicator />
    </Select.Trigger>
    <Description />
    <Select.Popover>
      <ListBox>
        <ListBox.Item>
          <ListBox.ItemIndicator />
        </ListBox.Item>
      </ListBox>
    </Select.Popover>
  </Select>
);

Multiple Select

<Select placeholder="Select countries" selectionMode="multiple">
  ...
</Select>

With Disabled Options

<Select disabledKeys={["cat", "kangaroo"]} placeholder="Select an animal">
  ...
</Select>

Custom Indicator

import { ChevronsExpandVertical } from "gravity-icons-solid";

<Select.Indicator class="size-3">
  <ChevronsExpandVertical />
</Select.Indicator>

With Description

Select your state of residence
import { Description, Label, ListBox, Select } from "heroui-solid"
import { For } from "solid-js"

const states = [
  { id: "florida", name: "Florida" },
  { id: "delaware", name: "Delaware" },
  { id: "california", name: "California" },
  { id: "texas", name: "Texas" },
  { id: "new-york", name: "New York" },
  { id: "washington", name: "Washington" }
]

export function WithDescription() {
  return (
    <Select class="w-[256px]" placeholder="Select one">
      <Label>State</Label>
      <Select.Trigger>
        <Select.Value />
        <Select.Indicator />
      </Select.Trigger>
      <Select.Popover>
        <ListBox>
          <For each={states}>
            {(state) => (
              <ListBox.Item id={state.id} textValue={state.name}>
                {state.name}
                <ListBox.ItemIndicator />
              </ListBox.Item>
            )}
          </For>
        </ListBox>
      </Select.Popover>
      <Description>Select your state of residence</Description>
    </Select>
  )
}

With Sections

import { Header, Label, ListBox, Select, Separator } from "heroui-solid"

export function WithSections() {
  return (
    <Select class="w-[256px]" placeholder="Select a country">
      <Label>Country</Label>
      <Select.Trigger>
        <Select.Value />
        <Select.Indicator />
      </Select.Trigger>
      <Select.Popover>
        <ListBox>
          <ListBox.Section>
            <Header>North America</Header>
            <ListBox.Item id="usa" textValue="United States">
              United States
              <ListBox.ItemIndicator />
            </ListBox.Item>
            <ListBox.Item id="canada" textValue="Canada">
              Canada
              <ListBox.ItemIndicator />
            </ListBox.Item>
            <ListBox.Item id="mexico" textValue="Mexico">
              Mexico
              <ListBox.ItemIndicator />
            </ListBox.Item>
          </ListBox.Section>
          <Separator />
          <ListBox.Section>
            <Header>Europe</Header>
            <ListBox.Item id="uk" textValue="United Kingdom">
              United Kingdom
              <ListBox.ItemIndicator />
            </ListBox.Item>
            <ListBox.Item id="france" textValue="France">
              France
              <ListBox.ItemIndicator />
            </ListBox.Item>
            <ListBox.Item id="germany" textValue="Germany">
              Germany
              <ListBox.ItemIndicator />
            </ListBox.Item>
            <ListBox.Item id="spain" textValue="Spain">
              Spain
              <ListBox.ItemIndicator />
            </ListBox.Item>
            <ListBox.Item id="italy" textValue="Italy">
              Italy
              <ListBox.ItemIndicator />
            </ListBox.Item>
          </ListBox.Section>
          <Separator />
          <ListBox.Section>
            <Header>Asia</Header>
            <ListBox.Item id="japan" textValue="Japan">
              Japan
              <ListBox.ItemIndicator />
            </ListBox.Item>
            <ListBox.Item id="china" textValue="China">
              China
              <ListBox.ItemIndicator />
            </ListBox.Item>
            <ListBox.Item id="india" textValue="India">
              India
              <ListBox.ItemIndicator />
            </ListBox.Item>
            <ListBox.Item id="south-korea" textValue="South Korea">
              South Korea
              <ListBox.ItemIndicator />
            </ListBox.Item>
          </ListBox.Section>
        </ListBox>
      </Select.Popover>
    </Select>
  )
}

Required

import { Button, FieldError, Form, Label, ListBox, Select } from "heroui-solid"

export function Required() {
  const onSubmit = (e: SubmitEvent) => {
    e.preventDefault()
    const formData = new FormData(e.currentTarget as HTMLFormElement)
    const data: Record<string, string> = {}

    // Convert FormData to plain object
    formData.forEach((value, key) => {
      data[key] = value.toString()
    })

    alert("Form submitted successfully!")
  }

  return (
    <Form class="flex w-[256px] flex-col gap-4" onSubmit={onSubmit}>
      <Select class="w-full" isRequired name="state" placeholder="Select one">
        <Label>State</Label>
        <Select.Trigger>
          <Select.Value />
          <Select.Indicator />
        </Select.Trigger>
        <Select.Popover>
          <ListBox>
            <ListBox.Item id="florida" textValue="Florida">
              Florida
              <ListBox.ItemIndicator />
            </ListBox.Item>
            <ListBox.Item id="delaware" textValue="Delaware">
              Delaware
              <ListBox.ItemIndicator />
            </ListBox.Item>
            <ListBox.Item id="california" textValue="California">
              California
              <ListBox.ItemIndicator />
            </ListBox.Item>
            <ListBox.Item id="texas" textValue="Texas">
              Texas
              <ListBox.ItemIndicator />
            </ListBox.Item>
            <ListBox.Item id="new-york" textValue="New York">
              New York
              <ListBox.ItemIndicator />
            </ListBox.Item>
            <ListBox.Item id="washington" textValue="Washington">
              Washington
              <ListBox.ItemIndicator />
            </ListBox.Item>
          </ListBox>
        </Select.Popover>
        <FieldError />
      </Select>
      <Select
        class="w-full"
        isRequired
        name="country"
        placeholder="Select a country"
      >
        <Label>Country</Label>
        <Select.Trigger>
          <Select.Value />
          <Select.Indicator />
        </Select.Trigger>
        <Select.Popover>
          <ListBox>
            <ListBox.Item id="usa" textValue="United States">
              United States
              <ListBox.ItemIndicator />
            </ListBox.Item>
            <ListBox.Item id="canada" textValue="Canada">
              Canada
              <ListBox.ItemIndicator />
            </ListBox.Item>
            <ListBox.Item id="mexico" textValue="Mexico">
              Mexico
              <ListBox.ItemIndicator />
            </ListBox.Item>
            <ListBox.Item id="uk" textValue="United Kingdom">
              United Kingdom
              <ListBox.ItemIndicator />
            </ListBox.Item>
            <ListBox.Item id="france" textValue="France">
              France
              <ListBox.ItemIndicator />
            </ListBox.Item>
            <ListBox.Item id="germany" textValue="Germany">
              Germany
              <ListBox.ItemIndicator />
            </ListBox.Item>
          </ListBox>
        </Select.Popover>
        <FieldError />
      </Select>
      <Button type="submit">Submit</Button>
    </Form>
  )
}

Controlled

Selected: California

import { Label, ListBox, Select } from "heroui-solid"
import { createSignal, For } from "solid-js"

const states = [
  { id: "california", name: "California" },
  { id: "texas", name: "Texas" },
  { id: "florida", name: "Florida" },
  { id: "new-york", name: "New York" },
  { id: "illinois", name: "Illinois" },
  { id: "pennsylvania", name: "Pennsylvania" }
]

export function Controlled() {
  const [state, setState] = createSignal<string | null>("california")
  const selectedState = () => states.find((s) => s.id === state())

  return (
    <div style={{ display: "flex", "flex-direction": "column", gap: "0.5rem" }}>
      <Select
        placeholder="Select a state"
        style={{ width: "256px" }}
        value={state()}
        onChange={(value) => setState(value as string | null)}
      >
        <Label>State (controlled)</Label>
        <Select.Trigger>
          <Select.Value />
          <Select.Indicator />
        </Select.Trigger>
        <Select.Popover>
          <ListBox>
            <For each={states}>
              {(s) => (
                <ListBox.Item id={s.id} textValue={s.name}>
                  {s.name}
                  <ListBox.ItemIndicator />
                </ListBox.Item>
              )}
            </For>
          </ListBox>
        </Select.Popover>
      </Select>
      <p style={{ "font-size": "0.875rem", color: "var(--muted)" }}>
        Selected: {selectedState()?.name || "None"}
      </p>
    </div>
  )
}

Controlled Multiple

Selected: california, texas

import { Label, ListBox, Select } from "heroui-solid"
import { createSignal, For } from "solid-js"

const states = [
  { id: "california", name: "California" },
  { id: "texas", name: "Texas" },
  { id: "florida", name: "Florida" },
  { id: "new-york", name: "New York" },
  { id: "illinois", name: "Illinois" },
  { id: "pennsylvania", name: "Pennsylvania" }
]

export function ControlledMultiple() {
  const [selected, setSelected] = createSignal<string[]>([
    "california",
    "texas"
  ])

  return (
    <div class="space-y-4">
      <Select
        class="w-[256px]"
        placeholder="Select states"
        selectionMode="multiple"
        value={selected()}
        onChange={(keys) => setSelected(keys as string[])}
      >
        <Label>States (controlled multiple)</Label>
        <Select.Trigger>
          <Select.Value />
          <Select.Indicator />
        </Select.Trigger>
        <Select.Popover>
          <ListBox selectionMode="multiple">
            <For each={states}>
              {(s) => (
                <ListBox.Item id={s.id} textValue={s.name}>
                  {s.name}
                  <ListBox.ItemIndicator />
                </ListBox.Item>
              )}
            </For>
          </ListBox>
        </Select.Popover>
      </Select>
      <p class="text-sm text-muted">
        Selected: {selected().length > 0 ? selected().join(", ") : "None"}
      </p>
    </div>
  )
}

Controlled Open State

Select is closed

import { Button, Label, ListBox, Select } from "heroui-solid"
import { createSignal, For } from "solid-js"

const states = [
  { id: "florida", name: "Florida" },
  { id: "delaware", name: "Delaware" },
  { id: "california", name: "California" },
  { id: "texas", name: "Texas" },
  { id: "new-york", name: "New York" },
  { id: "washington", name: "Washington" }
]

export function ControlledOpenState() {
  const [isOpen, setIsOpen] = createSignal(false)

  return (
    <div class="space-y-4">
      <Select
        class="w-[256px]"
        isOpen={isOpen()}
        placeholder="Select one"
        onOpenChange={setIsOpen}
      >
        <Label>State</Label>
        <Select.Trigger>
          <Select.Value />
          <Select.Indicator />
        </Select.Trigger>
        <Select.Popover>
          <ListBox>
            <For each={states}>
              {(state) => (
                <ListBox.Item id={state.id} textValue={state.name}>
                  {state.name}
                  <ListBox.ItemIndicator />
                </ListBox.Item>
              )}
            </For>
          </ListBox>
        </Select.Popover>
      </Select>
      <Button onClick={() => setIsOpen(!isOpen())}>
        {isOpen() ? "Close" : "Open"} Select
      </Button>
      <p class="text-sm text-muted">Select is {isOpen() ? "open" : "closed"}</p>
    </div>
  )
}

Variants

The Select component supports two visual variants:

  • primary (default) - Standard styling with shadow, suitable for most use cases
  • secondary - Lower emphasis variant without shadow, suitable for use in Surface components
import { Label, ListBox, Select } from "heroui-solid"
import { For } from "solid-js"

const options = [
  { id: "option1", name: "Option 1" },
  { id: "option2", name: "Option 2" }
]

export function Variants() {
  return (
    <div style={{ display: "flex", "flex-direction": "column", gap: "1rem" }}>
      <Select
        placeholder="Select one"
        style={{ width: "256px" }}
        variant="primary"
      >
        <Label>Primary variant</Label>
        <Select.Trigger>
          <Select.Value />
          <Select.Indicator />
        </Select.Trigger>
        <Select.Popover>
          <ListBox>
            <For each={options}>
              {(option) => (
                <ListBox.Item id={option.id} textValue={option.name}>
                  {option.name}
                  <ListBox.ItemIndicator />
                </ListBox.Item>
              )}
            </For>
          </ListBox>
        </Select.Popover>
      </Select>
      <Select
        placeholder="Select one"
        style={{ width: "256px" }}
        variant="secondary"
      >
        <Label>Secondary variant</Label>
        <Select.Trigger>
          <Select.Value />
          <Select.Indicator />
        </Select.Trigger>
        <Select.Popover>
          <ListBox>
            <For each={options}>
              {(option) => (
                <ListBox.Item id={option.id} textValue={option.name}>
                  {option.name}
                  <ListBox.ItemIndicator />
                </ListBox.Item>
              )}
            </For>
          </ListBox>
        </Select.Popover>
      </Select>
    </div>
  )
}

Full Width

<Select fullWidth placeholder="Select one">
  ...
</Select>

In Surface

When used inside a Surface component, use variant="secondary" to apply the lower emphasis variant suitable for surface backgrounds.

import {
  Button,
  FieldError,
  Form,
  Label,
  ListBox,
  Select,
  Surface
} from "heroui-solid"

export function OnSurface() {
  const onSubmit = (e: SubmitEvent) => {
    e.preventDefault()
    const formData = new FormData(e.currentTarget as HTMLFormElement)
    const data: Record<string, string> = {}

    // Convert FormData to plain object
    formData.forEach((value, key) => {
      data[key] = value.toString()
    })

    alert("Form submitted successfully!")
  }

  return (
    <Surface class="w-[320px] rounded-3xl p-6">
      <Form class="flex w-full flex-col gap-4" onSubmit={onSubmit}>
        <Select
          class="w-full"
          isRequired
          name="state"
          placeholder="Select one"
          variant="secondary"
        >
          <Label>State</Label>
          <Select.Trigger>
            <Select.Value />
            <Select.Indicator />
          </Select.Trigger>
          <Select.Popover>
            <ListBox>
              <ListBox.Item id="florida" textValue="Florida">
                Florida
                <ListBox.ItemIndicator />
              </ListBox.Item>
              <ListBox.Item id="delaware" textValue="Delaware">
                Delaware
                <ListBox.ItemIndicator />
              </ListBox.Item>
              <ListBox.Item id="california" textValue="California">
                California
                <ListBox.ItemIndicator />
              </ListBox.Item>
              <ListBox.Item id="texas" textValue="Texas">
                Texas
                <ListBox.ItemIndicator />
              </ListBox.Item>
              <ListBox.Item id="new-york" textValue="New York">
                New York
                <ListBox.ItemIndicator />
              </ListBox.Item>
              <ListBox.Item id="washington" textValue="Washington">
                Washington
                <ListBox.ItemIndicator />
              </ListBox.Item>
            </ListBox>
          </Select.Popover>
          <FieldError />
        </Select>
        <Select
          class="w-full"
          isRequired
          name="country"
          placeholder="Select a country"
          variant="secondary"
        >
          <Label>Country</Label>
          <Select.Trigger>
            <Select.Value />
            <Select.Indicator />
          </Select.Trigger>
          <Select.Popover>
            <ListBox>
              <ListBox.Item id="usa" textValue="United States">
                United States
                <ListBox.ItemIndicator />
              </ListBox.Item>
              <ListBox.Item id="canada" textValue="Canada">
                Canada
                <ListBox.ItemIndicator />
              </ListBox.Item>
              <ListBox.Item id="mexico" textValue="Mexico">
                Mexico
                <ListBox.ItemIndicator />
              </ListBox.Item>
              <ListBox.Item id="uk" textValue="United Kingdom">
                United Kingdom
                <ListBox.ItemIndicator />
              </ListBox.Item>
              <ListBox.Item id="france" textValue="France">
                France
                <ListBox.ItemIndicator />
              </ListBox.Item>
              <ListBox.Item id="germany" textValue="Germany">
                Germany
                <ListBox.ItemIndicator />
              </ListBox.Item>
            </ListBox>
          </Select.Popover>
          <FieldError />
        </Select>
        <Button type="submit">Submit</Button>
      </Form>
    </Surface>
  )
}

Custom Value

import {
  Avatar,
  AvatarFallback,
  AvatarImage,
  Description,
  Label,
  ListBox,
  Select
} from "heroui-solid"
import { For } from "solid-js"

const users = [
  {
    avatarUrl:
      "https://heroui-assets.nyc3.cdn.digitaloceanspaces.com/avatars/blue.jpg",
    email: "bob@heroui.com",
    fallback: "B",
    id: "1",
    name: "Bob"
  },
  {
    avatarUrl:
      "https://heroui-assets.nyc3.cdn.digitaloceanspaces.com/avatars/green.jpg",
    email: "fred@heroui.com",
    fallback: "F",
    id: "2",
    name: "Fred"
  },
  {
    avatarUrl:
      "https://heroui-assets.nyc3.cdn.digitaloceanspaces.com/avatars/purple.jpg",
    email: "martha@heroui.com",
    fallback: "M",
    id: "3",
    name: "Martha"
  },
  {
    avatarUrl:
      "https://heroui-assets.nyc3.cdn.digitaloceanspaces.com/avatars/red.jpg",
    email: "john@heroui.com",
    fallback: "J",
    id: "4",
    name: "John"
  },
  {
    avatarUrl:
      "https://heroui-assets.nyc3.cdn.digitaloceanspaces.com/avatars/orange.jpg",
    email: "jane@heroui.com",
    fallback: "J",
    id: "5",
    name: "Jane"
  }
]

export function CustomValue() {
  return (
    <Select class="w-[256px]" placeholder="Select a user">
      <Label>User</Label>
      <Select.Trigger>
        <Select.Value>
          {(state) => {
            const selectedOptions = state.selectedOptions()

            if (selectedOptions.length === 0) {
              return undefined
            }

            if (selectedOptions.length > 1) {
              return `${selectedOptions.length} users selected`
            }

            const selectedItem = users.find(
              (user) => user.id === selectedOptions[0]?.id
            )

            if (!selectedItem) {
              return undefined
            }

            return (
              <div class="flex items-center gap-2">
                <Avatar class="size-4" size="sm">
                  <AvatarImage src={selectedItem.avatarUrl} />
                  <AvatarFallback>{selectedItem.fallback}</AvatarFallback>
                </Avatar>
                <span>{selectedItem.name}</span>
              </div>
            )
          }}
        </Select.Value>
        <Select.Indicator />
      </Select.Trigger>
      <Select.Popover>
        <ListBox>
          <For each={users}>
            {(user) => (
              <ListBox.Item id={user.id} textValue={user.name}>
                <Avatar size="sm">
                  <AvatarImage src={user.avatarUrl} />
                  <AvatarFallback>{user.fallback}</AvatarFallback>
                </Avatar>
                <div class="flex flex-col">
                  <Label>{user.name}</Label>
                  <Description>{user.email}</Description>
                </div>
                <ListBox.ItemIndicator />
              </ListBox.Item>
            )}
          </For>
        </ListBox>
      </Select.Popover>
    </Select>
  )
}

Disabled

<Select isDisabled defaultValue="california" placeholder="Select one">
  ...
</Select>
  • TextField: Composition-friendly fields with labels and validation
  • Label / Description / FieldError: Field satellites, all work inside Select

Styling

Passing Tailwind CSS classes

import { Label, ListBox, Select } from "heroui-solid";

function CustomSelect() {
  return (
    <Select class="w-full">
      <Label>State</Label>
      <Select.Trigger class="rounded-lg border bg-surface-secondary p-2">
        <Select.Value />
        <Select.Indicator />
      </Select.Trigger>
      <Select.Popover>
        <ListBox>
          <ListBox.Item id="1" textValue="Item 1" class="hover:bg-surface-secondary">
            Item 1
          </ListBox.Item>
        </ListBox>
      </Select.Popover>
    </Select>
  );
}

Customizing the component classes

To customize the Select component classes, you can use the @layer components directive.

@layer components {
  .select {
    @apply flex flex-col gap-1;
  }

  .select__trigger {
    @apply rounded-lg border border-border bg-surface p-2;
  }

  .select__value {
    @apply text-current;
  }

  .select__indicator {
    @apply text-muted;
  }

  .select__popover {
    @apply rounded-lg border border-border bg-surface p-2;
  }
}

HeroUI follows the BEM methodology to ensure component variants and states are reusable and easy to customize.

CSS Classes

The Select component uses these CSS classes (View source styles):

Base Classes

  • .select - Base select container
  • .select__trigger - The button that triggers the select
  • .select__value - The displayed value or placeholder
  • .select__indicator - The dropdown indicator icon
  • .select__popover - The popover container
  • .list-box / .list-box-item / .list-box-item__indicator - The options list

Variant Classes

  • .select--primary - Primary variant with shadow (default)
  • .select--secondary - Secondary variant without shadow, suitable for use in surfaces

State Classes

  • .select[data-invalid="true"] - Invalid state
  • .select__trigger[data-focus-visible="true"] - Focused trigger state
  • .select__trigger[data-disabled="true"] - Disabled trigger state

Interactive States

  • Hover: :hover on trigger
  • Focus: :focus-visible on trigger (shows focus ring)
  • Disabled: :disabled on trigger, [data-disabled="true"] on the root
  • Invalid: [data-invalid="true"] on the root
  • Open: [data-open="true"] on indicator (rotates it)
  • Placeholder: [data-placeholder-shown] on the value (Kobalte attribute)
  • Highlighted item: [data-highlighted] (Kobalte attribute)

API Reference

Select Props

PropTypeDefaultDescription
placeholderJSX.Element-Temporary text that occupies the select when it is empty
selectionMode"single" | "multiple""single"Whether single or multiple selection is enabled
isOpenboolean-Sets the open state of the menu (controlled)
defaultOpenboolean-Sets the default open state of the menu (uncontrolled)
onOpenChange(isOpen: boolean) => void-Handler called when the open state changes
disabledKeysIterable<string>-Keys of disabled items
isDisabledboolean-Whether the select is disabled
valuestring | string[] | null-Current value (controlled)
defaultValuestring | string[] | null-Default value (uncontrolled)
onChange(value: string | string[] | null) => void-Handler called when the value changes
isRequiredboolean-Whether user input is required
isInvalidboolean-Whether the select value is invalid
namestring-The name of the input, used when submitting an HTML form
fullWidthbooleanfalseWhether the select should take full width of its container
variant"primary" | "secondary""primary"Visual variant of the component. primary is the default style with shadow. secondary is a lower emphasis variant without shadow, suitable for use in surfaces.
classstring-Additional CSS classes
childrenJSX.Element-Select content

Select.Trigger Props

PropTypeDefaultDescription
classstring-Additional CSS classes
childrenJSX.Element-Trigger content

Select.Value Props

PropTypeDefaultDescription
classstring-Additional CSS classes
childrenJSX.Element | (state: SelectValueState) => JSX.Element-Value content or render function

When using a render function, state exposes selectedOption(), selectedOptions(), remove(option), and clear() (Kobalte's value state).

Select.Indicator Props

PropTypeDefaultDescription
classstring-Additional CSS classes
childrenJSX.Element-Custom indicator content

Select.Popover Props

PropTypeDefaultDescription
placementstring"bottom"Placement relative to the trigger (Kobalte placements, e.g. "top")
classstring-Additional CSS classes
childrenJSX.Element-Content children

ListBox / ListBox.Item Props

PropTypeDefaultDescription
idstring-Item key (required)
textValuestringidText used for typeahead and the default value rendering
isDisabledbooleanfalseWhether the item is disabled
variant"default" | "danger""default"Item visual variant
classstring-Additional CSS classes
childrenJSX.Element-Item content

Accessibility

The Select component implements the ARIA listbox pattern (via Kobalte) and provides:

  • Full keyboard navigation support
  • Screen reader announcements for selection changes
  • Proper focus management
  • Support for disabled states
  • Typeahead search functionality
  • HTML form integration

Differences from HeroUI React

  • Built on Kobalte's Select, which is data-driven: this port bridges the JSX item API by collecting ListBox.Item descriptors and registering them as Kobalte options. Inside a Select, the ListBox selection props are ignored — the Select root owns the selection state.
  • ListBox.Section is not supported inside a Select yet (the popover marker protocol can't defer the section header) — flat item lists only.
  • Select.Value render props expose Kobalte's state (selectedOptions() accessors) instead of React Aria's { defaultChildren, isPlaceholder, state }.
  • Select.Popover placements use Kobalte's syntax ("top", "bottom-start", …) instead of React Aria's ("top left").
  • Popover enter/exit animations ride Kobalte's data-expanded/data-closed attributes; the placeholder state is [data-placeholder-shown].
  • No render prop — use Kobalte's polymorphic as where supported.

Last updated: 7/19/26, 3:27 AM

HeroUI SolidUnofficial SolidJS port of HeroUI v3, built on Kobalte and @heroui/styles