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

Combobox

A text input with a filterable list of options, for choosing one or more values by typing.

Combobox 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.

Combobox is built on the Base UI Combobox. The Combobox root takes the items to choose from and holds the value. ComboboxInput renders the text field inside an InputGroup, with an optional button that opens the list and an optional clear button. ComboboxContent renders the popup in a portal; inside it, ComboboxList renders each item through a render function, ComboboxEmpty appears when nothing matches, and ComboboxItem shows a check mark when selected. Group related items with ComboboxGroup, ComboboxLabel, ComboboxCollection and ComboboxSeparator.

For multiple selection, replace ComboboxInput with ComboboxChips, which shows each selected value as a ComboboxChip next to a ComboboxChipsInput. Use a combobox when the list is long enough that typing to filter helps. For a short, fixed list without filtering, use a select; for a list of actions rather than values, use a command menu.

Installation

CLI

$ pnpm dlx shadcn@latest add @esuiss/combobox

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 combobox

Manual

  1. Install the dependencies and add the cn helper:
$ pnpm add @base-ui/react @phosphor-icons/react
  1. Add the pieces it builds on: Button, Input Group.

  2. Copy the source into components/ui/combobox.tsx:

  1. Update the import paths to match your project.

Usage

import { ComboboxValue, ComboboxTrigger, ComboboxInput, ComboboxContent, ComboboxList, ComboboxItem, ComboboxGroup, ComboboxLabel, ComboboxCollection, ComboboxEmpty, ComboboxSeparator, ComboboxChips, ComboboxChip, ComboboxChipsInput } 
<div
















Pass items to the root and a function as the child of ComboboxList, which receives each item and returns a ComboboxItem with a matching value. The value is uncontrolled with defaultValue, or controlled with value and onValueChange. To read or control the typed text, use inputValue and onInputValueChange.

For grouped data, pass groups as items, render a ComboboxGroup with its own items for each one, and render its items with ComboboxCollection. For multiple selection, set multiple on the root, create a ref with useComboboxAnchor, attach it to ComboboxChips, and pass it as anchor to ComboboxContent so the popup aligns to the chips field. Use ComboboxValue with a render function to map the selected values to chips.

Examples

With clear button

A combobox with a starting value and a clear button, enabled with showClear.

Disabled

A disabled combobox whose input and toggle button cannot be focused or used.

Grouped

Options grouped by region with labels and separators between groups.

Highlight match

A controlled inputValue used to emphasize the matching part of each option.

Multiple

Multiple selection shown as removable chips, with Backspace removing the last chip.

API Reference

Anatomy11 parts
<Combobox>
<ComboboxInput />
<ComboboxContent>
<ComboboxEmpty />
<ComboboxList>
<ComboboxItem />
<ComboboxGroup>
01

Combobox

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

Also accepts the props of ComboboxPrimitive.Root.

02

ComboboxValue

Renders the current value, or passes it to a render function, for example to draw chips.

Also accepts the props of ComboboxPrimitive.Value.Props.

03

ComboboxTrigger

A button that opens the list, with a caret icon.

Also accepts the props of ComboboxPrimitive.Trigger.Props.

04

ComboboxInput

The text field, rendered inside an InputGroup with optional open and clear buttons.

PropTypeDefaultDescription
showTriggerbooleantrueShows the button at the end of the field that opens the list.
showClearbooleanfalseShows a clear button at the end of the field. When shown, it hides the open button.

Also accepts the props of ComboboxPrimitive.Input.Props.

05

ComboboxContent

The popup, rendered in a portal and positioned below the input.

Also accepts the props of ComboboxPrimitive.Popup.Props, ComboboxPrimitive.Positioner.Props.

06

ComboboxList

The scrollable list that renders items through a render function.

Also accepts the props of ComboboxPrimitive.List.Props.

07

ComboboxItem

A selectable option that shows a check mark when selected.

Also accepts the props of ComboboxPrimitive.Item.Props.

08

ComboboxGroup

Groups related options and takes its own items.

Also accepts the props of ComboboxPrimitive.Group.Props.

09

ComboboxLabel

The visible label of a group.

Also accepts the props of ComboboxPrimitive.GroupLabel.Props.

10

ComboboxCollection

Renders the items of a group through a render function.

Also accepts the props of ComboboxPrimitive.Collection.Props.

11

ComboboxEmpty

Content shown when no options match the typed text.

Also accepts the props of ComboboxPrimitive.Empty.Props.

12

ComboboxSeparator

A divider between groups.

Also accepts the props of ComboboxPrimitive.Separator.Props.

13

ComboboxChips

The field that holds selected values as chips in multiple selection.

Also accepts the props of React.ComponentPropsWithRef<typeof ComboboxPrimitive.Chips>, ComboboxPrimitive.Chips.Props.

14

ComboboxChip

A selected value shown as a chip, with an optional remove button.

PropTypeDefaultDescription
showRemovebooleantrueShows a button inside the chip that removes the value.
removeLabelstringRemove {value}Accessible name of the remove button. Defaults to Remove followed by the chip text.

Also accepts the props of ComboboxPrimitive.Chip.Props.

15

ComboboxChipsInput

The text field placed after the chips in multiple selection.

Also accepts the props of ComboboxPrimitive.Input.Props.

Accessibility

  • The input has the combobox role and sets aria-expanded to reflect whether the list is open.
  • The popup has the listbox role, options have the option role and selected options set aria-selected; groups have the group role and take their name from ComboboxLabel.
  • Typing filters the options. Arrow keys move the highlight, Enter selects the highlighted option, and Escape closes the list.
  • In multiple selection, Backspace in the empty input removes the last chip.
  • The open button is labelled Show options and the clear button is labelled Clear.
  • When disabled, the input and the open button are disabled and removed from the Tab order.
  • Give the input an accessible name with a visible label or aria-label.

Notes

  • Styling hooks: data-slot values such as combobox-content, combobox-list, combobox-item, combobox-label, combobox-empty, combobox-chips, combobox-chip, combobox-chip-remove and combobox-chip-input.
  • Highlighted items get data-highlighted and disabled items get data-disabled.
  • The popup matches the width of its anchor through --anchor-width and limits its height to the available space.
  • Uses the popover, accent, primary, secondary and border colors.

Combobox pairs a text input with a floating list that filters its items as the user types. ComboboxInput draws the field with a caret trigger and an optional clear button, and ComboboxContent renders the list through a portal (inside a full-window overlay on iOS), positioned below the input and as wide as it.

For multiple selection, set multiple and use ComboboxChips with a ComboboxChip per selected value and a ComboboxChipsInput for typing. Each chip has a remove button.

Installation

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

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

$ pnpm dlx @esuiss/ui@latest add combobox
$ pnpm dlx @esuiss/ui@latest add combobox --platform swift
NextCommandPreviousCollapsible

