suiss/ui
  • Docs
  • Components
  • Patterns
  • Interactions
  • Charts
  • Blocks
GitHubGet started
DocsComponentsPatternsInteractionsChartsBlocks
Components82
  • Overview
  • Accordion
  • Alert
  • Alert Dialog
  • Alert Sheet
  • Amount Input
  • Aspect Ratio
  • Attachment
  • Avatar
  • Badge
  • Bottom Bar
  • Breadcrumb
  • Bubble
  • Button
  • Button Group
  • Calendar
  • Card
  • Carousel
  • Chart
  • Checkbox
  • Chip
  • Collapsible
  • Combobox
  • Command
  • Context Menu
  • Dialog
  • Direction
  • Drawer
  • Dropdown Menu
  • Empty
  • FAB
  • Field
  • Fullscreen Menu
  • Haptics
  • Header
  • Hover Card
  • Icon
  • Input
  • Input Accessory Bar
  • Input Group
  • Input OTP
  • Item
  • Kbd
  • Keyboard Scroll View
  • Keypad
  • Label
  • Marker
  • Menubar
  • Message
  • Message Scroller
  • Native Select
  • Navigation Menu
  • Page Indicator
  • Pagination
  • Pin Pad
  • Popover
  • Progress
  • QR Scanner
  • Questionnaire
  • Radio Group
  • Resizable
  • Screen
  • Scroll Area
  • Select
  • Separator
  • Sheet
  • Sidebar
  • Signature Pad
  • Skeleton
  • Slider
  • Spinner
  • Switch
  • Tab Bar
  • Table
  • Tabs
  • Text
  • Textarea
  • Toast
  • Toggle
  • Toggle Group
  • Tooltip
  • Wheel Picker
  • Widget

suiss/ui · Component

Select

A button that opens a list of options so people can pick one value.

Select is an open-source component from suiss/ui for React, React Native, SwiftUI and Jetpack Compose. The @esuiss/ui CLI copies its source into your project, so you own the code.

import {
  Select,
  SelectContent,
  SelectGroup,
  SelectItem,
  SelectTrigger,
  SelectValue,
} from "@/components/ui/select"

const fruits = [
  { value: "apple", label: "Apple" },
  { value: "banana", label: "Banana" },
  { value: "blueberry", label: "Blueberry" },
  { value: "grapes", label: "Grapes" },
  { value: "pineapple", label: "Pineapple" },
]

export default function SelectDemo() {
  return (
    <Select items={fruits}>
      <SelectTrigger aria-label="Fruit" className="w-48">
        <SelectValue placeholder="Select a fruit" />
      </SelectTrigger>
      <SelectContent>
        <SelectGroup>
          {fruits.map((fruit) => (
            <SelectItem key={fruit.value} value={fruit.value}>
              {fruit.label}
            </SelectItem>
          ))}
        </SelectGroup>
      </SelectContent>
    </Select>
  )
}

Select wraps the select primitive of the chosen base. SelectTrigger shows the current choice through SelectValue and a chevron icon; SelectContent renders the popup in a portal, positioned against the trigger, and holds SelectItem options. Use SelectGroup with a SelectLabel to title a set of options, and SelectSeparator to divide groups.

A highlight pill slides between options as the highlighted item changes, and the selected item shows a check mark. Use Select when people choose one value from a fixed list. For a few always-visible options, consider ToggleGroup or RadioGroup.

Installation

CLI

$ pnpm dlx shadcn@latest add @esuiss/select

Works in any project with a components.json. See Installation for the one-line registry setup. Or use the suiss/ui CLI:

$ pnpm dlx @esuiss/ui@latest add select

Manual

  1. Install the dependencies and add the cn helper:
$ pnpm add @base-ui/react @phosphor-icons/react
  1. Copy the source into components/ui/select.tsx:
  1. Update the import paths to match your project.

Usage

import { SelectGroup, SelectValue, SelectTrigger, SelectContent, SelectLabel, SelectItem, SelectSeparator, SelectScrollUpButton, SelectScrollDownButton } 
<Select












Pass items to Select as { value, label } pairs so SelectValue can show the label for the chosen value, even before the popup opens. Use defaultValue for an uncontrolled select, or value with onValueChange to control it. SelectValue shows its placeholder until a value is chosen.

SelectContent opens below the trigger by default. It accepts the positioner props side, sideOffset, align, alignOffset and alignItemWithTrigger.

Examples

Open

Renders the popup open on mount with a value already selected.

With value

Starts with a default value, so the trigger shows its label.

Small

Uses the compact sm trigger size.

Grouped

Splits options into labelled groups with a separator between them.

With icons

Places an icon before each option's label.

Disabled

Disables the select so it can't be opened.

Invalid

Marks the trigger with aria-invalid to show an error state.

API Reference

Anatomy9 parts
<Select>
<SelectTrigger>
<SelectValue />
</SelectTrigger>
<SelectContent>
<SelectGroup>
<SelectItem />
01

Select

The root that holds the selected value and the open state.

Also accepts the props of the primitive part it wraps.

02

SelectGroup

Groups related options; label it with a SelectLabel.

Also accepts the props of the primitive part it wraps.

03

SelectValue

Shows the selected option's label, or the placeholder, inside the trigger.

Also accepts the props of the primitive part it wraps.

04

SelectTrigger

The button that opens the popup and displays the current value.

PropTypeDefaultDescription
size"sm" | "default""default"Sets the trigger height: default is 44px tall, sm is 36px with smaller corners.

Also accepts the props of the primitive part it wraps.

05

SelectContent

The positioned popup that holds the option list and scroll arrows.

Also accepts the props of the primitive parts it wraps.

06

SelectLabel

A heading that names a SelectGroup.

Also accepts the props of the primitive part it wraps.

07

SelectItem

A selectable option, with a check mark when selected.

Also accepts the props of the primitive part it wraps.

08

SelectSeparator

A thin line that divides groups of options.

Also accepts the props of the primitive part it wraps.

09

SelectScrollUpButton

An arrow that scrolls the list up when it overflows; rendered by SelectContent.

Also accepts the props of React.ComponentProps<typeof SelectPrimitive.ScrollUpArrow>.

10

SelectScrollDownButton

An arrow that scrolls the list down when it overflows; rendered by SelectContent.

Also accepts the props of React.ComponentProps<typeof SelectPrimitive.ScrollDownArrow>.

Accessibility

  • The trigger has the combobox role and the popup list has the listbox role; each item is an option with aria-selected on the chosen one.
  • Give the trigger an accessible name, such as aria-label or an associated label.
  • When the popup opens, focus moves to the selected option. Arrow keys move between options and Enter selects.
  • Escape closes the popup without changing the value, and focus returns to the trigger.
  • A SelectGroup with a SelectLabel is exposed as a named group.

Notes

  • Parts carry data-slot attributes such as select-trigger, select-content, select-item and select-highlight.
  • The trigger sets data-size, and styles data-placeholder, aria-invalid and disabled states. Invalid triggers use the destructive color.
  • The popup uses bg-popover, the highlight uses bg-accent text-accent-foreground, and the check mark uses text-primary.
  • The highlight animation is turned off when reduced motion is preferred.

Select is built on the select primitive. SelectTrigger is a pressable field with the combobox role and an up-down caret. SelectContent renders in a portal, positioned next to the trigger, and fades in and out with Reanimated. The list matches the trigger width.

The trigger comes in two sizes, default and sm. Use Select for picking one value from a short list; for a list that needs searching, use Combobox.

Installation

$ pnpm dlx shadcn@latest add @esuiss-rn/select

See Installation for the components.json setup. Or use the suiss/ui CLI:

$ pnpm dlx @esuiss/ui@latest add select
$ pnpm dlx @esuiss/ui@latest add select --platform swift
NextSeparatorPreviousScroll Area

On this page

  1. 01Installation
    • CLI
    • Manual
  2. 02Usage
  3. 03Examples
    • Open
    • With value
    • Small
    • Grouped
    • With icons
    • Disabled
    • Invalid
  4. 04API Reference
    • Select
    • SelectGroup
    • SelectValue
    • SelectTrigger
    • SelectContent
    • SelectLabel
    • SelectItem
    • SelectSeparator
    • SelectScrollUpButton
    • SelectScrollDownButton
  5. 05Accessibility
  6. 06Notes
  7. 07Installation
    • Manual
  8. 08Usage
  9. 09Examples
    • Default
    • With value
    • Small
    • Grouped
    • With label
    • Disabled
    • Invalid
  10. 10API Reference
    • Select
    • SelectGroup
    • SelectValue
    • SelectTrigger
    • SelectContent
    • SelectLabel
    • SelectItem
    • SelectSeparator
  11. 11Accessibility
  12. 12Notes
from
"@/components/ui/select"
items
=
{fruits}>
<SelectTrigger aria-label="Fruit" className="w-48">
<SelectValue placeholder="Select a fruit" />
</SelectTrigger>
<SelectContent>
<SelectGroup>
{fruits.map((fruit) => (
<SelectItem key={fruit.value} value={fruit.value}>
{fruit.label}
</SelectItem>
))}
</SelectGroup>
</SelectContent>
</Select>
<SelectLabel />
</SelectGroup>
<SelectItem />
<SelectSeparator />
</SelectContent>
</Select>
$ pnpm dlx @esuiss/ui@latest add select --platform kotlin