On this page

  1. 01Installation
    • CLI
    • Manual
  2. 02Usage
  3. 03Examples
    • With clear button
    • Disabled
    • Grouped
    • Highlight match
    • Multiple
  4. 04API Reference
    • Combobox
    • ComboboxValue
    • ComboboxTrigger
    • ComboboxInput
    • ComboboxContent
    • ComboboxList
    • ComboboxItem
    • ComboboxGroup
    • ComboboxLabel
    • ComboboxCollection
    • ComboboxEmpty
    • ComboboxSeparator
    • ComboboxChips
    • ComboboxChip
    • ComboboxChipsInput
  5. 05Accessibility
  6. 06Notes
  7. 07Installation
    • Manual
  8. 08Usage
  9. 09Examples
    • Default
    • With value
    • With clear
    • Grouped
    • Multiple
    • With label
    • Disabled
    • Invalid
  10. 10API Reference
    • Combobox
    • ComboboxTrigger
    • ComboboxInput
    • ComboboxContent
    • ComboboxList
    • ComboboxItem
    • ComboboxGroup
    • ComboboxLabel
    • ComboboxEmpty
    • ComboboxSeparator
    • ComboboxChips
    • ComboboxChip
    • ComboboxChipsInput
  11. 11Accessibility
  12. 12Notes
from
"@/components/ui/combobox"
className
=
"w-72"
>
<Combobox items={frameworks}>
<ComboboxInput
placeholder="Select a framework"
disabled={false}
/>
<ComboboxContent>
<ComboboxEmpty>No framework found.</ComboboxEmpty>
<ComboboxList>
{(item: string) => (
<ComboboxItem key={item} value={item}>
{item}
</ComboboxItem>
)}
</ComboboxList>
</ComboboxContent>
</Combobox>
</div>
<ComboboxSeparator />
<ComboboxLabel />
<ComboboxCollection>
<ComboboxItem />
</ComboboxCollection>
</ComboboxGroup>
</ComboboxList>
</ComboboxContent>
</Combobox>
$ pnpm dlx @esuiss/ui@latest add combobox --platform kotlin

The CLI also adds what it builds on: Button, 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: Button, Icon, Text, and the primitives components/primitives/combobox.tsx, components/primitives/field.tsx, components/primitives/positioner.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/combobox.tsx:
  1. Update the import paths to match your project.
  1. Install the dependencies:
npx expo install cn nativewind phosphor-react-native react-native-reanimated react-native-screens react-native-svg react-native-worklets
  1. Add the pieces it builds on: Button, Icon, Text, and the primitives components/primitives/combobox.tsx, components/primitives/field.tsx, components/primitives/positioner.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/combobox.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: Button, Icon, Text, and the primitives components/primitives/combobox.tsx, components/primitives/field.tsx, components/primitives/positioner.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/combobox.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/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/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/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/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/Checkbox.kt
package com.esuiss.ui.components

import androidx.compose.foundation.background
import androidx.compose.foundation.border
import androidx.compose.foundation.interaction.MutableInteractionSource
import androidx.compose.foundation.interaction.collectIsPressedAsState
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.selection.triStateToggleable
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.remember
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.alpha
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/DropdownMenu.kt
package com.esuiss.ui.components

import androidx.compose.animation.core.animateFloatAsState
import androidx.compose.foundation.background
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.RowScope
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
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/Haptics.kt
package com.esuiss.ui.components

import androidx.compose.runtime.Composable
import androidx.compose.runtime.CompositionLocalProvider
import androidx.compose.runtime.remember
import androidx.compose.runtime.staticCompositionLocalOf
import androidx.compose.ui.hapticfeedback.HapticFeedback
import androidx.compose.ui.hapticfeedback.HapticFeedbackType
import androidx.compose.ui.platform.LocalHapticFeedback

val LocalHapticsEnabled = staticCompositionLocalOf { true }

@Composable
fun ProvideHaptics(enabled: Boolean, content: @Composable () -> Unit) {
    CompositionLocalProvider(LocalHapticsEnabled provides enabled, content = content)
}
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/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/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 { ComboboxTrigger, ComboboxInput, ComboboxContent, ComboboxList, ComboboxItem, ComboboxGroup, ComboboxLabel, ComboboxEmpty, ComboboxSeparator, ComboboxChips, ComboboxChip, ComboboxChipsInput } from "@/components/ui/combobox"
<ComboboxDemo defaultValue="Expo" defaultOpen />

Pass items to the root and render the filtered list with a function child of ComboboxList. For grouped items, render a ComboboxGroup per group with a ComboboxLabel and a ComboboxCollection. Add ComboboxEmpty for the no-results message.

Use value, defaultValue and onValueChange for the selection, and inputValue with onInputValueChange for the typed text. For object items, provide itemToStringLabel. filter, limit and locale tune matching.

With chips, create a ref with useComboboxAnchor(), attach it to ComboboxChips, and pass it to ComboboxContent as anchor so the list lines up with the chips field. On ComboboxInput, set showClear to add a clear button and showTrigger={false} to hide the caret.

Examples

Default

Web

Web preview

import * as React from "react"
import { Combobox, ComboboxContent, ComboboxEmpty, ComboboxInput, ComboboxItem, ComboboxList } from "@/components/ui/combobox"

const frameworks = [
  "Next.js",
  "SvelteKit",
  "Nuxt.js",
  "Remix",
  "Astro",
  "Gatsby",
  "Expo",
]

function ComboboxDemo({
  defaultValue = null,
  defaultOpen,
  disabled,
  showClear,
  labelledBy,
  invalid,
}: {
  defaultValue?: string | null
  defaultOpen?: boolean
  disabled?: boolean
  showClear?: boolean
  labelledBy?: string
  invalid?: boolean
}) {
  const [value, setValue] = React.useState<string | null>(defaultValue)
  return (
    <Combobox
      items={frameworks}
      value={value}
      onValueChange={setValue}
      defaultOpen={defaultOpen}
      disabled={disabled}
    >
      <ComboboxInput
        className="w-64"
        placeholder="Select a framework"
        showClear={showClear}
        disabled={disabled}
        invalid={invalid}
        accessibilityLabelledBy={labelledBy}
      />
      <ComboboxContent>
        <ComboboxEmpty>No framework found.</ComboboxEmpty>
        <ComboboxList>
          {(item: string) => (
            <ComboboxItem key={item} value={item}>
              {item}
            </ComboboxItem>
          )}
        </ComboboxList>
      </ComboboxContent>
    </Combobox>
  )
}

export function ComboboxExample() {
  return (
    <ComboboxDemo />
  )
}
import EsuissUI
import SwiftUI

let comboboxFrameworks = [
  "Next.js",
  "SvelteKit",
  "Nuxt.js",
  "Remix",
  "Astro",
  "Gatsby",
  "Expo",
]

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

  init(initial: String? = nil) {
    _value = State(initialValue: initial)
  }

  var body: some View {
    Combobox(
      comboboxFrameworks,
      selection: $value,
      placeholder: "Select a framework",
      emptyText: "No framework found."
    )
    .frame(width: 256)
  }
}
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.Combobox

val ComboboxFrameworks = listOf("Next.js", "SvelteKit", "Nuxt.js", "Remix", "Astro", "Gatsby", "Expo")

@Composable
fun ComboboxDemo(
    defaultValue: String? = null,
    disabled: Boolean = false,
    showClear: Boolean = false,
    label: String? = null,
    invalid: Boolean? = null,
) {
    var value by rememberSaveable { mutableStateOf(defaultValue) }
    Column {
        Combobox(
            items = ComboboxFrameworks,
            value = value,
            onValueChange = { value = it },
            modifier = Modifier
                .width(256.dp)
                .then(if (label != null) Modifier.semantics { contentDescription = label } else Modifier),
            placeholder = "Select a framework",
            emptyText = "No framework found.",
            showClear = showClear,
            invalid = invalid,
            enabled = !disabled,
        )
    }
}

With value

Starts with a selected value.

Web

Web preview

import * as React from "react"
import { Combobox, ComboboxContent, ComboboxEmpty, ComboboxInput, ComboboxItem, ComboboxList } from "@/components/ui/combobox"

const frameworks = [
  "Next.js",
  "SvelteKit",
  "Nuxt.js",
  "Remix",
  "Astro",
  "Gatsby",
  "Expo",
]

function ComboboxDemo({
  defaultValue = null,
  defaultOpen,
  disabled,
  showClear,
  labelledBy,
  invalid,
}: {
  defaultValue?: string | null
  defaultOpen?: boolean
  disabled?: boolean
  showClear?: boolean
  labelledBy?: string
  invalid?: boolean
}) {
  const [value, setValue] = React.useState<string | null>(defaultValue)
  return (
    <Combobox
      items={frameworks}
      value={value}
      onValueChange={setValue}
      defaultOpen={defaultOpen}
      disabled={disabled}
    >
      <ComboboxInput
        className="w-64"
        placeholder="Select a framework"
        showClear={showClear}
        disabled={disabled}
        invalid={invalid}
        accessibilityLabelledBy={labelledBy}
      />
      <ComboboxContent>
        <ComboboxEmpty>No framework found.</ComboboxEmpty>
        <ComboboxList>
          {(item: string) => (
            <ComboboxItem key={item} value={item}>
              {item}
            </ComboboxItem>
          )}
        </ComboboxList>
      </ComboboxContent>
    </Combobox>
  )
}

export function ComboboxWithValueExample() {
  return (
    <ComboboxDemo defaultValue="Expo" />
  )
}
import EsuissUI
import SwiftUI

struct ComboboxWithValueExample: View {
  var body: some View {
    ComboboxDemo(initial: "Expo")
  }
}

let comboboxFrameworks = [
  "Next.js",
  "SvelteKit",
  "Nuxt.js",
  "Remix",
  "Astro",
  "Gatsby",
  "Expo",
]

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

  init(initial: String? = nil) {
    _value = State(initialValue: initial)
  }

  var body: some View {
    Combobox(
      comboboxFrameworks,
      selection: $value,
      placeholder: "Select a framework",
      emptyText: "No framework found."
    )
    .frame(width: 256)
  }
}
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.Combobox

@Composable
fun ComboboxWithValueExample() {
    ComboboxDemo(defaultValue = "Expo")
}

val ComboboxFrameworks = listOf("Next.js", "SvelteKit", "Nuxt.js", "Remix", "Astro", "Gatsby", "Expo")

@Composable
fun ComboboxDemo(
    defaultValue: String? = null,
    disabled: Boolean = false,
    showClear: Boolean = false,
    label: String? = null,
    invalid: Boolean? = null,
) {
    var value by rememberSaveable { mutableStateOf(defaultValue) }
    Column {
        Combobox(
            items = ComboboxFrameworks,
            value = value,
            onValueChange = { value = it },
            modifier = Modifier
                .width(256.dp)
                .then(if (label != null) Modifier.semantics { contentDescription = label } else Modifier),
            placeholder = "Select a framework",
            emptyText = "No framework found.",
            showClear = showClear,
            invalid = invalid,
            enabled = !disabled,
        )
    }
}

With clear

A clear button through showClear.

Web

Web preview

import * as React from "react"
import { Combobox, ComboboxContent, ComboboxEmpty, ComboboxInput, ComboboxItem, ComboboxList } from "@/components/ui/combobox"

const frameworks = [
  "Next.js",
  "SvelteKit",
  "Nuxt.js",
  "Remix",
  "Astro",
  "Gatsby",
  "Expo",
]

function ComboboxDemo({
  defaultValue = null,
  defaultOpen,
  disabled,
  showClear,
  labelledBy,
  invalid,
}: {
  defaultValue?: string | null
  defaultOpen?: boolean
  disabled?: boolean
  showClear?: boolean
  labelledBy?: string
  invalid?: boolean
}) {
  const [value, setValue] = React.useState<string | null>(defaultValue)
  return (
    <Combobox
      items={frameworks}
      value={value}
      onValueChange={setValue}
      defaultOpen={defaultOpen}
      disabled={disabled}
    >
      <ComboboxInput
        className="w-64"
        placeholder="Select a framework"
        showClear={showClear}
        disabled={disabled}
        invalid={invalid}
        accessibilityLabelledBy={labelledBy}
      />
      <ComboboxContent>
        <ComboboxEmpty>No framework found.</ComboboxEmpty>
        <ComboboxList>
          {(item: string) => (
            <ComboboxItem key={item} value={item}>
              {item}
            </ComboboxItem>
          )}
        </ComboboxList>
      </ComboboxContent>
    </Combobox>
  )
}

export function ComboboxWithClearExample() {
  return (
    <ComboboxDemo defaultValue="Remix" showClear />
  )
}
import EsuissUI
import SwiftUI

struct ComboboxWithClearExample: View {
  var body: some View {
    ComboboxDemo(initial: "Remix")
  }
}

let comboboxFrameworks = [
  "Next.js",
  "SvelteKit",
  "Nuxt.js",
  "Remix",
  "Astro",
  "Gatsby",
  "Expo",
]

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

  init(initial: String? = nil) {
    _value = State(initialValue: initial)
  }

  var body: some View {
    Combobox(
      comboboxFrameworks,
      selection: $value,
      placeholder: "Select a framework",
      emptyText: "No framework found."
    )
    .frame(width: 256)
  }
}
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.Combobox

@Composable
fun ComboboxWithClearExample() {
    ComboboxDemo(defaultValue = "Remix", showClear = true)
}

val ComboboxFrameworks = listOf("Next.js", "SvelteKit", "Nuxt.js", "Remix", "Astro", "Gatsby", "Expo")