The CLI also adds what it builds on: Icon, Text. On React Native it also adds the animated view components/ui/animated-view.tsx.

Manual

  1. Install the dependencies:
npx expo install cn phosphor-react-native react-native-reanimated react-native-screens react-native-svg react-native-worklets
  1. Add the pieces it builds on: Icon, Text, and the primitives components/primitives/field.tsx, components/primitives/positioner.tsx, components/primitives/select.tsx, components/primitives/use-render.tsx, and the animated view components/ui/animated-view.tsx from the next step.

  2. Copy the animated view into components/ui/animated-view.tsx:

uniwind/components/ui/animated-view.tsx
import { Pressable } from "react-native"
import Animated from "react-native-reanimated"

const AnimatedPressable = Animated.createAnimatedComponent(Pressable)

type AnimatedViewProps =
  | (React.ComponentProps<typeof Animated.View> &
      React.RefAttributes<React.ComponentRef<typeof Animated.View>> & {
        as?: "View"
      })
  | (React.ComponentProps<typeof AnimatedPressable> &
      React.RefAttributes<React.ComponentRef<typeof AnimatedPressable>> & {
        as: "Pressable"
      })

function AnimatedView(props: AnimatedViewProps) {
  1. Copy the source into components/ui/select.tsx:
uniwind/components/ui/select.tsx
import { cn } from "cn"
import { CaretUpDownIcon, CheckIcon } from "phosphor-react-native"
import * as React from "react"
import { Platform, type StyleProp, type ViewStyle } from "react-native"
import { FadeIn, FadeOut, ReduceMotion } from "react-native-reanimated"
import { FullWindowOverlay as RNFullWindowOverlay } from "react-native-screens"
import { useFieldRootContext } from "@/components/primitives/field"
import { useOptionalPositionerContext } from "@/components/primitives/positioner"
import * as SelectPrimitive from "@/components/primitives/select"
import {
  resolveClassName,
  resolveStyle,
} from "@/components/primitives/use-render"
import { AnimatedView } from "@/components/ui/animated-view"
import { Icon } from "@/components/ui/icon"
import { TextClassContext } from "@/components/ui/text"
  1. Update the import paths to match your project.
  1. Install the dependencies:
npx expo install cn phosphor-react-native react-native-reanimated react-native-screens react-native-svg react-native-worklets
  1. Add the pieces it builds on: Icon, Text, and the primitives components/primitives/field.tsx, components/primitives/positioner.tsx, components/primitives/select.tsx, components/primitives/use-render.tsx, and the animated view components/ui/animated-view.tsx from the next step.

  2. Copy the animated view into components/ui/animated-view.tsx:

  1. Copy the source into components/ui/select.tsx:
  1. Update the import paths to match your project.
  1. Install the dependencies:
npx expo install phosphor-react-native react-native-reanimated react-native-screens react-native-svg react-native-worklets
  1. Add the pieces it builds on: Icon, Text, and the primitives components/primitives/field.tsx, components/primitives/positioner.tsx, components/primitives/select.tsx, components/primitives/use-render.tsx, and the animated view components/ui/animated-view.tsx from the next step.

  2. Copy the animated view into components/ui/animated-view.tsx:

  1. Copy the source into components/ui/select.tsx:
  1. Update the import paths to match your project.
  1. Target iOS 17 or later.

  2. Add the Swift package to your app:

Package.swift
.package(url: "https://github.com/phosphor-icons/swift", from: "2.1.0")
  1. Copy the theme files every view uses:
swift/Theme/Theme.swift
import SwiftUI
import UIKit

struct RGBA: Sendable {
  let red: CGFloat
  let green: CGFloat
  let blue: CGFloat
  let alpha: CGFloat

  init(_ hex: UInt32, _ alpha: CGFloat = 1) {
    red = CGFloat((hex >> 16) & 0xFF) / 255
    green = CGFloat((hex >> 8) & 0xFF) / 255
    blue = CGFloat(hex & 0xFF) / 255
    self.alpha = alpha
  }
swift/Theme/Tokens.swift
import SwiftUI

public enum EsuissColor {
  public static let background = Color.adaptive(light: .init(0xFFFFFF), dark: .init(0x000000))
  public static let foreground = Color.adaptive(light: .init(0x1D1D1F), dark: .init(0xF5F5F7))
  public static let card = Color.adaptive(light: .init(0xFFFFFF), dark: .init(0x1D1D1F))
  public static let cardForeground = Color.adaptive(light: .init(0x1D1D1F), dark: .init(0xF5F5F7))
  public static let popover = Color.adaptive(light: .init(0xFFFFFF), dark: .init(0x1D1D1F))
  public static let popoverForeground = Color.adaptive(light: .init(0x1D1D1F), dark: .init(0xF5F5F7))
  public static let primary = Color.adaptive(light: .init(0x0071E3), dark: .init(0x2997FF))
  public static let primaryForeground = Color.adaptive(light: .init(0xFFFFFF), dark: .init(0xFFFFFF))
  public static let secondary = Color.adaptive(light: .init(0x000000, 0.04), dark: .init(0xFFFFFF, 0.1))
  public static let secondaryForeground = Color.adaptive(light: .init(0x1D1D1F), dark: .init(0xF5F5F7))
  public static let muted = Color.adaptive(light: .init(0xF5F5F7), dark: .init(0x161617))
  public static let mutedForeground = Color.adaptive(light: .init(0x6E6E73), dark: .init(0xA1A1A6))
  public static let accent = Color.adaptive(light: .init(0x000000, 0.06), dark: .init(0xFFFFFF, 0.1))
  1. Copy the source and every file it uses:
swift/Components/Select.swift
import PhosphorSwift
import SwiftUI

struct SelectContext: @unchecked Sendable {
  var selected: AnyHashable?
  var registering = false
  var select: @MainActor @Sendable (AnyHashable) -> Void = { _ in }
}

extension EnvironmentValues {
  @Entry var select = SelectContext()
}

struct SelectLabelsKey: PreferenceKey {
  static var defaultValue: [AnyHashable: Text] { [:] }
swift/Components/Button.swift
import SwiftUI

public struct VariantButtonStyle: ButtonStyle {
  public enum Variant: CaseIterable, Sendable {
    case `default`, outline, secondary, tinted, ghost, plain, destructive, link
  }

  public enum Size: CaseIterable, Sendable {
    case `default`, xs, sm, lg, xl, icon, iconXs, iconSm, iconLg, iconXl

    var height: CGFloat {
      switch self {
      case .xs, .iconXs: 24
      case .sm, .iconSm: 32
      case .default, .icon: 36
      case .lg, .iconLg: 44
swift/Components/ButtonGroup.swift
import SwiftUI

extension EnvironmentValues {
  @Entry var buttonGroup: Axis? = nil
  @Entry var buttonGroupSize: CGSize = .zero
}

enum ButtonGroupSpace: Hashable {
  case group
}

struct ButtonGroupFrame {
  let bounds: Anchor<CGRect>
  let outlined: Bool
}
swift/Components/DropdownMenu.swift
import PhosphorSwift
import SwiftUI

enum MenuPresentation: Sendable {
  case system, popup
}

extension EnvironmentValues {
  @Entry var menuPresentation: MenuPresentation = .system
  @Entry var menuRadio = MenuRadioContext()
}

struct MenuRadioContext: @unchecked Sendable {
  var selected: AnyHashable?
  var select: @MainActor @Sendable (AnyHashable) -> Void = { _ in }
}
swift/Components/Field.swift
import SwiftUI

extension EnvironmentValues {
  @Entry public var isInvalid = false
  @Entry var fieldInset = false
  @Entry var fieldGroupInset = false
  @Entry var fieldSetInset = false
}

extension View {
  public func invalid(_ isInvalid: Bool = true) -> some View {
    environment(\.isInvalid, isInvalid)
  }
}

public enum FieldOrientation: CaseIterable, Sendable {
swift/Components/Overlay.swift
import PhosphorSwift
import Observation
import SwiftUI

public enum PopupAlignment: Sendable, CaseIterable {
  case start, center, end
}

struct OverlayDismiss: Sendable {
  let action: @MainActor @Sendable () -> Void

  @MainActor func callAsFunction() {
    action()
  }
}
swift/Components/Separator.swift
import SwiftUI

public struct Separator: View {
  let orientation: Axis

  public init(_ orientation: Axis = .horizontal) {
    self.orientation = orientation
  }

  public var body: some View {
    Rectangle()
      .fill(EsuissColor.border)
      .frame(width: orientation == .vertical ? 1 : nil, height: orientation == .horizontal ? 1 : nil)
      .frame(maxWidth: orientation == .horizontal ? .infinity : nil, maxHeight: orientation == .vertical ? .infinity : nil)
      .accessibilityHidden(true)
  }
swift/Components/TextVariant.swift
import SwiftUI

public enum TextVariant: CaseIterable, Sendable {
  case `default`, h1, h2, h3, h4, p, blockquote, code, lead, large, small, muted

  var style: TextStyle {
    switch self {
    case .h1: .xl3
    case .h2: .xl2
    case .h3: .xl
    case .h4, .lead, .large: .lg
    case .code, .small, .muted: .sm
    default: .base
    }
  }
  1. Add the files to your app target.
  1. Set minSdk to 24 or higher and add the dependencies:
build.gradle.kts
implementation(platform("androidx.compose:compose-bom:2026.09.00"))
implementation("androidx.compose.foundation:foundation")
implementation("androidx.compose.ui:ui")
implementation("com.adamglin:phosphor-icon-android:1.0.0")
  1. Copy the theme files every view uses:
kotlin/theme/Theme.kt
package com.esuiss.ui.theme

import androidx.compose.foundation.isSystemInDarkTheme
import androidx.compose.runtime.Composable
import androidx.compose.runtime.CompositionLocalProvider
import androidx.compose.runtime.Immutable
import androidx.compose.runtime.staticCompositionLocalOf
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.dropShadow
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.Shape
import androidx.compose.ui.graphics.shadow.Shadow as DropShadow
import androidx.compose.ui.text.TextStyle
import androidx.compose.ui.text.style.LineHeightStyle
import androidx.compose.ui.unit.Dp
import androidx.compose.ui.unit.DpOffset
kotlin/theme/Tokens.kt
package com.esuiss.ui.theme

import androidx.compose.runtime.Immutable
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.unit.dp

@Immutable
data class Colors(
    val background: Color,
    val foreground: Color,
    val card: Color,
    val cardForeground: Color,
    val popover: Color,
    val popoverForeground: Color,
    val primary: Color,
    val primaryForeground: Color,
  1. Copy the source and every file it uses:
kotlin/components/Select.kt
package com.esuiss.ui.components

import androidx.compose.foundation.background
import androidx.compose.foundation.border
import androidx.compose.foundation.clickable
import androidx.compose.foundation.interaction.MutableInteractionSource
import androidx.compose.foundation.interaction.collectIsPressedAsState
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.ColumnScope
import androidx.compose.foundation.layout.IntrinsicSize
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.heightIn
kotlin/components/Button.kt
package com.esuiss.ui.components

import androidx.compose.foundation.background
import androidx.compose.foundation.border
import androidx.compose.foundation.clickable
import androidx.compose.foundation.interaction.MutableInteractionSource
import androidx.compose.foundation.interaction.collectIsPressedAsState
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.RowScope
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.shape.CircleShape
import androidx.compose.foundation.shape.RoundedCornerShape
kotlin/components/ButtonGroup.kt
package com.esuiss.ui.components

import androidx.compose.foundation.background
import androidx.compose.foundation.gestures.Orientation
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.RowScope
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.shape.CircleShape
import androidx.compose.foundation.shape.CornerSize
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.runtime.Composable
import androidx.compose.runtime.CompositionLocalProvider
import androidx.compose.runtime.DisposableEffect
import androidx.compose.runtime.Immutable
kotlin/components/Content.kt
package com.esuiss.ui.components

import androidx.compose.runtime.compositionLocalOf
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.text.TextStyle
import androidx.compose.ui.unit.Dp
import androidx.compose.ui.unit.dp

val LocalContentStyle = compositionLocalOf { TextStyle.Default }

val LocalContentColor = compositionLocalOf { Color.Unspecified }

val LocalIconSize = compositionLocalOf { 20.dp }
kotlin/components/Field.kt
package com.esuiss.ui.components

import androidx.compose.foundation.background
import androidx.compose.foundation.gestures.Orientation
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.ColumnScope
import androidx.compose.foundation.layout.LayoutScopeMarker
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.RowScope
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.heightIn
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.foundation.text.BasicText
kotlin/components/FormSupport.kt
package com.esuiss.ui.components

import android.provider.Settings
import androidx.compose.animation.core.CubicBezierEasing
import androidx.compose.foundation.background
import androidx.compose.foundation.border
import androidx.compose.foundation.gestures.Orientation
import androidx.compose.foundation.text.selection.TextSelectionColors
import androidx.compose.runtime.Composable
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.alpha
import androidx.compose.ui.draw.clip
import androidx.compose.ui.draw.drawWithContent
import androidx.compose.ui.geometry.Offset
kotlin/components/Icon.kt
package com.esuiss.ui.components

import androidx.compose.foundation.Image
import androidx.compose.foundation.layout.size
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.ColorFilter
import androidx.compose.ui.graphics.takeOrElse
import androidx.compose.ui.graphics.vector.ImageVector
import androidx.compose.ui.unit.Dp
import com.esuiss.ui.theme.EsuissTheme

@Composable
fun Icon(
    imageVector: ImageVector,
kotlin/components/Label.kt
package com.esuiss.ui.components

import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.RowScope
import androidx.compose.foundation.text.BasicText
import androidx.compose.runtime.Composable
import androidx.compose.runtime.CompositionLocalProvider
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.alpha
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.unit.dp
import com.esuiss.ui.theme.EsuissTheme
import com.esuiss.ui.theme.Typography
kotlin/components/Overlay.kt
package com.esuiss.ui.components

import android.os.Build
import android.view.Window
import android.view.WindowManager
import androidx.compose.animation.AnimatedVisibility
import androidx.compose.animation.AnimatedVisibilityScope
import androidx.compose.animation.EnterExitState
import androidx.compose.animation.core.MutableTransitionState
import androidx.compose.animation.core.animateFloat
import androidx.compose.animation.core.tween
import androidx.compose.animation.fadeIn
import androidx.compose.animation.fadeOut
import androidx.compose.animation.scaleIn
import androidx.compose.animation.scaleOut
import androidx.compose.animation.slideIn
kotlin/components/Separator.kt
package com.esuiss.ui.components

import androidx.compose.foundation.background
import androidx.compose.foundation.gestures.Orientation
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.fillMaxHeight
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.width
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.unit.dp
import com.esuiss.ui.theme.EsuissTheme

@Composable
fun Separator(
  1. Update the com.esuiss.ui package names to match your project.

Usage

import { SelectGroup, SelectValue, SelectTrigger, SelectContent, SelectLabel, SelectItem, SelectSeparator } from "@/components/ui/select"
<SelectDemo initial={fruits[2]} defaultOpen />

Put a SelectValue inside SelectTrigger, and SelectItem entries inside SelectContent. Group items with SelectGroup and SelectLabel, and divide groups with SelectSeparator.

Control the value with value and onValueChange on Select, or start from defaultValue. Pass the full option list as items so SelectValue can show the label of the current value. Select also accepts multiple, disabled, readOnly, open, defaultOpen and onOpenChange.

Give each SelectItem a value and a label; the label is shown when no children are passed. SelectValue shows placeholder until a value is chosen. The Option type ({ value, label }) is also exported.

Examples

Default

iOS
Select on iOS in light modeSelect on iOS in dark mode
import * as React from "react"
import { type Option, Select, SelectContent, SelectGroup, SelectItem, SelectLabel, SelectTrigger, SelectValue } from "@/components/ui/select"

const fruits: Option[] = [
  { value: "mango", label: "Mango" },
  { value: "banana", label: "Banana" },
  { value: "blueberry", label: "Blueberry" },
  { value: "grapes", label: "Grapes" },
  { value: "pineapple", label: "Pineapple" },
]

function SelectDemo({
  initial,
  size,
  disabled = false,
  invalid = false,
  labelledBy,
  defaultOpen,
}: {
  initial?: Option
  size?: "default" | "sm"
  disabled?: boolean
  invalid?: boolean
  labelledBy?: string
  defaultOpen?: boolean
}) {
  const [value, setValue] = React.useState<string | null>(
    initial?.value ?? null
  )
  return (
    <Select
      items={fruits}
      value={value}
      onValueChange={setValue}
      disabled={disabled}
      defaultOpen={defaultOpen}
    >
      <SelectTrigger
        size={size}
        accessibilityLabelledBy={labelledBy}
        invalid={invalid}
        className="w-48"
      >
        <SelectValue placeholder="Select a fruit" />
      </SelectTrigger>
      <SelectContent className="w-48">
        <SelectGroup>
          <SelectLabel>Fruits</SelectLabel>
          {fruits.map((fruit) => (
            <SelectItem
              key={fruit.value}
              value={fruit.value}
              label={fruit.label}
            />
          ))}
        </SelectGroup>
      </SelectContent>
    </Select>
  )
}

export function SelectExample() {
  return (
    <SelectDemo />
  )
}
import EsuissUI
import SwiftUI

struct SelectOption {
  let value: String
  let label: LocalizedStringKey
}

let selectFruits = [
  SelectOption(value: "mango", label: "Mango"),
  SelectOption(value: "banana", label: "Banana"),
  SelectOption(value: "blueberry", label: "Blueberry"),
  SelectOption(value: "grapes", label: "Grapes"),
  SelectOption(value: "pineapple", label: "Pineapple"),
]

struct SelectDemo: View {
  var size: SelectSize = .default

  @State private var value: String?

  init(initial: String? = nil, size: SelectSize = .default) {
    self.size = size
    _value = State(initialValue: initial)
  }

  var body: some View {
    SelectField(selection: $value, placeholder: "Select a fruit", size: size) {
      SelectGroup {
        SelectLabel("Fruits")
        ForEach(selectFruits, id: \.value) { fruit in
          SelectItem(fruit.label, value: fruit.value)
        }
      }
    }
    .frame(width: 192)
  }
}
import androidx.compose.foundation.layout.width
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.saveable.rememberSaveable
import androidx.compose.runtime.setValue
import androidx.compose.ui.Modifier
import androidx.compose.ui.semantics.contentDescription
import androidx.compose.ui.semantics.semantics
import androidx.compose.ui.unit.dp
import com.esuiss.ui.components.Select
import com.esuiss.ui.components.SelectGroup
import com.esuiss.ui.components.SelectItem
import com.esuiss.ui.components.SelectLabel
import com.esuiss.ui.components.SelectOption
import com.esuiss.ui.components.SelectSize

val selectFruits = listOf(
    SelectOption("mango", "Mango"),
    SelectOption("banana", "Banana"),
    SelectOption("blueberry", "Blueberry"),
    SelectOption("grapes", "Grapes"),
    SelectOption("pineapple", "Pineapple"),
)

@Composable
fun SelectDemo(
    initial: String? = null,
    size: SelectSize = SelectSize.Default,
    enabled: Boolean = true,
    invalid: Boolean = false,
    label: String? = null,
) {
    var value by rememberSaveable { mutableStateOf(initial) }
    Select(
        value = value,
        onValueChange = { value = it },
        displayValue = selectFruits.firstOrNull { it.value == value }?.label,
        modifier = Modifier
            .width(192.dp)
            .then(if (label != null) Modifier.semantics { contentDescription = label } else Modifier),
        placeholder = "Select a fruit",
        size = size,
        invalid = invalid,
        enabled = enabled,
    ) {
        SelectGroup {
            SelectLabel("Fruits")
            selectFruits.forEach { SelectItem(it.value, it.label) }
        }
    }
}

With value

A select that starts with a chosen value.

iOS
Select (with value) on iOS in light modeSelect (with value) on iOS in dark mode
import * as React from "react"
import { type Option, Select, SelectContent, SelectGroup, SelectItem, SelectLabel, SelectTrigger, SelectValue } from "@/components/ui/select"

const fruits: Option[] = [
  { value: "mango", label: "Mango" },
  { value: "banana", label: "Banana" },
  { value: "blueberry", label: "Blueberry" },
  { value: "grapes", label: "Grapes" },
  { value: "pineapple", label: "Pineapple" },
]

function SelectDemo({
  initial,
  size,
  disabled = false,
  invalid = false,
  labelledBy,
  defaultOpen,
}: {
  initial?: Option
  size?: "default" | "sm"
  disabled?: boolean
  invalid?: boolean
  labelledBy?: string
  defaultOpen?: boolean
}) {
  const [value, setValue] = React.useState<string | null>(
    initial?.value ?? null
  )
  return (
    <Select
      items={fruits}
      value={value}
      onValueChange={setValue}
      disabled={disabled}
      defaultOpen={defaultOpen}
    >
      <SelectTrigger
        size={size}
        accessibilityLabelledBy={labelledBy}
        invalid={invalid}
        className="w-48"
      >
        <SelectValue placeholder="Select a fruit" />
      </SelectTrigger>
      <SelectContent className="w-48">
        <SelectGroup>
          <SelectLabel>Fruits</SelectLabel>
          {fruits.map((fruit) => (
            <SelectItem
              key={fruit.value}
              value={fruit.value}
              label={fruit.label}
            />
          ))}
        </SelectGroup>
      </SelectContent>
    </Select>
  )
}

export function SelectWithValueExample() {
  return (
    <SelectDemo initial={fruits[2]} />
  )
}
import EsuissUI
import SwiftUI

struct SelectWithValueExample: View {
  var body: some View {
    SelectDemo(initial: "blueberry")
  }
}

struct SelectOption {
  let value: String
  let label: LocalizedStringKey
}

let selectFruits = [
  SelectOption(value: "mango", label: "Mango"),
  SelectOption(value: "banana", label: "Banana"),
  SelectOption(value: "blueberry", label: "Blueberry"),
  SelectOption(value: "grapes", label: "Grapes"),
  SelectOption(value: "pineapple", label: "Pineapple"),
]

struct SelectDemo: View {
  var size: SelectSize = .default

  @State private var value: String?

  init(initial: String? = nil, size: SelectSize = .default) {
    self.size = size
    _value = State(initialValue: initial)
  }

  var body: some View {
    SelectField(selection: $value, placeholder: "Select a fruit", size: size) {
      SelectGroup {
        SelectLabel("Fruits")
        ForEach(selectFruits, id: \.value) { fruit in
          SelectItem(fruit.label, value: fruit.value)
        }
      }
    }
    .frame(width: 192)
  }
}
import androidx.compose.foundation.layout.width
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.saveable.rememberSaveable
import androidx.compose.runtime.setValue
import androidx.compose.ui.Modifier
import androidx.compose.ui.semantics.contentDescription
import androidx.compose.ui.semantics.semantics
import androidx.compose.ui.unit.dp
import com.esuiss.ui.components.Select
import com.esuiss.ui.components.SelectGroup
import com.esuiss.ui.components.SelectItem
import com.esuiss.ui.components.SelectLabel
import com.esuiss.ui.components.SelectOption
import com.esuiss.ui.components.SelectSize

@Composable
fun SelectWithValueExample() {
    SelectDemo(initial = "blueberry")
}

val selectFruits = listOf(
    SelectOption("mango", "Mango"),
    SelectOption("banana", "Banana"),
    SelectOption("blueberry", "Blueberry"),
    SelectOption("grapes", "Grapes"),
    SelectOption("pineapple", "Pineapple"),
)

@Composable
fun SelectDemo(
    initial: String? = null,
    size: SelectSize = SelectSize.Default,
    enabled: Boolean = true,
    invalid: Boolean = false,
    label: String? = null,
) {
    var value by rememberSaveable { mutableStateOf(initial) }
    Select(
        value = value,
        onValueChange = { value = it },
        displayValue = selectFruits.firstOrNull { it.value == value }?.label,
        modifier = Modifier
            .width(192.dp)
            .then(if (label != null) Modifier.semantics { contentDescription = label } else Modifier),
        placeholder = "Select a fruit",
        size = size,
        invalid = invalid,
        enabled = enabled,
    ) {
        SelectGroup {
            SelectLabel("Fruits")
            selectFruits.forEach { SelectItem(it.value, it.label) }
        }
    }
}

Small

The sm trigger size.

iOS
Select (small) on iOS in light modeSelect (small) on iOS in dark mode
import * as React from "react"
import { type Option, Select, SelectContent, SelectGroup, SelectItem, SelectLabel, SelectTrigger, SelectValue } from "@/components/ui/select"

const fruits: Option[] = [
  { value: "mango", label: "Mango" },
  { value: "banana", label: "Banana" },
  { value: "blueberry", label: "Blueberry" },
  { value: "grapes", label: "Grapes" },
  { value: "pineapple", label: "Pineapple" },
]

function SelectDemo({
  initial,
  size,
  disabled = false,
  invalid = false,
  labelledBy,
  defaultOpen,
}: {
  initial?: Option
  size?: "default" | "sm"
  disabled?: boolean
  invalid?: boolean
  labelledBy?: string
  defaultOpen?: boolean
}) {
  const [value, setValue] = React.useState<string | null>(
    initial?.value ?? null
  )
  return (
    <Select
      items={fruits}
      value={value}
      onValueChange={setValue}
      disabled={disabled}
      defaultOpen={defaultOpen}
    >
      <SelectTrigger
        size={size}
        accessibilityLabelledBy={labelledBy}
        invalid={invalid}
        className="w-48"
      >
        <SelectValue placeholder="Select a fruit" />
      </SelectTrigger>
      <SelectContent className="w-48">
        <SelectGroup>
          <SelectLabel>Fruits</SelectLabel>
          {fruits.map((fruit) => (
            <SelectItem
              key={fruit.value}
              value={fruit.value}
              label={fruit.label}
            />
          ))}
        </SelectGroup>
      </SelectContent>
    </Select>
  )
}

export function SelectSmallExample() {
  return (
    <SelectDemo size="sm" />
  )
}
import EsuissUI
import SwiftUI

struct SelectSmallExample: View {
  var body: some View {
    SelectDemo(size: .sm)
  }
}

struct SelectOption {
  let value: String
  let label: LocalizedStringKey
}

let selectFruits = [
  SelectOption(value: "mango", label: "Mango"),
  SelectOption(value: "banana", label: "Banana"),
  SelectOption(value: "blueberry", label: "Blueberry"),
  SelectOption(value: "grapes", label: "Grapes"),
  SelectOption(value: "pineapple", label: "Pineapple"),
]

struct SelectDemo: View {
  var size: SelectSize = .default

  @State private var value: String?

  init(initial: String? = nil, size: SelectSize = .default) {
    self.size = size
    _value = State(initialValue: initial)
  }

  var body: some View {
    SelectField(selection: $value, placeholder: "Select a fruit", size: size) {
      SelectGroup {
        SelectLabel("Fruits")
        ForEach(selectFruits, id: \.value) { fruit in
          SelectItem(fruit.label, value: fruit.value)
        }
      }
    }
    .frame(width: 192)
  }
}
import androidx.compose.foundation.layout.width
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.saveable.rememberSaveable
import androidx.compose.runtime.setValue
import androidx.compose.ui.Modifier
import androidx.compose.ui.semantics.contentDescription
import androidx.compose.ui.semantics.semantics
import androidx.compose.ui.unit.dp
import com.esuiss.ui.components.Select
import com.esuiss.ui.components.SelectGroup
import com.esuiss.ui.components.SelectItem
import com.esuiss.ui.components.SelectLabel
import com.esuiss.ui.components.SelectOption
import com.esuiss.ui.components.SelectSize

@Composable
fun SelectSmallExample() {
    SelectDemo(size = SelectSize.Sm)
}

val selectFruits = listOf(
    SelectOption("mango", "Mango"),
    SelectOption("banana", "Banana"),
    SelectOption("blueberry", "Blueberry"),
    SelectOption("grapes", "Grapes"),
    SelectOption("pineapple", "Pineapple"),
)

@Composable
fun SelectDemo(
    initial: String? = null,
    size: SelectSize = SelectSize.Default,
    enabled: Boolean = true,
    invalid: Boolean = false,
    label: String? = null,
) {
    var value by rememberSaveable { mutableStateOf(initial) }
    Select(
        value = value,
        onValueChange = { value = it },
        displayValue = selectFruits.firstOrNull { it.value == value }?.label,
        modifier = Modifier
            .width(192.dp)
            .then(if (label != null) Modifier.semantics { contentDescription = label } else Modifier),
        placeholder = "Select a fruit",
        size = size,
        invalid = invalid,
        enabled = enabled,
    ) {
        SelectGroup {
            SelectLabel("Fruits")
            selectFruits.forEach { SelectItem(it.value, it.label) }
        }
    }
}

Grouped

Two labeled groups divided by a separator.

iOS
Select (grouped) on iOS in light modeSelect (grouped) on iOS in dark mode
import * as React from "react"
import { type Option, Select, SelectContent, SelectGroup, SelectItem, SelectLabel, SelectSeparator, SelectTrigger, SelectValue } from "@/components/ui/select"

const fruits: Option[] = [
  { value: "mango", label: "Mango" },
  { value: "banana", label: "Banana" },
  { value: "blueberry", label: "Blueberry" },
  { value: "grapes", label: "Grapes" },
  { value: "pineapple", label: "Pineapple" },
]

const vegetables: Option[] = [
  { value: "carrot", label: "Carrot" },
  { value: "broccoli", label: "Broccoli" },
  { value: "spinach", label: "Spinach" },
]

function GroupedDemo() {
  const [value, setValue] = React.useState<string | null>(null)
  return (
    <Select
      items={[...fruits.slice(0, 3), ...vegetables]}
      value={value}
      onValueChange={setValue}
    >
      <SelectTrigger className="w-56">
        <SelectValue placeholder="Select produce" />
      </SelectTrigger>
      <SelectContent className="w-56">
        <SelectGroup>
          <SelectLabel>Fruits</SelectLabel>
          {fruits.slice(0, 3).map((fruit) => (
            <SelectItem
              key={fruit.value}
              value={fruit.value}
              label={fruit.label}
            />
          ))}
        </SelectGroup>
        <SelectSeparator />
        <SelectGroup>
          <SelectLabel>Vegetables</SelectLabel>
          {vegetables.map((vegetable) => (
            <SelectItem
              key={vegetable.value}
              value={vegetable.value}
              label={vegetable.label}
            />
          ))}
        </SelectGroup>
      </SelectContent>
    </Select>
  )
}

export function SelectGroupedExample() {
  return (
    <GroupedDemo />
  )
}
import EsuissUI
import SwiftUI

struct SelectOption {
  let value: String
  let label: LocalizedStringKey
}

let selectFruits = [
  SelectOption(value: "mango", label: "Mango"),
  SelectOption(value: "banana", label: "Banana"),
  SelectOption(value: "blueberry", label: "Blueberry"),
  SelectOption(value: "grapes", label: "Grapes"),
  SelectOption(value: "pineapple", label: "Pineapple"),
]

let selectVegetables = [
  SelectOption(value: "carrot", label: "Carrot"),
  SelectOption(value: "broccoli", label: "Broccoli"),
  SelectOption(value: "spinach", label: "Spinach"),
]

struct SelectGroupedDemo: View {
  @State private var value: String?

  var body: some View {
    SelectField(selection: $value, placeholder: "Select produce") {
      SelectGroup {
        SelectLabel("Fruits")
        ForEach(selectFruits.prefix(3), id: \.value) { fruit in
          SelectItem(fruit.label, value: fruit.value)
        }
      }
      SelectSeparator()
      SelectGroup {
        SelectLabel("Vegetables")
        ForEach(selectVegetables, id: \.value) { vegetable in
          SelectItem(vegetable.label, value: vegetable.value)
        }
      }
    }
    .frame(width: 224)
  }
}
import androidx.compose.foundation.layout.width
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.saveable.rememberSaveable
import androidx.compose.runtime.setValue
import androidx.compose.ui.Modifier
import androidx.compose.ui.unit.dp
import com.esuiss.ui.components.Select
import com.esuiss.ui.components.SelectGroup
import com.esuiss.ui.components.SelectItem
import com.esuiss.ui.components.SelectLabel
import com.esuiss.ui.components.SelectOption
import com.esuiss.ui.components.SelectSeparator

val selectFruits = listOf(
    SelectOption("mango", "Mango"),
    SelectOption("banana", "Banana"),
    SelectOption("blueberry", "Blueberry"),
    SelectOption("grapes", "Grapes"),
    SelectOption("pineapple", "Pineapple"),
)

val selectVegetables = listOf(
    SelectOption("carrot", "Carrot"),
    SelectOption("broccoli", "Broccoli"),
    SelectOption("spinach", "Spinach"),
)

@Composable
fun SelectGrouped() {
    var value by rememberSaveable { mutableStateOf<String?>(null) }
    val fruits = selectFruits.take(3)
    Select(
        value = value,
        onValueChange = { value = it },
        displayValue = (fruits + selectVegetables).firstOrNull { it.value == value }?.label,
        modifier = Modifier.width(224.dp),
        placeholder = "Select produce",
    ) {
        SelectGroup {
            SelectLabel("Fruits")
            fruits.forEach { SelectItem(it.value, it.label) }
        }
        SelectSeparator()
        SelectGroup {
            SelectLabel("Vegetables")
            selectVegetables.forEach { SelectItem(it.value, it.label) }
        }
    }
}

With label

A select labeled by a Label through accessibilityLabelledBy, with helper text.

iOS
Select (with label) on iOS in light modeSelect (with label) on iOS in dark mode
import * as React from "react"
import { View } from "react-native"
import { Label } from "@/components/ui/label"
import { type Option, Select, SelectContent, SelectGroup, SelectItem, SelectLabel, SelectTrigger, SelectValue } from "@/components/ui/select"
import { Text } from "@/components/ui/text"

const fruits: Option[] = [
  { value: "mango", label: "Mango" },
  { value: "banana", label: "Banana" },
  { value: "blueberry", label: "Blueberry" },
  { value: "grapes", label: "Grapes" },
  { value: "pineapple", label: "Pineapple" },
]

function SelectDemo({
  initial,
  size,
  disabled = false,
  invalid = false,
  labelledBy,
  defaultOpen,
}: {
  initial?: Option
  size?: "default" | "sm"
  disabled?: boolean
  invalid?: boolean
  labelledBy?: string
  defaultOpen?: boolean
}) {
  const [value, setValue] = React.useState<string | null>(
    initial?.value ?? null
  )
  return (
    <Select
      items={fruits}
      value={value}
      onValueChange={setValue}
      disabled={disabled}
      defaultOpen={defaultOpen}
    >
      <SelectTrigger
        size={size}
        accessibilityLabelledBy={labelledBy}
        invalid={invalid}
        className="w-48"
      >
        <SelectValue placeholder="Select a fruit" />
      </SelectTrigger>
      <SelectContent className="w-48">
        <SelectGroup>
          <SelectLabel>Fruits</SelectLabel>
          {fruits.map((fruit) => (
            <SelectItem
              key={fruit.value}
              value={fruit.value}
              label={fruit.label}
            />
          ))}
        </SelectGroup>
      </SelectContent>
    </Select>
  )
}

export function SelectWithLabelExample() {
  return (
    <View className="gap-2">
      <Label nativeID="select-fruit">Favorite fruit</Label>
      <SelectDemo labelledBy="select-fruit" />
      <Text className="text-sm text-muted-foreground">
        Used to personalize your recommendations.
      </Text>
    </View>
  )
}
import EsuissUI
import SwiftUI

struct SelectWithLabelExample: View {
  var body: some View {
    VStack(alignment: .leading, spacing: 8) {
      FieldLabel("Favorite fruit")
        .accessibilityHidden(true)
      SelectDemo()
        .accessibilityLabel(Text("Favorite fruit"))
      Text("Used to personalize your recommendations.")
        .textStyle(.sm)
        .foregroundStyle(EsuissColor.mutedForeground)
    }
    .frame(maxWidth: .infinity, alignment: .leading)
  }
}

struct SelectOption {
  let value: String
  let label: LocalizedStringKey
}

let selectFruits = [
  SelectOption(value: "mango", label: "Mango"),
  SelectOption(value: "banana", label: "Banana"),
  SelectOption(value: "blueberry", label: "Blueberry"),
  SelectOption(value: "grapes", label: "Grapes"),
  SelectOption(value: "pineapple", label: "Pineapple"),
]

struct SelectDemo: View {
  var size: SelectSize = .default

  @State private var value: String?

  init(initial: String? = nil, size: SelectSize = .default) {
    self.size = size
    _value = State(initialValue: initial)
  }

  var body: some View {
    SelectField(selection: $value, placeholder: "Select a fruit", size: size) {
      SelectGroup {
        SelectLabel("Fruits")
        ForEach(selectFruits, id: \.value) { fruit in
          SelectItem(fruit.label, value: fruit.value)
        }
      }
    }
    .frame(width: 192)
  }
}
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.width
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.saveable.rememberSaveable
import androidx.compose.runtime.setValue
import androidx.compose.ui.Modifier
import androidx.compose.ui.semantics.contentDescription
import androidx.compose.ui.semantics.semantics
import androidx.compose.ui.unit.dp
import com.esuiss.ui.components.Label
import com.esuiss.ui.components.Select
import com.esuiss.ui.components.SelectGroup
import com.esuiss.ui.components.SelectItem
import com.esuiss.ui.components.SelectLabel
import com.esuiss.ui.components.SelectOption
import com.esuiss.ui.components.SelectSize
import com.esuiss.ui.components.Text
import com.esuiss.ui.theme.EsuissTheme
import com.esuiss.ui.theme.Typography

val selectFruits = listOf(
    SelectOption("mango", "Mango"),
    SelectOption("banana", "Banana"),
    SelectOption("blueberry", "Blueberry"),
    SelectOption("grapes", "Grapes"),
    SelectOption("pineapple", "Pineapple"),
)

@Composable
fun SelectDemo(
    initial: String? = null,
    size: SelectSize = SelectSize.Default,
    enabled: Boolean = true,
    invalid: Boolean = false,
    label: String? = null,
) {
    var value by rememberSaveable { mutableStateOf(initial) }
    Select(
        value = value,
        onValueChange = { value = it },
        displayValue = selectFruits.firstOrNull { it.value == value }?.label,
        modifier = Modifier
            .width(192.dp)
            .then(if (label != null) Modifier.semantics { contentDescription = label } else Modifier),
        placeholder = "Select a fruit",
        size = size,
        invalid = invalid,
        enabled = enabled,
    ) {
        SelectGroup {
            SelectLabel("Fruits")
            selectFruits.forEach { SelectItem(it.value, it.label) }
        }
    }
}

@Composable
fun SelectWithLabel() {
    Column(verticalArrangement = Arrangement.spacedBy(8.dp)) {
        Label("Favorite fruit")
        SelectDemo(label = "Favorite fruit")
        Text("Used to personalize your recommendations.", style = Typography.sm, color = EsuissTheme.colors.mutedForeground)
    }
}

Disabled

A disabled select with a value.

iOS
Select (disabled) on iOS in light modeSelect (disabled) on iOS in dark mode
import * as React from "react"
import { type Option, Select, SelectContent, SelectGroup, SelectItem, SelectLabel, SelectTrigger, SelectValue } from "@/components/ui/select"

const fruits: Option[] = [
  { value: "mango", label: "Mango" },
  { value: "banana", label: "Banana" },
  { value: "blueberry", label: "Blueberry" },
  { value: "grapes", label: "Grapes" },
  { value: "pineapple", label: "Pineapple" },
]

function SelectDemo({
  initial,
  size,
  disabled = false,
  invalid = false,
  labelledBy,
  defaultOpen,
}: {
  initial?: Option
  size?: "default" | "sm"
  disabled?: boolean
  invalid?: boolean
  labelledBy?: string
  defaultOpen?: boolean
}) {
  const [value, setValue] = React.useState<string | null>(
    initial?.value ?? null
  )
  return (
    <Select
      items={fruits}
      value={value}
      onValueChange={setValue}
      disabled={disabled}
      defaultOpen={defaultOpen}
    >
      <SelectTrigger
        size={size}
        accessibilityLabelledBy={labelledBy}
        invalid={invalid}
        className="w-48"
      >
        <SelectValue placeholder="Select a fruit" />
      </SelectTrigger>
      <SelectContent className="w-48">
        <SelectGroup>
          <SelectLabel>Fruits</SelectLabel>
          {fruits.map((fruit) => (
            <SelectItem
              key={fruit.value}
              value={fruit.value}
              label={fruit.label}
            />
          ))}
        </SelectGroup>
      </SelectContent>
    </Select>
  )
}

export function SelectDisabledExample() {
  return (
    <SelectDemo disabled initial={fruits[0]} />
  )
}
import EsuissUI
import SwiftUI

struct SelectDisabledExample: View {
  var body: some View {
    SelectDemo(initial: "mango")
      .disabled(true)
  }
}

struct SelectOption {
  let value: String
  let label: LocalizedStringKey
}

let selectFruits = [
  SelectOption(value: "mango", label: "Mango"),
  SelectOption(value: "banana", label: "Banana"),
  SelectOption(value: "blueberry", label: "Blueberry"),
  SelectOption(value: "grapes", label: "Grapes"),
  SelectOption(value: "pineapple", label: "Pineapple"),
]

struct SelectDemo: View {
  var size: SelectSize = .default

  @State private var value: String?

  init(initial: String? = nil, size: SelectSize = .default) {
    self.size = size
    _value = State(initialValue: initial)
  }

  var body: some View {
    SelectField(selection: $value, placeholder: "Select a fruit", size: size) {
      SelectGroup {
        SelectLabel("Fruits")
        ForEach(selectFruits, id: \.value) { fruit in
          SelectItem(fruit.label, value: fruit.value)
        }
      }
    }
    .frame(width: 192)
  }
}
import androidx.compose.foundation.layout.width
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.saveable.rememberSaveable
import androidx.compose.runtime.setValue
import androidx.compose.ui.Modifier
import androidx.compose.ui.semantics.contentDescription
import androidx.compose.ui.semantics.semantics
import androidx.compose.ui.unit.dp
import com.esuiss.ui.components.Select
import com.esuiss.ui.components.SelectGroup
import com.esuiss.ui.components.SelectItem
import com.esuiss.ui.components.SelectLabel
import com.esuiss.ui.components.SelectOption
import com.esuiss.ui.components.SelectSize

@Composable
fun SelectDisabledExample() {
    SelectDemo(initial = "mango", enabled = false)
}

val selectFruits = listOf(
    SelectOption("mango", "Mango"),
    SelectOption("banana", "Banana"),
    SelectOption("blueberry", "Blueberry"),
    SelectOption("grapes", "Grapes"),
    SelectOption("pineapple", "Pineapple"),
)

@Composable
fun SelectDemo(
    initial: String? = null,
    size: SelectSize = SelectSize.Default,
    enabled: Boolean = true,
    invalid: Boolean = false,
    label: String? = null,
) {
    var value by rememberSaveable { mutableStateOf(initial) }
    Select(
        value = value,
        onValueChange = { value = it },
        displayValue = selectFruits.firstOrNull { it.value == value }?.label,
        modifier = Modifier
            .width(192.dp)
            .then(if (label != null) Modifier.semantics { contentDescription = label } else Modifier),
        placeholder = "Select a fruit",
        size = size,
        invalid = invalid,
        enabled = enabled,
    ) {
        SelectGroup {
            SelectLabel("Fruits")
            selectFruits.forEach { SelectItem(it.value, it.label) }
        }
    }
}

Invalid

The invalid state with an error message.

iOS
Select (invalid) on iOS in light modeSelect (invalid) on iOS in dark mode
import * as React from "react"
import { View } from "react-native"
import { type Option, Select, SelectContent, SelectGroup, SelectItem, SelectLabel, SelectTrigger, SelectValue } from "@/components/ui/select"
import { Text } from "@/components/ui/text"

const fruits: Option[] = [
  { value: "mango", label: "Mango" },
  { value: "banana", label: "Banana" },
  { value: "blueberry", label: "Blueberry" },
  { value: "grapes", label: "Grapes" },
  { value: "pineapple", label: "Pineapple" },
]

function SelectDemo({
  initial,
  size,
  disabled = false,
  invalid = false,
  labelledBy,
  defaultOpen,
}: {
  initial?: Option
  size?: "default" | "sm"
  disabled?: boolean
  invalid?: boolean
  labelledBy?: string
  defaultOpen?: boolean
}) {
  const [value, setValue] = React.useState<string | null>(
    initial?.value ?? null
  )
  return (
    <Select
      items={fruits}
      value={value}
      onValueChange={setValue}
      disabled={disabled}
      defaultOpen={defaultOpen}
    >
      <SelectTrigger
        size={size}
        accessibilityLabelledBy={labelledBy}
        invalid={invalid}
        className="w-48"
      >
        <SelectValue placeholder="Select a fruit" />
      </SelectTrigger>
      <SelectContent className="w-48">
        <SelectGroup>
          <SelectLabel>Fruits</SelectLabel>
          {fruits.map((fruit) => (
            <SelectItem
              key={fruit.value}
              value={fruit.value}
              label={fruit.label}
            />
          ))}
        </SelectGroup>
      </SelectContent>
    </Select>
  )
}

export function SelectInvalidExample() {
  return (
    <View className="gap-2">
      <SelectDemo invalid />
      <Text className="text-sm text-destructive">Please select a fruit.</Text>
    </View>
  )
}
import EsuissUI
import SwiftUI

struct SelectInvalidExample: View {
  var body: some View {
    VStack(alignment: .leading, spacing: 8) {
      SelectDemo()
        .invalid()
      Text("Please select a fruit.")
        .textStyle(.sm)
        .foregroundStyle(EsuissColor.destructive)
    }
    .frame(maxWidth: .infinity, alignment: .leading)
  }
}

struct SelectOption {
  let value: String
  let label: LocalizedStringKey
}

let selectFruits = [
  SelectOption(value: "mango", label: "Mango"),
  SelectOption(value: "banana", label: "Banana"),
  SelectOption(value: "blueberry", label: "Blueberry"),
  SelectOption(value: "grapes", label: "Grapes"),
  SelectOption(value: "pineapple", label: "Pineapple"),
]

struct SelectDemo: View {
  var size: SelectSize = .default

  @State private var value: String?

  init(initial: String? = nil, size: SelectSize = .default) {
    self.size = size
    _value = State(initialValue: initial)
  }

  var body: some View {
    SelectField(selection: $value, placeholder: "Select a fruit", size: size) {
      SelectGroup {
        SelectLabel("Fruits")
        ForEach(selectFruits, id: \.value) { fruit in
          SelectItem(fruit.label, value: fruit.value)
        }
      }
    }
    .frame(width: 192)
  }
}
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.width
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.saveable.rememberSaveable
import androidx.compose.runtime.setValue
import androidx.compose.ui.Modifier
import androidx.compose.ui.semantics.contentDescription
import androidx.compose.ui.semantics.semantics
import androidx.compose.ui.unit.dp
import com.esuiss.ui.components.Select
import com.esuiss.ui.components.SelectGroup
import com.esuiss.ui.components.SelectItem
import com.esuiss.ui.components.SelectLabel
import com.esuiss.ui.components.SelectOption
import com.esuiss.ui.components.SelectSize
import com.esuiss.ui.components.Text
import com.esuiss.ui.theme.EsuissTheme
import com.esuiss.ui.theme.Typography

val selectFruits = listOf(
    SelectOption("mango", "Mango"),
    SelectOption("banana", "Banana"),
    SelectOption("blueberry", "Blueberry"),
    SelectOption("grapes", "Grapes"),
    SelectOption("pineapple", "Pineapple"),
)

@Composable
fun SelectDemo(
    initial: String? = null,
    size: SelectSize = SelectSize.Default,
    enabled: Boolean = true,
    invalid: Boolean = false,
    label: String? = null,
) {
    var value by rememberSaveable { mutableStateOf(initial) }
    Select(
        value = value,
        onValueChange = { value = it },
        displayValue = selectFruits.firstOrNull { it.value == value }?.label,
        modifier = Modifier
            .width(192.dp)
            .then(if (label != null) Modifier.semantics { contentDescription = label } else Modifier),
        placeholder = "Select a fruit",
        size = size,
        invalid = invalid,
        enabled = enabled,
    ) {
        SelectGroup {
            SelectLabel("Fruits")
            selectFruits.forEach { SelectItem(it.value, it.label) }
        }
    }
}

@Composable
fun SelectInvalid() {
    Column(verticalArrangement = Arrangement.spacedBy(8.dp)) {
        SelectDemo(invalid = true)
        Text("Please select a fruit.", style = Typography.sm, color = EsuissTheme.colors.destructive)
    }
}

API Reference

01

Select

The root that holds the selected value and the open state.

PropTypeDefaultDescription
itemsSelectItems-Value and label pairs used to show the selected label.
valueValue | Value[] | null-The selected value, for controlled use.
defaultValueValue | Value[] | null-The initial value when uncontrolled.
onValueChange(value, details) => void-Called when the selection changes.
multiplebooleanfalseAllows several values to be selected.
openboolean-Whether the popup is open, for controlled use.
defaultOpenbooleanfalseWhether the popup starts open when uncontrolled.
onOpenChange(open: boolean, details) => void-Called when the popup opens or closes.
modalbooleantrueBlocks interaction with the rest of the screen while open.
disabledbooleanfalseDisables the select.
readOnlybooleanfalseShows the value without allowing changes.
02

SelectGroup

Groups related items.

Also accepts the props of React.ComponentProps<typeof SelectPrimitive.Group>.

03

SelectValue

Shows the selected label or the placeholder.

Also accepts the props of React.ComponentProps<typeof SelectPrimitive.Value>.

04

SelectTrigger

Pressable field that opens the list.

PropTypeDefaultDescription
childrenReact.ReactNode-
size"default" | "sm""default"Trigger height: default or sm.
invalidboolean-Shows the destructive border. Falls back to the invalid state of an enclosing field.

Also accepts the props of React.ComponentProps<typeof SelectPrimitive.Trigger>.

05

SelectContent

Floating list rendered in a portal.

PropTypeDefaultDescription
portalHoststring-Name of the portal host to render into.

Also accepts the props of React.ComponentProps<typeof SelectPrimitive.Popup>, PositionerProps.

06

SelectLabel

Heading for a group.

Also accepts the props of React.ComponentProps<typeof SelectPrimitive.GroupLabel>.

07

SelectItem

One option, with a check mark when selected.

PropTypeDefaultDescription
childrenReact.ReactNode-

Also accepts the props of React.ComponentProps<typeof SelectPrimitive.Item>.

08

SelectSeparator

Line between groups.

Also accepts the props of React.ComponentProps<typeof SelectPrimitive.Separator>.

Accessibility

  • The trigger has the combobox role and reports its expanded and disabled states.
  • The list has the list role; each item has the option role, uses its label as accessibilityLabel and reports selected and disabled states.
  • Groups have the group role and are labeled by their SelectLabel.
  • Label the trigger with accessibilityLabelledBy pointing at a Label's nativeID, or with accessibilityLabel.
  • The Android back button and the VoiceOver escape gesture close the list.

Notes

  • Uses bg-secondary for the trigger, bg-popover for the list and text-primary for the check mark; colors follow the system appearance.
  • On iOS the list renders inside a FullWindowOverlay from react-native-screens, so it appears above native modals.
iOS
Select (open by default) on iOS in light modeSelect (open by default) on iOS in dark mode
import * as React from "react"
import { type Option, Select, SelectContent, SelectGroup, SelectItem, SelectLabel, SelectTrigger, SelectValue } from "@/components/ui/select"

const fruits: Option[] = [
  { value: "mango", label: "Mango" },
  { value: "banana", label: "Banana" },
  { value: "blueberry", label: "Blueberry" },
  { value: "grapes", label: "Grapes" },
  { value: "pineapple", label: "Pineapple" },
]

function SelectDemo({
  initial,
  size,
  disabled = false,
  invalid = false,
  labelledBy,
  defaultOpen,
}: {
  initial?: Option
  size?: "default" | "sm"
  disabled?: boolean
  invalid?: boolean
  labelledBy?: string
  defaultOpen?: boolean
}) {
  const [value, setValue] = React.useState<string | null>(
    initial?.value ?? null
  )
  return (
    <Select
      items={fruits}
      value={value}
      onValueChange={setValue}
      disabled={disabled}
      defaultOpen={defaultOpen}
    >
      <SelectTrigger
        size={size}
        accessibilityLabelledBy={labelledBy}
        invalid={invalid}
        className="w-48"
      >
        <SelectValue placeholder="Select a fruit" />
      </SelectTrigger>
      <SelectContent className="w-48">
        <SelectGroup>
          <SelectLabel>Fruits</SelectLabel>
          {fruits.map((fruit) => (
            <SelectItem
              key={fruit.value}
              value={fruit.value}
              label={fruit.label}
            />
          ))}
        </SelectGroup>
      </SelectContent>
    </Select>
  )
}

export function SelectOpenByDefaultExample() {
  return (
    <SelectDemo initial={fruits[2]} defaultOpen />
  )
}
components/ui/select.tsx
"use client"

import { Select as SelectPrimitive } from "@base-ui/react/select"
import {
  CaretDownIcon,
  CaretUpDownIcon,
  CaretUpIcon,
  CheckIcon,
} from "@phosphor-icons/react"
import type * as React from "react"
import { cn } from "@/lib/utils"

const Select = SelectPrimitive.Root

function trackHighlight(list: HTMLDivElement | null) {
  const indicator = list?.querySelector<HTMLElement>(
import {
  Select,
  SelectContent,
  SelectGroup,
  SelectItem,
  SelectTrigger,
  SelectValue,
} from "@/components/ui/select"

const fruits = [
  { value: "apple", label: "Apple" },
  { value: "banana", label: "Banana" },
  { value: "blueberry", label: "Blueberry" },
  { value: "grapes", label: "Grapes" },
  { value: "pineapple", label: "Pineapple" },
]

export default function SelectOpen() {
  return (
    <Select items={fruits} defaultValue="banana">
      <SelectTrigger aria-label="Fruit" className="w-48">
        <SelectValue placeholder="Select a fruit" />
      </SelectTrigger>
      <SelectContent>
        <SelectGroup>
          {fruits.map((fruit) => (
            <SelectItem key={fruit.value} value={fruit.value}>
              {fruit.label}
            </SelectItem>
          ))}
        </SelectGroup>
      </SelectContent>
    </Select>
  )
}
import {
  Select,
  SelectContent,
  SelectGroup,
  SelectItem,
  SelectTrigger,
  SelectValue,
} from "@/components/ui/select"

const fruits = [
  { value: "apple", label: "Apple" },
  { value: "banana", label: "Banana" },
  { value: "blueberry", label: "Blueberry" },
  { value: "grapes", label: "Grapes" },
  { value: "pineapple", label: "Pineapple" },
]

export default function SelectWithValue() {
  return (
    <Select items={fruits} defaultValue="blueberry">
      <SelectTrigger aria-label="Fruit" className="w-48">
        <SelectValue placeholder="Select a fruit" />
      </SelectTrigger>
      <SelectContent>
        <SelectGroup>
          {fruits.map((fruit) => (
            <SelectItem key={fruit.value} value={fruit.value}>
              {fruit.label}
            </SelectItem>
          ))}
        </SelectGroup>
      </SelectContent>
    </Select>
  )
}
import {
  Select,
  SelectContent,
  SelectItem,
  SelectTrigger,
  SelectValue,
} from "@/components/ui/select"

const fruits = [
  { value: "apple", label: "Apple" },
  { value: "banana", label: "Banana" },
  { value: "blueberry", label: "Blueberry" },
  { value: "grapes", label: "Grapes" },
  { value: "pineapple", label: "Pineapple" },
]

export default function SelectSmall() {
  return (
    <Select items={fruits}>
      <SelectTrigger aria-label="Fruit" size="sm" className="w-40">
        <SelectValue placeholder="Select a fruit" />
      </SelectTrigger>
      <SelectContent>
        {fruits.map((fruit) => (
          <SelectItem key={fruit.value} value={fruit.value}>
            {fruit.label}
          </SelectItem>
        ))}
      </SelectContent>
    </Select>
  )
}
import { Fragment } from "react"
import {
  Select,
  SelectContent,
  SelectGroup,
  SelectItem,
  SelectLabel,
  SelectSeparator,
  SelectTrigger,
  SelectValue,
} from "@/components/ui/select"

const timezones = [
  {
    label: "North America",
    items: [
      { value: "est", label: "Eastern Time" },
      { value: "cst", label: "Central Time" },
      { value: "pst", label: "Pacific Time" },
    ],
  },
  {
    label: "Europe",
    items: [
      { value: "gmt", label: "Greenwich Mean Time" },
      { value: "cet", label: "Central European Time" },
      { value: "eet", label: "Eastern European Time" },
    ],
  },
]

export default function SelectGrouped() {
  return (
    <Select
      items={timezones.flatMap((group) => group.items)}
      defaultValue="cet"
    >
      <SelectTrigger aria-label="Timezone" className="w-60">
        <SelectValue placeholder="Select a timezone" />
      </SelectTrigger>
      <SelectContent>
        {timezones.map((group, index) => (
          <Fragment key={group.label}>
            {index > 0 && <SelectSeparator />}
            <SelectGroup>
              <SelectLabel>{group.label}</SelectLabel>
              {group.items.map((item) => (
                <SelectItem key={item.value} value={item.value}>
                  {item.label}
                </SelectItem>
              ))}
            </SelectGroup>
          </Fragment>
        ))}
      </SelectContent>
    </Select>
  )
}
import { GlobeIcon, MoonIcon, SunIcon } from "@phosphor-icons/react"
import {
  Select,
  SelectContent,
  SelectItem,
  SelectTrigger,
  SelectValue,
} from "@/components/ui/select"

const themes = [
  {
    value: "light",
    label: "Light",
    icon: (
      <SunIcon />
    ),
  },
  {
    value: "dark",
    label: "Dark",
    icon: (
      <MoonIcon />
    ),
  },
  {
    value: "system",
    label: "System",
    icon: (
      <GlobeIcon />
    ),
  },
]

export default function SelectWithIcons() {
  return (
    <Select
      items={themes.map(({ value, label }) => ({ value, label }))}
      defaultValue="system"
    >
      <SelectTrigger aria-label="Theme" className="w-40">
        <SelectValue placeholder="Theme" />
      </SelectTrigger>
      <SelectContent>
        {themes.map(({ value, label, icon }) => (
          <SelectItem key={value} value={value}>
            {icon}
            {label}
          </SelectItem>
        ))}
      </SelectContent>
    </Select>
  )
}
import {
  Select,
  SelectContent,
  SelectGroup,
  SelectItem,
  SelectTrigger,
  SelectValue,
} from "@/components/ui/select"

const fruits = [
  { value: "apple", label: "Apple" },
  { value: "banana", label: "Banana" },
  { value: "blueberry", label: "Blueberry" },
  { value: "grapes", label: "Grapes" },
  { value: "pineapple", label: "Pineapple" },
]

export default function SelectDisabled() {
  return (
    <Select items={fruits} disabled>
      <SelectTrigger aria-label="Fruit" className="w-48">
        <SelectValue placeholder="Select a fruit" />
      </SelectTrigger>
      <SelectContent>
        <SelectGroup>
          {fruits.map((fruit) => (
            <SelectItem key={fruit.value} value={fruit.value}>
              {fruit.label}
            </SelectItem>
          ))}
        </SelectGroup>
      </SelectContent>
    </Select>
  )
}
import {
  Select,
  SelectContent,
  SelectItem,
  SelectTrigger,
  SelectValue,
} from "@/components/ui/select"

const fruits = [
  { value: "apple", label: "Apple" },
  { value: "banana", label: "Banana" },
  { value: "blueberry", label: "Blueberry" },
  { value: "grapes", label: "Grapes" },
  { value: "pineapple", label: "Pineapple" },
]

export default function SelectInvalid() {
  return (
    <Select items={fruits}>
      <SelectTrigger aria-label="Fruit" className="w-48" aria-invalid>
        <SelectValue placeholder="Select a fruit" />
      </SelectTrigger>
      <SelectContent>
        {fruits.map((fruit) => (
          <SelectItem key={fruit.value} value={fruit.value}>
            {fruit.label}
          </SelectItem>
        ))}
      </SelectContent>
    </Select>
  )
}
nativewind/components/ui/animated-view.tsx
import { Pressable } from "react-native"
import Animated from "react-native-reanimated"

const AnimatedPressable = Animated.createAnimatedComponent(Pressable)

type AnimatedViewProps =
  | (React.ComponentProps<typeof Animated.View> &
      React.RefAttributes<React.ComponentRef<typeof Animated.View>> & {
        as?: "View"
      })
  | (React.ComponentProps<typeof AnimatedPressable> &
      React.RefAttributes<React.ComponentRef<typeof AnimatedPressable>> & {
        as: "Pressable"
      })

function AnimatedView(props: AnimatedViewProps) {
nativewind/components/ui/select.tsx
import { cn } from "cn"
import { CaretUpDownIcon, CheckIcon } from "phosphor-react-native"
import * as React from "react"
import { Platform, type StyleProp, type ViewStyle } from "react-native"
import { FadeIn, FadeOut, ReduceMotion } from "react-native-reanimated"
import { FullWindowOverlay as RNFullWindowOverlay } from "react-native-screens"
import { useFieldRootContext } from "@/components/primitives/field"
import { useOptionalPositionerContext } from "@/components/primitives/positioner"
import * as SelectPrimitive from "@/components/primitives/select"
import {
  resolveClassName,
  resolveStyle,
} from "@/components/primitives/use-render"
import { AnimatedView } from "@/components/ui/animated-view"
import { Icon } from "@/components/ui/icon"
import { TextClassContext } from "@/components/ui/text"
stylesheet/components/ui/animated-view.tsx
import { Pressable } from "react-native"
import Animated from "react-native-reanimated"

const AnimatedPressable = Animated.createAnimatedComponent(Pressable)

type AnimatedViewProps =
  | (React.ComponentProps<typeof Animated.View> &
      React.RefAttributes<React.ComponentRef<typeof Animated.View>> & {
        as?: "View"
      })
  | (React.ComponentProps<typeof AnimatedPressable> &
      React.RefAttributes<React.ComponentRef<typeof AnimatedPressable>> & {
        as: "Pressable"
      })

function AnimatedView(props: AnimatedViewProps) {
stylesheet/components/ui/select.tsx
import { CaretUpDownIcon, CheckIcon } from "phosphor-react-native"
import * as React from "react"
import {
  Platform,
  type StyleProp,
  StyleSheet,
  type TextStyle,
  type ViewStyle,
} from "react-native"
import { FadeIn, FadeOut, ReduceMotion } from "react-native-reanimated"
import { FullWindowOverlay as RNFullWindowOverlay } from "react-native-screens"
import { useFieldRootContext } from "@/components/primitives/field"
import { useOptionalPositionerContext } from "@/components/primitives/positioner"
import * as SelectPrimitive from "@/components/primitives/select"
import { resolveStyle } from "@/components/primitives/use-render"
import { AnimatedView } from "@/components/ui/animated-view"