@Composable
fun ComboboxDemo(
    defaultValue: String? = null,
    disabled: Boolean = false,
    showClear: Boolean = false,
    label: String? = null,
    invalid: Boolean? = null,
) {
    var value by rememberSaveable { mutableStateOf(defaultValue) }
    Column {
        Combobox(
            items = ComboboxFrameworks,
            value = value,
            onValueChange = { value = it },
            modifier = Modifier
                .width(256.dp)
                .then(if (label != null) Modifier.semantics { contentDescription = label } else Modifier),
            placeholder = "Select a framework",
            emptyText = "No framework found.",
            showClear = showClear,
            invalid = invalid,
            enabled = !disabled,
        )
    }
}

Grouped

Items in labelled groups with separators.

Web

Web preview

import * as React from "react"
import { Combobox, ComboboxCollection, ComboboxContent, ComboboxEmpty, ComboboxGroup, ComboboxInput, ComboboxItem, ComboboxLabel, ComboboxList, ComboboxSeparator } from "@/components/ui/combobox"

interface Produce {
  value: string
  label: string
}

const produce: { value: string; items: Produce[] }[] = [
  {
    value: "Fruits",
    items: [
      { value: "mango", label: "Mango" },
      { value: "banana", label: "Banana" },
      { value: "blueberry", label: "Blueberry" },
    ],
  },
  {
    value: "Vegetables",
    items: [
      { value: "carrot", label: "Carrot" },
      { value: "broccoli", label: "Broccoli" },
      { value: "spinach", label: "Spinach" },
    ],
  },
]

function GroupedDemo() {
  return (
    <Combobox items={produce} itemToStringLabel={(item: Produce) => item.label}>
      <ComboboxInput className="w-64" placeholder="Select produce" />
      <ComboboxContent>
        <ComboboxEmpty>No produce found.</ComboboxEmpty>
        <ComboboxList>
          {(group: (typeof produce)[number], index: number) => (
            <React.Fragment key={group.value}>
              {index > 0 ? <ComboboxSeparator /> : null}
              <ComboboxGroup items={group.items}>
                <ComboboxLabel>{group.value}</ComboboxLabel>
                <ComboboxCollection>
                  {(item: Produce) => (
                    <ComboboxItem key={item.value} value={item}>
                      {item.label}
                    </ComboboxItem>
                  )}
                </ComboboxCollection>
              </ComboboxGroup>
            </React.Fragment>
          )}
        </ComboboxList>
      </ComboboxContent>
    </Combobox>
  )
}

export function ComboboxGroupedExample() {
  return (
    <GroupedDemo />
  )
}
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.unit.dp
import com.esuiss.ui.components.Combobox

data class ComboboxProduce(val value: String, val label: String, val group: String)

val ComboboxProduceItems = listOf(
    ComboboxProduce("mango", "Mango", "Fruits"),
    ComboboxProduce("banana", "Banana", "Fruits"),
    ComboboxProduce("blueberry", "Blueberry", "Fruits"),
    ComboboxProduce("carrot", "Carrot", "Vegetables"),
    ComboboxProduce("broccoli", "Broccoli", "Vegetables"),
    ComboboxProduce("spinach", "Spinach", "Vegetables"),
)

@Composable
fun ComboboxGrouped() {
    var value by rememberSaveable { mutableStateOf<String?>(null) }
    Column {
        Combobox(
            items = ComboboxProduceItems,
            value = ComboboxProduceItems.firstOrNull { it.value == value },
            onValueChange = { value = it?.value },
            modifier = Modifier.width(256.dp),
            itemText = { it.label },
            placeholder = "Select produce",
            emptyText = "No produce found.",
            groupBy = { it.group },
        )
    }
}

Multiple

Multiple selection shown as removable chips.

Web

Web preview

import * as React from "react"
import { Combobox, ComboboxChip, ComboboxChips, ComboboxChipsInput, ComboboxContent, ComboboxEmpty, ComboboxItem, ComboboxList, useComboboxAnchor } from "@/components/ui/combobox"

const frameworks = [
  "Next.js",
  "SvelteKit",
  "Nuxt.js",
  "Remix",
  "Astro",
  "Gatsby",
  "Expo",
]

function MultipleDemo() {
  const anchor = useComboboxAnchor()
  const [value, setValue] = React.useState<string[]>(["Expo", "Astro"])
  return (
    <Combobox
      multiple
      items={frameworks}
      value={value}
      onValueChange={setValue}
    >
      <ComboboxChips ref={anchor} className="w-72">
        {value.map((item) => (
          <ComboboxChip key={item} value={item}>
            {item}
          </ComboboxChip>
        ))}
        <ComboboxChipsInput placeholder="Add framework" />
      </ComboboxChips>
      <ComboboxContent anchor={anchor}>
        <ComboboxEmpty>No framework found.</ComboboxEmpty>
        <ComboboxList>
          {(item: string) => (
            <ComboboxItem key={item} value={item}>
              {item}
            </ComboboxItem>
          )}
        </ComboboxList>
      </ComboboxContent>
    </Combobox>
  )
}

export function ComboboxMultipleExample() {
  return (
    <MultipleDemo />
  )
}
import EsuissUI
import SwiftUI

let comboboxFrameworks = [
  "Next.js",
  "SvelteKit",
  "Nuxt.js",
  "Remix",
  "Astro",
  "Gatsby",
  "Expo",
]

struct ComboboxMultipleDemo: View {
  @State private var value = ["Expo", "Astro"]

  var body: some View {
    Combobox(
      comboboxFrameworks,
      selection: $value,
      placeholder: "Add framework",
      emptyText: "No framework found."
    )
    .frame(width: 288)
  }
}
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.unit.dp
import com.esuiss.ui.components.MultiCombobox

val ComboboxFrameworks = listOf("Next.js", "SvelteKit", "Nuxt.js", "Remix", "Astro", "Gatsby", "Expo")

@Composable
fun ComboboxMultiple() {
    var values by rememberSaveable { mutableStateOf(listOf("Expo", "Astro")) }
    Column {
        MultiCombobox(
            items = ComboboxFrameworks,
            values = values,
            onValuesChange = { values = it },
            modifier = Modifier.width(288.dp),
            placeholder = "Add framework",
            emptyText = "No framework found.",
        )
    }
}

With label

Linked to a Label with helper text.

Web

Web preview

import * as React from "react"
import { View } from "react-native"
import { Combobox, ComboboxContent, ComboboxEmpty, ComboboxInput, ComboboxItem, ComboboxList } from "@/components/ui/combobox"
import { Label } from "@/components/ui/label"
import { Text } from "@/components/ui/text"

const frameworks = [
  "Next.js",
  "SvelteKit",
  "Nuxt.js",
  "Remix",
  "Astro",
  "Gatsby",
  "Expo",
]

function ComboboxDemo({
  defaultValue = null,
  defaultOpen,
  disabled,
  showClear,
  labelledBy,
  invalid,
}: {
  defaultValue?: string | null
  defaultOpen?: boolean
  disabled?: boolean
  showClear?: boolean
  labelledBy?: string
  invalid?: boolean
}) {
  const [value, setValue] = React.useState<string | null>(defaultValue)
  return (
    <Combobox
      items={frameworks}
      value={value}
      onValueChange={setValue}
      defaultOpen={defaultOpen}
      disabled={disabled}
    >
      <ComboboxInput
        className="w-64"
        placeholder="Select a framework"
        showClear={showClear}
        disabled={disabled}
        invalid={invalid}
        accessibilityLabelledBy={labelledBy}
      />
      <ComboboxContent>
        <ComboboxEmpty>No framework found.</ComboboxEmpty>
        <ComboboxList>
          {(item: string) => (
            <ComboboxItem key={item} value={item}>
              {item}
            </ComboboxItem>
          )}
        </ComboboxList>
      </ComboboxContent>
    </Combobox>
  )
}

export function ComboboxWithLabelExample() {
  return (
    <View className="gap-2">
      <Label nativeID="combobox-framework">Framework</Label>
      <ComboboxDemo labelledBy="combobox-framework" />
      <Text className="text-sm text-muted-foreground">
        Search and pick the framework you use.
      </Text>
    </View>
  )
}
import EsuissUI
import SwiftUI

struct ComboboxWithLabelExample: View {
  var body: some View {
    VStack(alignment: .leading, spacing: 8) {
      FieldLabel("Framework")
        .accessibilityHidden(true)
      ComboboxDemo()
        .accessibilityLabel(Text("Framework"))
      Text("Search and pick the framework you use.")
        .textStyle(.sm)
        .foregroundStyle(EsuissColor.mutedForeground)
    }
  }
}

let comboboxFrameworks = [
  "Next.js",
  "SvelteKit",
  "Nuxt.js",
  "Remix",
  "Astro",
  "Gatsby",
  "Expo",
]

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

  init(initial: String? = nil) {
    _value = State(initialValue: initial)
  }

  var body: some View {
    Combobox(
      comboboxFrameworks,
      selection: $value,
      placeholder: "Select a framework",
      emptyText: "No framework found."
    )
    .frame(width: 256)
  }
}
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.fillMaxWidth
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.Combobox
import com.esuiss.ui.components.Label
import com.esuiss.ui.components.Text
import com.esuiss.ui.theme.EsuissTheme
import com.esuiss.ui.theme.Typography

val ComboboxFrameworks = listOf("Next.js", "SvelteKit", "Nuxt.js", "Remix", "Astro", "Gatsby", "Expo")

@Composable
fun ComboboxDemo(
    defaultValue: String? = null,
    disabled: Boolean = false,
    showClear: Boolean = false,
    label: String? = null,
    invalid: Boolean? = null,
) {
    var value by rememberSaveable { mutableStateOf(defaultValue) }
    Column {
        Combobox(
            items = ComboboxFrameworks,
            value = value,
            onValueChange = { value = it },
            modifier = Modifier
                .width(256.dp)
                .then(if (label != null) Modifier.semantics { contentDescription = label } else Modifier),
            placeholder = "Select a framework",
            emptyText = "No framework found.",
            showClear = showClear,
            invalid = invalid,
            enabled = !disabled,
        )
    }
}

@Composable
fun ComboboxWithLabel() {
    Column(Modifier.fillMaxWidth(), verticalArrangement = Arrangement.spacedBy(8.dp)) {
        Label("Framework")
        ComboboxDemo(label = "Framework")
        Text(
            "Search and pick the framework you use.",
            style = Typography.sm.copy(color = EsuissTheme.colors.mutedForeground),
        )
    }
}

Disabled

A disabled combobox.

Web

Web preview

import * as React from "react"
import { Combobox, ComboboxContent, ComboboxEmpty, ComboboxInput, ComboboxItem, ComboboxList } from "@/components/ui/combobox"

const frameworks = [
  "Next.js",
  "SvelteKit",
  "Nuxt.js",
  "Remix",
  "Astro",
  "Gatsby",
  "Expo",
]

function ComboboxDemo({
  defaultValue = null,
  defaultOpen,
  disabled,
  showClear,
  labelledBy,
  invalid,
}: {
  defaultValue?: string | null
  defaultOpen?: boolean
  disabled?: boolean
  showClear?: boolean
  labelledBy?: string
  invalid?: boolean
}) {
  const [value, setValue] = React.useState<string | null>(defaultValue)
  return (
    <Combobox
      items={frameworks}
      value={value}
      onValueChange={setValue}
      defaultOpen={defaultOpen}
      disabled={disabled}
    >
      <ComboboxInput
        className="w-64"
        placeholder="Select a framework"
        showClear={showClear}
        disabled={disabled}
        invalid={invalid}
        accessibilityLabelledBy={labelledBy}
      />
      <ComboboxContent>
        <ComboboxEmpty>No framework found.</ComboboxEmpty>
        <ComboboxList>
          {(item: string) => (
            <ComboboxItem key={item} value={item}>
              {item}
            </ComboboxItem>
          )}
        </ComboboxList>
      </ComboboxContent>
    </Combobox>
  )
}

export function ComboboxDisabledExample() {
  return (
    <ComboboxDemo defaultValue="Astro" disabled />
  )
}
import EsuissUI
import SwiftUI

struct ComboboxDisabledExample: View {
  var body: some View {
    ComboboxDemo(initial: "Astro")
      .disabled(true)
  }
}

let comboboxFrameworks = [
  "Next.js",
  "SvelteKit",
  "Nuxt.js",
  "Remix",
  "Astro",
  "Gatsby",
  "Expo",
]

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

  init(initial: String? = nil) {
    _value = State(initialValue: initial)
  }

  var body: some View {
    Combobox(
      comboboxFrameworks,
      selection: $value,
      placeholder: "Select a framework",
      emptyText: "No framework found."
    )
    .frame(width: 256)
  }
}
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.Combobox

@Composable
fun ComboboxDisabledExample() {
    ComboboxDemo(defaultValue = "Astro", disabled = true)
}

val ComboboxFrameworks = listOf("Next.js", "SvelteKit", "Nuxt.js", "Remix", "Astro", "Gatsby", "Expo")

@Composable
fun ComboboxDemo(
    defaultValue: String? = null,
    disabled: Boolean = false,
    showClear: Boolean = false,
    label: String? = null,
    invalid: Boolean? = null,
) {
    var value by rememberSaveable { mutableStateOf(defaultValue) }
    Column {
        Combobox(
            items = ComboboxFrameworks,
            value = value,
            onValueChange = { value = it },
            modifier = Modifier
                .width(256.dp)
                .then(if (label != null) Modifier.semantics { contentDescription = label } else Modifier),
            placeholder = "Select a framework",
            emptyText = "No framework found.",
            showClear = showClear,
            invalid = invalid,
            enabled = !disabled,
        )
    }
}

Invalid

The invalid style with an error message.

Web

Web preview

import * as React from "react"
import { View } from "react-native"
import { Combobox, ComboboxContent, ComboboxEmpty, ComboboxInput, ComboboxItem, ComboboxList } from "@/components/ui/combobox"
import { Text } from "@/components/ui/text"

const frameworks = [
  "Next.js",
  "SvelteKit",
  "Nuxt.js",
  "Remix",
  "Astro",
  "Gatsby",
  "Expo",
]

function ComboboxDemo({
  defaultValue = null,
  defaultOpen,
  disabled,
  showClear,
  labelledBy,
  invalid,
}: {
  defaultValue?: string | null
  defaultOpen?: boolean
  disabled?: boolean
  showClear?: boolean
  labelledBy?: string
  invalid?: boolean
}) {
  const [value, setValue] = React.useState<string | null>(defaultValue)
  return (
    <Combobox
      items={frameworks}
      value={value}
      onValueChange={setValue}
      defaultOpen={defaultOpen}
      disabled={disabled}
    >
      <ComboboxInput
        className="w-64"
        placeholder="Select a framework"
        showClear={showClear}
        disabled={disabled}
        invalid={invalid}
        accessibilityLabelledBy={labelledBy}
      />
      <ComboboxContent>
        <ComboboxEmpty>No framework found.</ComboboxEmpty>
        <ComboboxList>
          {(item: string) => (
            <ComboboxItem key={item} value={item}>
              {item}
            </ComboboxItem>
          )}
        </ComboboxList>
      </ComboboxContent>
    </Combobox>
  )
}

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

struct ComboboxInvalidExample: View {
  var body: some View {
    VStack(alignment: .leading, spacing: 8) {
      ComboboxDemo()
        .invalid()
      Text("Please select a framework.")
        .textStyle(.sm)
        .foregroundStyle(EsuissColor.destructive)
    }
  }
}

let comboboxFrameworks = [
  "Next.js",
  "SvelteKit",
  "Nuxt.js",
  "Remix",
  "Astro",
  "Gatsby",
  "Expo",
]

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

  init(initial: String? = nil) {
    _value = State(initialValue: initial)
  }

  var body: some View {
    Combobox(
      comboboxFrameworks,
      selection: $value,
      placeholder: "Select a framework",
      emptyText: "No framework found."
    )
    .frame(width: 256)
  }
}
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.fillMaxWidth
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.Combobox
import com.esuiss.ui.components.Text
import com.esuiss.ui.theme.EsuissTheme
import com.esuiss.ui.theme.Typography

val ComboboxFrameworks = listOf("Next.js", "SvelteKit", "Nuxt.js", "Remix", "Astro", "Gatsby", "Expo")

@Composable
fun ComboboxDemo(
    defaultValue: String? = null,
    disabled: Boolean = false,
    showClear: Boolean = false,
    label: String? = null,
    invalid: Boolean? = null,
) {
    var value by rememberSaveable { mutableStateOf(defaultValue) }
    Column {
        Combobox(
            items = ComboboxFrameworks,
            value = value,
            onValueChange = { value = it },
            modifier = Modifier
                .width(256.dp)
                .then(if (label != null) Modifier.semantics { contentDescription = label } else Modifier),
            placeholder = "Select a framework",
            emptyText = "No framework found.",
            showClear = showClear,
            invalid = invalid,
            enabled = !disabled,
        )
    }
}

@Composable
fun ComboboxInvalid() {
    Column(Modifier.fillMaxWidth(), verticalArrangement = Arrangement.spacedBy(8.dp)) {
        ComboboxDemo(invalid = true)
        Text("Please select a framework.", style = Typography.sm.copy(color = EsuissTheme.colors.destructive))
    }
}

API Reference

01

Combobox

The root that holds the items, the selected value, the typed text and the open state.

PropTypeDefaultDescription
itemsItem[] | ComboboxItemGroup<Item>[]-The items to filter, flat or grouped.
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.
inputValuestring-The typed text, for controlled use.
onInputValueChange(inputValue: string, details) => void-Called when the typed text changes.
filterComboboxFilter<Item> | null-Custom filter function; null turns filtering off.
limitnumber-The maximum number of items shown.
itemToStringLabel(itemValue: Value) => string-Returns the label shown for an item value.
openOnInputClickbooleantrueOpens the list when the input is pressed.
openboolean-Whether the list is open, for controlled use.
defaultOpenbooleanfalseWhether the list starts open when uncontrolled.
onOpenChange(open: boolean, details) => void-Called when the list opens or closes.
disabledbooleanfalseDisables the combobox.
readOnlybooleanfalseShows the value without allowing changes.
02

ComboboxTrigger

Round button that opens the list.

PropTypeDefaultDescription
childrenReact.ReactNode-

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

03

ComboboxInput

Text field with caret trigger and optional clear button.

PropTypeDefaultDescription
classNamestring-
childrenReact.ReactNode-
showTriggerbooleantrueShows the caret button. Defaults to true.
showClearbooleanfalseShows a clear button when there is text or a value.
invalidboolean-Shows the destructive border; also read from a surrounding Field.

Also accepts the props of React.ComponentProps<typeof ComboboxPrimitive.Input>.

04

ComboboxContent

Floating popup for the list.

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

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

05

ComboboxList

Scrollable list of items.

Also accepts the props of React.ComponentProps<typeof ComboboxPrimitive.List>.

06

ComboboxItem

Selectable option with a check mark when selected.

PropTypeDefaultDescription
childrenReact.ReactNode-

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

07

ComboboxGroup

Group of items.

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

08

ComboboxLabel

Heading for a group.

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

09

ComboboxEmpty

Message shown when nothing matches.

Also accepts the props of React.ComponentProps<typeof ComboboxPrimitive.Empty>.

10

ComboboxSeparator

Line between groups.

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

11

ComboboxChips

Field that holds chips for multiple selection.

PropTypeDefaultDescription
invalidboolean-Shows the destructive border.

Also accepts the props of React.ComponentProps<typeof ComboboxPrimitive.Chips>.

12

ComboboxChip

Selected value with a remove button.

PropTypeDefaultDescription
showRemovebooleantrueShows the remove button. Defaults to true.
removeLabelstringRemove {value}Accessible name of the remove button. Defaults to Remove followed by the chip text.

Also accepts the props of React.ComponentProps<typeof ComboboxPrimitive.Chip>.

13

ComboboxChipsInput

Text input inside the chips field.

Also accepts the props of React.ComponentProps<typeof ComboboxPrimitive.Input>.

Accessibility

  • The input has the combobox role with expanded and disabled state.
  • Items have the option role with selected and disabled state; the list has the list role.
  • The empty message is a polite live region.
  • The trigger, clear and chip remove buttons are labelled "Show options", "Clear" and "Remove".

Notes

  • Uses bg-secondary, bg-popover, border-ring and border-destructive tokens; light and dark follow the system appearance.
  • In Uniwind and NativeWind, a focused field also draws the ring-3 ring-ring/50 focus ring.
  • Fade animations respect the system Reduce Motion setting.
Web

Web preview

import * as React from "react"
import { Combobox, ComboboxContent, ComboboxEmpty, ComboboxInput, ComboboxItem, ComboboxList } from "@/components/ui/combobox"

const frameworks = [
  "Next.js",
  "SvelteKit",
  "Nuxt.js",
  "Remix",
  "Astro",
  "Gatsby",
  "Expo",
]

function ComboboxDemo({
  defaultValue = null,
  defaultOpen,
  disabled,
  showClear,
  labelledBy,
  invalid,
}: {
  defaultValue?: string | null
  defaultOpen?: boolean
  disabled?: boolean
  showClear?: boolean
  labelledBy?: string
  invalid?: boolean
}) {
  const [value, setValue] = React.useState<string | null>(defaultValue)
  return (
    <Combobox
      items={frameworks}
      value={value}
      onValueChange={setValue}
      defaultOpen={defaultOpen}
      disabled={disabled}
    >
      <ComboboxInput
        className="w-64"
        placeholder="Select a framework"
        showClear={showClear}
        disabled={disabled}
        invalid={invalid}
        accessibilityLabelledBy={labelledBy}
      />
      <ComboboxContent>
        <ComboboxEmpty>No framework found.</ComboboxEmpty>
        <ComboboxList>
          {(item: string) => (
            <ComboboxItem key={item} value={item}>
              {item}
            </ComboboxItem>
          )}
        </ComboboxList>
      </ComboboxContent>
    </Combobox>
  )
}

export function ComboboxOpenByDefaultExample() {
  return (
    <ComboboxDemo defaultValue="Expo" defaultOpen />
  )
}
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.Combobox

@Composable
fun ComboboxOpenByDefaultExample() {
    ComboboxDemo(defaultValue = "Expo")
}

val ComboboxFrameworks = listOf("Next.js", "SvelteKit", "Nuxt.js", "Remix", "Astro", "Gatsby", "Expo")

@Composable
fun ComboboxDemo(
    defaultValue: String? = null,
    disabled: Boolean = false,
    showClear: Boolean = false,
    label: String? = null,
    invalid: Boolean? = null,
) {
    var value by rememberSaveable { mutableStateOf(defaultValue) }
    Column {
        Combobox(
            items = ComboboxFrameworks,
            value = value,
            onValueChange = { value = it },
            modifier = Modifier
                .width(256.dp)
                .then(if (label != null) Modifier.semantics { contentDescription = label } else Modifier),
            placeholder = "Select a framework",
            emptyText = "No framework found.",
            showClear = showClear,
            invalid = invalid,
            enabled = !disabled,
        )
    }
}
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) {
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) {
import {
  Combobox,
  ComboboxContent,
  ComboboxEmpty,
  ComboboxInput,
  ComboboxItem,
  ComboboxList,
} from "@/components/ui/combobox"

const frameworks = ["Next.js", "Remix", "Astro", "SvelteKit", "Nuxt", "Gatsby"]

export default function ComboboxDemo() {
  return (
    <div className="w-72">
      <Combobox items={frameworks}>
        <ComboboxInput placeholder="Select a framework" disabled={false} />
        <ComboboxContent>
          <ComboboxEmpty>No framework found.</ComboboxEmpty>
          <ComboboxList>
            {(item: string) => (
              <ComboboxItem key={item} value={item}>
                {item}
              </ComboboxItem>
            )}
          </ComboboxList>
        </ComboboxContent>
      </Combobox>
    </div>
  )
}
components/ui/combobox.tsx
"use client"

import { Combobox as ComboboxPrimitive } from "@base-ui/react"
import { CaretDownIcon, CheckIcon, XIcon } from "@phosphor-icons/react"
import * as React from "react"
import { Button } from "@/components/ui/button"
import {
  InputGroup,
  InputGroupAddon,
  InputGroupButton,
  InputGroupInput,
} from "@/components/ui/input-group"
import { cn } from "@/lib/utils"

const Combobox = ComboboxPrimitive.Root
import {
  Combobox,
  ComboboxContent,
  ComboboxEmpty,
  ComboboxInput,
  ComboboxItem,
  ComboboxList,
} from "@/components/ui/combobox"

const frameworks = ["Next.js", "Remix", "Astro", "SvelteKit", "Nuxt", "Gatsby"]

export default function ComboboxWithClearButton() {
  return (
    <div className="w-72">
      <Combobox items={frameworks} defaultValue="Astro">
        <ComboboxInput placeholder="Select a framework" showClear />
        <ComboboxContent>
          <ComboboxEmpty>No framework found.</ComboboxEmpty>
          <ComboboxList>
            {(item: string) => (
              <ComboboxItem key={item} value={item}>
                {item}
              </ComboboxItem>
            )}
          </ComboboxList>
        </ComboboxContent>
      </Combobox>
    </div>
  )
}
import {
  Combobox,
  ComboboxContent,
  ComboboxEmpty,
  ComboboxInput,
  ComboboxItem,
  ComboboxList,
} from "@/components/ui/combobox"

const frameworks = ["Next.js", "Remix", "Astro", "SvelteKit", "Nuxt", "Gatsby"]

export default function ComboboxDisabled() {
  return (
    <div className="w-72">
      <Combobox disabled items={frameworks}>
        <ComboboxInput placeholder="Select a framework" disabled={true} />
        <ComboboxContent>
          <ComboboxEmpty>No framework found.</ComboboxEmpty>
          <ComboboxList>
            {(item: string) => (
              <ComboboxItem key={item} value={item}>
                {item}
              </ComboboxItem>
            )}
          </ComboboxList>
        </ComboboxContent>
      </Combobox>
    </div>
  )
}
import {
  Combobox,
  ComboboxCollection,
  ComboboxContent,
  ComboboxEmpty,
  ComboboxGroup,
  ComboboxInput,
  ComboboxItem,
  ComboboxLabel,
  ComboboxList,
  ComboboxSeparator,
} from "@/components/ui/combobox"

const timezones = [
  {
    value: "Americas",
    items: ["New York", "Chicago", "Los Angeles", "São Paulo"],
  },
  {
    value: "Europe",
    items: ["London", "Berlin", "Istanbul", "Madrid"],
  },
  {
    value: "Asia",
    items: ["Tokyo", "Singapore", "Dubai"],
  },
]

export default function ComboboxGrouped() {
  return (
    <div className="w-72">
      <Combobox items={timezones}>
        <ComboboxInput placeholder="Select a city" />
        <ComboboxContent>
          <ComboboxEmpty>No city found.</ComboboxEmpty>
          <ComboboxList>
            {(group: (typeof timezones)[number], index: number) => (
              <ComboboxGroup key={group.value} items={group.items}>
                {index > 0 && <ComboboxSeparator />}
                <ComboboxLabel>{group.value}</ComboboxLabel>
                <ComboboxCollection>
                  {(item: string) => (
                    <ComboboxItem key={item} value={item}>
                      {item}
                    </ComboboxItem>
                  )}
                </ComboboxCollection>
              </ComboboxGroup>
            )}
          </ComboboxList>
        </ComboboxContent>
      </Combobox>
    </div>
  )
}
import * as React from "react"
import {
  Combobox,
  ComboboxContent,
  ComboboxEmpty,
  ComboboxInput,
  ComboboxItem,
  ComboboxList,
} from "@/components/ui/combobox"

const frameworks = ["Next.js", "Remix", "Astro", "SvelteKit", "Nuxt", "Gatsby"]

function HighlightedMatch({ text, query }: { text: string; query: string }) {
  const start = query ? text.toLowerCase().indexOf(query.toLowerCase()) : -1
  if (start < 0) return text
  return (
    <span>
      {text.slice(0, start)}
      <span className="font-semibold">
        {text.slice(start, start + query.length)}
      </span>
      {text.slice(start + query.length)}
    </span>
  )
}

function HighlightMatchCombobox() {
  const [query, setQuery] = React.useState("")
  return (
    <Combobox
      items={frameworks}
      inputValue={query}
      onInputValueChange={setQuery}
    >
      <ComboboxInput placeholder="Type to filter" />
      <ComboboxContent>
        <ComboboxEmpty>No framework found.</ComboboxEmpty>
        <ComboboxList>
          {(item: string) => (
            <ComboboxItem key={item} value={item}>
              <HighlightedMatch text={item} query={query} />
            </ComboboxItem>
          )}
        </ComboboxList>
      </ComboboxContent>
    </Combobox>
  )
}

export default function ComboboxHighlightMatch() {
  return (
    <div className="w-72">
      <HighlightMatchCombobox />
    </div>
  )
}
import {
  Combobox,
  ComboboxChip,
  ComboboxChips,
  ComboboxChipsInput,
  ComboboxContent,
  ComboboxEmpty,
  ComboboxItem,
  ComboboxList,
  ComboboxValue,
  useComboboxAnchor,
} from "@/components/ui/combobox"

const frameworks = ["Next.js", "Remix", "Astro", "SvelteKit", "Nuxt", "Gatsby"]

function MultipleCombobox({ disabled }: { disabled?: boolean }) {
  const anchor = useComboboxAnchor()

  return (
    <Combobox
      disabled={disabled}
      multiple
      items={frameworks}
      defaultValue={["Next.js", "Astro"]}
    >
      <ComboboxChips ref={anchor}>
        <ComboboxValue>
          {(values: string[]) => (
            <>
              {values.map((value) => (
                <ComboboxChip key={value}>{value}</ComboboxChip>
              ))}
              <ComboboxChipsInput placeholder="Add framework" />
            </>
          )}
        </ComboboxValue>
      </ComboboxChips>
      <ComboboxContent anchor={anchor}>
        <ComboboxEmpty>No framework found.</ComboboxEmpty>
        <ComboboxList>
          {(item: string) => (
            <ComboboxItem key={item} value={item}>
              {item}
            </ComboboxItem>
          )}
        </ComboboxList>
      </ComboboxContent>
    </Combobox>
  )
}

export default function ComboboxMultiple() {
  return (
    <div className="w-72">
      <MultipleCombobox disabled={false} />
    </div>
  )
}
uniwind/components/ui/combobox.tsx
import { cn } from "cn"
import { CaretDownIcon, CheckIcon, XIcon } from "phosphor-react-native"
import * as React from "react"
import {
  Platform,
  type StyleProp,
  type View,
  type ViewStyle,
} from "react-native"
import { FadeIn, FadeOut, ReduceMotion } from "react-native-reanimated"
import { FullWindowOverlay as RNFullWindowOverlay } from "react-native-screens"
import * as ComboboxPrimitive from "@/components/primitives/combobox"
import { useFieldRootContext } from "@/components/primitives/field"
import { useOptionalPositionerContext } from "@/components/primitives/positioner"
import {
  resolveClassName,
swift/Components/Combobox.swift
import PhosphorSwift
import SwiftUI

public struct Combobox<Item: Hashable & Sendable>: View {
  let items: [Item]
  let single: Binding<Item?>?
  let multiple: Binding<[Item]>?
  let placeholder: LocalizedStringKey
  let searchPlaceholder: LocalizedStringKey
  let emptyText: LocalizedStringKey
  let title: (Item) -> String

  @State private var open = false
  @Environment(\.isEnabled) private var isEnabled
  @Environment(\.isInvalid) private var invalid
  @Environment(\.colorScheme) private var colorScheme
kotlin/components/Combobox.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.PressInteraction
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.FlowRow
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.heightIn
swift/Components/Item.swift
import SwiftUI

public enum ItemVariant: CaseIterable, Sendable {
  case `default`, outline, muted
}

public enum ItemSize: CaseIterable, Sendable {
  case `default`, sm, xs

  var horizontalPadding: CGFloat {
    switch self {
    case .default: 16
    case .sm: 14
    case .xs: 12
    }
  }
nativewind/components/ui/combobox.tsx
import { cn } from "cn"
import { cssInterop } from "nativewind"
import { CaretDownIcon, CheckIcon, XIcon } from "phosphor-react-native"
import * as React from "react"
import {
  Platform,
  type StyleProp,
  TextInput,
  type View,
  type ViewStyle,
} from "react-native"
import { FadeIn, FadeOut, ReduceMotion } from "react-native-reanimated"
import { FullWindowOverlay as RNFullWindowOverlay } from "react-native-screens"
import * as ComboboxPrimitive from "@/components/primitives/combobox"
import { useFieldRootContext } from "@/components/primitives/field"
import { useOptionalPositionerContext } from "@/components/primitives/positioner"
stylesheet/components/ui/combobox.tsx
import { CaretDownIcon, CheckIcon, XIcon } from "phosphor-react-native"
import * as React from "react"
import {
  Platform,
  type StyleProp,
  StyleSheet,
  type TextStyle,
  type View,
  type ViewStyle,
} from "react-native"
import { FadeIn, FadeOut, ReduceMotion } from "react-native-reanimated"
import { FullWindowOverlay as RNFullWindowOverlay } from "react-native-screens"
import * as ComboboxPrimitive from "@/components/primitives/combobox"
import { useFieldRootContext } from "@/components/primitives/field"
import { useOptionalPositionerContext } from "@/components/primitives/positioner"
import { resolveStyle } from "@/components/primitives/use-render"