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

Dropdown Menu

A menu of actions or options that opens from a button.

Dropdown Menu 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.

Dropdown menu wraps the menu primitive of the chosen base. The DropdownMenu root manages the open state and DropdownMenuTrigger opens it. DropdownMenuContent renders the menu in a portal, positioned below the trigger. Inside it, use DropdownMenuItem for actions, DropdownMenuCheckboxItem for toggles, and DropdownMenuRadioGroup with DropdownMenuRadioItem for a single choice. Organize items with DropdownMenuGroup, DropdownMenuLabel and DropdownMenuSeparator, and nest a submenu with DropdownMenuSub, DropdownMenuSubTrigger and DropdownMenuSubContent. DropdownMenuShortcut displays a keyboard shortcut at the end of an item.

Use a dropdown menu for actions and settings behind a visible button. For actions on an object opened with a right-click, use a context menu; to choose a value for a form field, use a select.

Installation

CLI

$ pnpm dlx shadcn@latest add @esuiss/dropdown-menu

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 dropdown-menu

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: use-platform.

  2. Copy the source into components/ui/dropdown-menu.tsx:

  1. Update the import paths to match your project.

Usage

import { DropdownMenu, DropdownMenuPortal, DropdownMenuTrigger, DropdownMenuContent, DropdownMenuGroup, DropdownMenuLabel, DropdownMenuItem, DropdownMenuSub, DropdownMenuSubTrigger, DropdownMenuSubContent, DropdownMenuCheckboxItem, DropdownMenuRadioGroup, DropdownMenuRadioItem, DropdownMenuSeparator, DropdownMenuShortcut } 
<



















































<



















































<

















































The menu is uncontrolled by default; set defaultOpen to start open, or control it with open and onOpenChange. Render the trigger as another component, such as a Button, with the render prop.

The menu is uncontrolled by default; set defaultOpen to start open, or control it with open and onOpenChange. Set asChild on DropdownMenuTrigger to render your own Button as the trigger.

The menu is uncontrolled by default; set defaultOpen to start open, or control it with isOpen and onOpenChange. The Button placed first inside DropdownMenu opens it.

Selecting a regular item closes the menu.

Checkbox and radio items keep the menu open so several choices can be made in a row.

Checkbox items are uncontrolled with defaultChecked, or controlled with checked and onCheckedChange. Radio groups take defaultValue, or value and onValueChange, and each radio item needs a value.

Checkbox items are uncontrolled with defaultSelected, or controlled with isSelected and onChange. Radio groups take defaultValue, or value and onChange, and each radio item needs a value.

Set inset on items and labels without an icon to align their text with items that have one, and set variant="destructive" on items that delete or remove something.

Examples

Checkboxes

Controlled checkbox items that toggle interface elements, including a disabled item.

Radio group

A controlled radio group for choosing a single panel position.

Inset

A labelled group of inset items without icons, each with a keyboard shortcut.

API Reference

Anatomy14 parts
<DropdownMenu>
<DropdownMenuTrigger />
<DropdownMenuContent>
<DropdownMenuGroup>
<DropdownMenuLabel />
<DropdownMenuItem>
<DropdownMenuShortcut
01

DropdownMenu

The root that manages the menu's open state.

Also accepts the props of the primitive part it wraps.

02

DropdownMenuPortal

Renders its children into a portal outside the current DOM hierarchy.

Also accepts the props of the primitive part it wraps.

03

DropdownMenuTrigger

The button that opens the menu.

Also accepts the props of the primitive part it wraps.

04

DropdownMenuContent

The menu panel, rendered in a portal and positioned relative to the trigger.

Also accepts the props of the primitive parts it wraps.

05

DropdownMenuGroup

Groups related items.

Also accepts the props of the primitive part it wraps.

06

DropdownMenuLabel

A heading for a group of items.

PropTypeDefaultDescription
insetboolean-Adds leading padding so the label aligns with items that have an icon or indicator.

Also accepts the props of the primitive part it wraps.

07

DropdownMenuItem

An action in the menu.

PropTypeDefaultDescription
insetboolean-Adds leading padding so the item aligns with items that have an icon or indicator.
variant"default" | "destructive""default"destructive colors the item and its icon with the destructive color.

Also accepts the props of the primitive part it wraps.

08

DropdownMenuSub

The root of a nested submenu.

Also accepts the props of the primitive part it wraps.

09

DropdownMenuSubTrigger

The item that opens a submenu, shown with a trailing caret.

PropTypeDefaultDescription
insetboolean-Adds leading padding so the trigger aligns with items that have an icon or indicator.

Also accepts the props of the primitive part it wraps.

10

DropdownMenuSubContent

The panel of a submenu, opened beside its trigger.

Also accepts the props of React.ComponentProps<typeof DropdownMenuContent>.

11

DropdownMenuCheckboxItem

An item that toggles a setting on or off and shows a check mark when on.

PropTypeDefaultDescription
insetboolean-Adds leading padding so the item aligns with other inset items.

Also accepts the props of the primitive part it wraps.

12

DropdownMenuRadioGroup

Groups radio items so that only one can be selected.

Also accepts the props of the primitive part it wraps.

13

DropdownMenuRadioItem

An item in a radio group that shows a check mark when selected.

PropTypeDefaultDescription
insetboolean-Adds leading padding so the item aligns with other inset items.

Also accepts the props of the primitive part it wraps.

14

DropdownMenuSeparator

A divider between groups of items.

Also accepts the props of the primitive part it wraps.

15

DropdownMenuShortcut

Displays a keyboard shortcut at the end of an item.

PropTypeDefaultDescription
modboolean-Prefixes the shortcut with the platform modifier key, ⌘ on Apple platforms and Ctrl+ elsewhere.

Also accepts the props of React.ComponentProps<"span">.

Accessibility

  • The trigger sets aria-expanded. The menu has the menu role; items have the menuitem, menuitemcheckbox or menuitemradio role, and checkbox and radio items set aria-checked.
  • Groups have the group role and take their name from DropdownMenuLabel.
  • Opening the menu from the keyboard focuses the first item. Arrow keys move between items, and disabled items set aria-disabled.
  • On a submenu trigger, the Right Arrow opens the submenu and moves focus into it, and the Left Arrow returns to the trigger. The arrow directions follow the reading direction.
  • Escape closes the menu and returns focus to the trigger.
  • DropdownMenuShortcut only displays a shortcut; it does not register one. It is hidden on small screens.

Notes

  • Styling hooks: data-slot values such as dropdown-menu-content, dropdown-menu-item, dropdown-menu-checkbox-item, dropdown-menu-checkbox-item-indicator, dropdown-menu-radio-item, dropdown-menu-radio-item-indicator, dropdown-menu-sub-trigger, dropdown-menu-label, dropdown-menu-separator and dropdown-menu-shortcut.
  • Items set data-inset and data-variant; disabled items get data-disabled, and an open submenu trigger gets data-popup-open.
  • By default the menu opens below the trigger, aligned to start, and matches the trigger's width with a minimum of 12rem. Override the width with className.
  • Uses the popover, accent, destructive and border colors.

DropdownMenu opens DropdownMenuContent next to a DropdownMenuTrigger when it is pressed. The content renders through a portal (inside a full-window overlay on iOS) as a raised, rounded card positioned below the trigger by default.

Items can be plain actions, checkbox items, radio items in a group, or a submenu. DropdownMenuSubContent expands in place under its trigger, and the caret turns to point down while it is open. For a menu that opens on long press at the touch point, use Context Menu.

Installation

$ pnpm dlx shadcn@latest add @esuiss-rn/dropdown-menu

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

$ pnpm dlx @esuiss/ui@latest add dropdown-menu
$ pnpm dlx @esuiss/ui@latest add dropdown-menu --platform swift
NextEmptyPreviousDrawer

On this page

  1. 01Installation
    • CLI
    • Manual
  2. 02Usage
  3. 03Examples
    • Checkboxes
    • Radio group
    • Inset
  4. 04API Reference
    • DropdownMenu
    • DropdownMenuPortal
    • DropdownMenuTrigger
    • DropdownMenuContent
    • DropdownMenuGroup
    • DropdownMenuLabel
    • DropdownMenuItem
    • DropdownMenuSub
    • DropdownMenuSubTrigger
    • DropdownMenuSubContent
    • DropdownMenuCheckboxItem
    • DropdownMenuRadioGroup
    • DropdownMenuRadioItem
    • DropdownMenuSeparator
    • DropdownMenuShortcut
  5. 05Accessibility
  6. 06Notes
  7. 07Installation
    • Manual
  8. 08Usage
  9. 09Examples
    • Default
    • Checkboxes
    • Radio group
    • Inset
  10. 10API Reference
    • DropdownMenu
    • DropdownMenuContent
    • DropdownMenuSubTrigger
    • DropdownMenuSubContent
    • DropdownMenuItem
    • DropdownMenuCheckboxItem
    • DropdownMenuRadioItem
    • DropdownMenuLabel
    • DropdownMenuSeparator
  11. 11Accessibility
  12. 12Notes
from
"@/components/ui/dropdown-menu"
DropdownMenu
>
<DropdownMenuTrigger render={<Button variant="outline" />}>
Open menu
</DropdownMenuTrigger>
<DropdownMenuContent className="w-56">
<DropdownMenuGroup>
<DropdownMenuLabel>My account</DropdownMenuLabel>
<DropdownMenuItem>
<UserIcon />
Profile
<DropdownMenuShortcut mod>⇧P</DropdownMenuShortcut>
</DropdownMenuItem>
<DropdownMenuItem>
<CreditCardIcon />
Billing
<DropdownMenuShortcut mod>B</DropdownMenuShortcut>
</DropdownMenuItem>
<DropdownMenuItem>
<GearIcon />
Settings
<DropdownMenuShortcut mod>,</DropdownMenuShortcut>
</DropdownMenuItem>
</DropdownMenuGroup>
<DropdownMenuSeparator />
<DropdownMenuSub>
<DropdownMenuSubTrigger>
<UserPlusIcon />
Invite users
</DropdownMenuSubTrigger>
<DropdownMenuSubContent>
<DropdownMenuItem>
<EnvelopeIcon />
Email
</DropdownMenuItem>
<DropdownMenuItem>
<ChatCircleIcon />
Message
</DropdownMenuItem>
</DropdownMenuSubContent>
</DropdownMenuSub>
<DropdownMenuItem disabled>API access</DropdownMenuItem>
<DropdownMenuSeparator />
<DropdownMenuItem variant="destructive">
<TrashIcon />
Delete account
</DropdownMenuItem>
<DropdownMenuItem>
<SignOutIcon />
Log out
<DropdownMenuShortcut mod>⇧Q</DropdownMenuShortcut>
</DropdownMenuItem>
</DropdownMenuContent>
</DropdownMenu>
DropdownMenu
>
<DropdownMenuTrigger asChild>
<Button variant="outline">Open menu</Button>
</DropdownMenuTrigger>
<DropdownMenuContent className="w-56">
<DropdownMenuGroup>
<DropdownMenuLabel>My account</DropdownMenuLabel>
<DropdownMenuItem>
<UserIcon />
Profile
<DropdownMenuShortcut mod>⇧P</DropdownMenuShortcut>
</DropdownMenuItem>
<DropdownMenuItem>
<CreditCardIcon />
Billing
<DropdownMenuShortcut mod>B</DropdownMenuShortcut>
</DropdownMenuItem>
<DropdownMenuItem>
<GearIcon />
Settings
<DropdownMenuShortcut mod>,</DropdownMenuShortcut>
</DropdownMenuItem>
</DropdownMenuGroup>
<DropdownMenuSeparator />
<DropdownMenuSub>
<DropdownMenuSubTrigger>
<UserPlusIcon />
Invite users
</DropdownMenuSubTrigger>
<DropdownMenuSubContent>
<DropdownMenuItem>
<EnvelopeIcon />
Email
</DropdownMenuItem>
<DropdownMenuItem>
<ChatCircleIcon />
Message
</DropdownMenuItem>
</DropdownMenuSubContent>
</DropdownMenuSub>
<DropdownMenuItem disabled>API access</DropdownMenuItem>
<DropdownMenuSeparator />
<DropdownMenuItem variant="destructive">
<TrashIcon />
Delete account
</DropdownMenuItem>
<DropdownMenuItem>
<SignOutIcon />
Log out
<DropdownMenuShortcut mod>⇧Q</DropdownMenuShortcut>
</DropdownMenuItem>
</DropdownMenuContent>
</DropdownMenu>
DropdownMenu
>
<Button variant="outline">Open menu</Button>
<DropdownMenuContent className="w-56">
<DropdownMenuGroup>
<DropdownMenuLabel>My account</DropdownMenuLabel>
<DropdownMenuItem>
<UserIcon />
Profile
<DropdownMenuShortcut mod>⇧P</DropdownMenuShortcut>
</DropdownMenuItem>
<DropdownMenuItem>
<CreditCardIcon />
Billing
<DropdownMenuShortcut mod>B</DropdownMenuShortcut>
</DropdownMenuItem>
<DropdownMenuItem>
<GearIcon />
Settings
<DropdownMenuShortcut mod>,</DropdownMenuShortcut>
</DropdownMenuItem>
</DropdownMenuGroup>
<DropdownMenuSeparator />
<DropdownMenuSub>
<DropdownMenuSubTrigger>
<UserPlusIcon />
Invite users
</DropdownMenuSubTrigger>
<DropdownMenuSubContent>
<DropdownMenuItem>
<EnvelopeIcon />
Email
</DropdownMenuItem>
<DropdownMenuItem>
<ChatCircleIcon />
Message
</DropdownMenuItem>
</DropdownMenuSubContent>
</DropdownMenuSub>
<DropdownMenuItem disabled>API access</DropdownMenuItem>
<DropdownMenuSeparator />
<DropdownMenuItem variant="destructive">
<TrashIcon />
Delete account
</DropdownMenuItem>
<DropdownMenuItem>
<SignOutIcon />
Log out
<DropdownMenuShortcut mod>⇧Q</DropdownMenuShortcut>
</DropdownMenuItem>
</DropdownMenuContent>
</DropdownMenu>
/>
</DropdownMenuItem>
</DropdownMenuGroup>
<DropdownMenuSeparator />
<DropdownMenuSub>
<DropdownMenuSubTrigger />
<DropdownMenuSubContent>
<DropdownMenuItem />
</DropdownMenuSubContent>
</DropdownMenuSub>
<DropdownMenuItem>
<DropdownMenuShortcut />
</DropdownMenuItem>
</DropdownMenuContent>
</DropdownMenu>
$ pnpm dlx @esuiss/ui@latest add dropdown-menu --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/menu.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/dropdown-menu.tsx:
  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/menu.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/dropdown-menu.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/menu.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/dropdown-menu.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/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/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/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/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/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/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/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 { DropdownMenuContent, DropdownMenuSubTrigger, DropdownMenuSubContent, DropdownMenuItem, DropdownMenuCheckboxItem, DropdownMenuRadioItem, DropdownMenuLabel, DropdownMenuSeparator } from "@/components/ui/dropdown-menu"
<View className="items-start">
  <DropdownMenu defaultOpen>
    <OpenButton />
    <DropdownMenuContent className="w-56" align="start">
      <DropdownMenuLabel>My account</DropdownMenuLabel>
      <DropdownMenuGroup>
        <DropdownMenuItem>
          <Icon as={UserIcon} className="size-4" />
          <Text>Profile</Text>
        </DropdownMenuItem>
        <DropdownMenuItem>
          <Icon as={CreditCardIcon} className="size-4" />
          <Text>Billing</Text>
        </DropdownMenuItem>
        <DropdownMenuItem>
          <Icon as={GearIcon} className="size-4" />
          <Text>Settings</Text>
        </DropdownMenuItem>
      </DropdownMenuGroup>
      <DropdownMenuSeparator />
      <DropdownMenuSub>
        <DropdownMenuSubTrigger>
          <View className="flex-row items-center gap-2">
            <Icon as={UserPlusIcon} className="size-4" />
            <Text>Invite users</Text>
          </View>
        </DropdownMenuSubTrigger>
        <DropdownMenuSubContent>
          <DropdownMenuItem>
            <Icon as={EnvelopeIcon} className="size-4" />
            <Text>Email</Text>
          </DropdownMenuItem>
          <DropdownMenuItem>
            <Icon as={ChatCircleIcon} className="size-4" />
            <Text>Message</Text>
          </DropdownMenuItem>
        </DropdownMenuSubContent>
      </DropdownMenuSub>
      <DropdownMenuSeparator />
      <DropdownMenuItem variant="destructive">
        <Icon as={SignOutIcon} className="size-4" />
        <Text>Log out</Text>
      </DropdownMenuItem>
    </DropdownMenuContent>
  </DropdownMenu>
</View>

Use render on DropdownMenuTrigger to render it as a Button. Items close the menu when pressed unless closeOnPress={false}.

Use DropdownMenuCheckboxItem with checked and onCheckedChange, and DropdownMenuRadioItem inside a DropdownMenuRadioGroup with value and onValueChange. Group items with DropdownMenuGroup and DropdownMenuLabel, and divide sections with DropdownMenuSeparator.

Set variant="destructive" on an item that deletes or removes. Use inset on items without an icon to align them with items that have one. Position the content with side, align, sideOffset and alignOffset.

Examples

Default

iOS
Dropdown Menu on iOS in light modeDropdown Menu on iOS in dark mode
import { ChatCircleIcon, CreditCardIcon, EnvelopeIcon, GearIcon, SignOutIcon, UserIcon, UserPlusIcon } from "phosphor-react-native"
import { View } from "react-native"
import { Button } from "@/components/ui/button"
import { DropdownMenu, DropdownMenuContent, DropdownMenuGroup, DropdownMenuItem, DropdownMenuLabel, DropdownMenuSeparator, DropdownMenuSub, DropdownMenuSubContent, DropdownMenuSubTrigger, DropdownMenuTrigger } from "@/components/ui/dropdown-menu"
import { Icon } from "@/components/ui/icon"
import { Text } from "@/components/ui/text"

function OpenButton({ label = "Open menu" }: { label?: string }) {
  return (
    <DropdownMenuTrigger render={<Button variant="outline" />}>
      <Text>{label}</Text>
    </DropdownMenuTrigger>
  )
}

export function DropdownMenuExample() {
  return (
    <View className="items-start">
      <DropdownMenu>
        <OpenButton />
        <DropdownMenuContent className="w-56" align="start">
          <DropdownMenuLabel>My account</DropdownMenuLabel>
          <DropdownMenuGroup>
            <DropdownMenuItem>
              <Icon as={UserIcon} className="size-4" />
              <Text>Profile</Text>
            </DropdownMenuItem>
            <DropdownMenuItem>
              <Icon as={CreditCardIcon} className="size-4" />
              <Text>Billing</Text>
            </DropdownMenuItem>
            <DropdownMenuItem>
              <Icon as={GearIcon} className="size-4" />
              <Text>Settings</Text>
            </DropdownMenuItem>
          </DropdownMenuGroup>
          <DropdownMenuSeparator />
          <DropdownMenuSub>
            <DropdownMenuSubTrigger>
              <View className="flex-row items-center gap-2">
                <Icon as={UserPlusIcon} className="size-4" />
                <Text>Invite users</Text>
              </View>
            </DropdownMenuSubTrigger>
            <DropdownMenuSubContent>
              <DropdownMenuItem>
                <Icon as={EnvelopeIcon} className="size-4" />
                <Text>Email</Text>
              </DropdownMenuItem>
              <DropdownMenuItem>
                <Icon as={ChatCircleIcon} className="size-4" />
                <Text>Message</Text>
              </DropdownMenuItem>
            </DropdownMenuSubContent>
          </DropdownMenuSub>
          <DropdownMenuSeparator />
          <DropdownMenuItem variant="destructive">
            <Icon as={SignOutIcon} className="size-4" />
            <Text>Log out</Text>
          </DropdownMenuItem>
        </DropdownMenuContent>
      </DropdownMenu>
    </View>
  )
}
import EsuissUI
import PhosphorSwift
import SwiftUI

struct DropdownMenuAccount: View {
  @State private var open: Bool

  init(open: Bool = false) {
    _open = State(initialValue: open)
  }

  var body: some View {
    DropdownMenu(isPresented: $open) {
      DropdownMenuLabel("My account")
      DropdownMenuGroup {
        DropdownMenuItem("Profile", icon: Ph.user.regular) {}
        DropdownMenuItem("Billing", icon: Ph.creditCard.regular) {}
        DropdownMenuItem("Settings", icon: Ph.gear.regular) {}
      }
      DropdownMenuSeparator()
      DropdownMenuSub("Invite users", icon: Ph.userPlus.regular) {
        DropdownMenuItem("Email", icon: Ph.envelope.regular) {}
        DropdownMenuItem("Message", icon: Ph.chatCircle.regular) {}
      }
      DropdownMenuSeparator()
      DropdownMenuItem("Log out", icon: Ph.signOut.regular, variant: .destructive) {}
    } label: {
      Text("Open menu")
    }
    .buttonStyle(.variant(.outline))
  }
}
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.adamglin.PhosphorIcons
import com.adamglin.phosphoricons.Regular
import com.adamglin.phosphoricons.regular.ChatCircle
import com.adamglin.phosphoricons.regular.CreditCard
import com.adamglin.phosphoricons.regular.Envelope
import com.adamglin.phosphoricons.regular.Gear
import com.adamglin.phosphoricons.regular.SignOut
import com.adamglin.phosphoricons.regular.User
import com.adamglin.phosphoricons.regular.UserPlus
import com.esuiss.ui.components.Button
import com.esuiss.ui.components.ButtonVariant
import com.esuiss.ui.components.DropdownMenu
import com.esuiss.ui.components.DropdownMenuGroup
import com.esuiss.ui.components.DropdownMenuItem
import com.esuiss.ui.components.DropdownMenuLabel
import com.esuiss.ui.components.DropdownMenuSeparator
import com.esuiss.ui.components.DropdownMenuSub
import com.esuiss.ui.components.MenuItemVariant
import com.esuiss.ui.components.PopupAlign

@Composable
fun DropdownMenuExample() {
    DropdownMenuAccount(defaultOpen = false)
}

@Composable
fun DropdownMenuDemo(
    label: String,
    defaultOpen: Boolean = false,
    content: @Composable () -> Unit,
) {
    var open by rememberSaveable { mutableStateOf(defaultOpen) }
    Column {
        DropdownMenu(
            expanded = open,
            onExpandedChange = { open = it },
            trigger = { Button(label, onClick = { open = !open }, variant = ButtonVariant.Outline) },
            modifier = Modifier.width(224.dp),
            align = PopupAlign.Start,
        ) {
            content()
        }
    }
}

@Composable
fun DropdownMenuAccount(defaultOpen: Boolean) {
    DropdownMenuDemo("Open menu", defaultOpen) {
        DropdownMenuLabel("My account")
        DropdownMenuGroup {
            DropdownMenuItem("Profile", onClick = {}, icon = PhosphorIcons.Regular.User)
            DropdownMenuItem("Billing", onClick = {}, icon = PhosphorIcons.Regular.CreditCard)
            DropdownMenuItem("Settings", onClick = {}, icon = PhosphorIcons.Regular.Gear)
        }
        DropdownMenuSeparator()
        DropdownMenuSub("Invite users", icon = PhosphorIcons.Regular.UserPlus) {
            DropdownMenuItem("Email", onClick = {}, icon = PhosphorIcons.Regular.Envelope)
            DropdownMenuItem("Message", onClick = {}, icon = PhosphorIcons.Regular.ChatCircle)
        }
        DropdownMenuSeparator()
        DropdownMenuItem("Log out", onClick = {}, icon = PhosphorIcons.Regular.SignOut, variant = MenuItemVariant.Destructive)
    }
}

Checkboxes

Checkbox items that toggle panels on and off.

iOS
Dropdown Menu (checkboxes) on iOS in light modeDropdown Menu (checkboxes) on iOS in dark mode
import * as React from "react"
import { View } from "react-native"
import { Button } from "@/components/ui/button"
import { DropdownMenu, DropdownMenuCheckboxItem, DropdownMenuContent, DropdownMenuLabel, DropdownMenuSeparator, DropdownMenuTrigger } from "@/components/ui/dropdown-menu"
import { Text } from "@/components/ui/text"

function OpenButton({ label = "Open menu" }: { label?: string }) {
  return (
    <DropdownMenuTrigger render={<Button variant="outline" />}>
      <Text>{label}</Text>
    </DropdownMenuTrigger>
  )
}

function CheckboxesDemo() {
  const [statusBar, setStatusBar] = React.useState(true)
  const [activityBar, setActivityBar] = React.useState(false)
  const [panel, setPanel] = React.useState(false)
  return (
    <View className="items-start">
      <DropdownMenu>
        <OpenButton label="View" />
        <DropdownMenuContent className="w-56" align="start">
          <DropdownMenuLabel>Appearance</DropdownMenuLabel>
          <DropdownMenuSeparator />
          <DropdownMenuCheckboxItem
            checked={statusBar}
            onCheckedChange={setStatusBar}
          >
            <Text>Status bar</Text>
          </DropdownMenuCheckboxItem>
          <DropdownMenuCheckboxItem
            checked={activityBar}
            onCheckedChange={setActivityBar}
            disabled
          >
            <Text>Activity bar</Text>
          </DropdownMenuCheckboxItem>
          <DropdownMenuCheckboxItem checked={panel} onCheckedChange={setPanel}>
            <Text>Panel</Text>
          </DropdownMenuCheckboxItem>
        </DropdownMenuContent>
      </DropdownMenu>
    </View>
  )
}

export function DropdownMenuCheckboxesExample() {
  return (
    <CheckboxesDemo />
  )
}
import EsuissUI
import SwiftUI

struct DropdownMenuCheckboxesDemo: View {
  @State private var statusBar = true
  @State private var activityBar = false
  @State private var panel = false

  var body: some View {
    DropdownMenu {
      DropdownMenuLabel("Appearance")
      DropdownMenuSeparator()
      DropdownMenuCheckboxItem("Status bar", isOn: $statusBar)
      DropdownMenuCheckboxItem("Activity bar", isOn: $activityBar)
        .disabled(true)
      DropdownMenuCheckboxItem("Panel", isOn: $panel)
    } label: {
      Text("View")
    }
    .buttonStyle(.variant(.outline))
  }
}
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.Button
import com.esuiss.ui.components.ButtonVariant
import com.esuiss.ui.components.DropdownMenu
import com.esuiss.ui.components.DropdownMenuCheckboxItem
import com.esuiss.ui.components.DropdownMenuLabel
import com.esuiss.ui.components.DropdownMenuSeparator
import com.esuiss.ui.components.PopupAlign

@Composable
fun DropdownMenuDemo(
    label: String,
    defaultOpen: Boolean = false,
    content: @Composable () -> Unit,
) {
    var open by rememberSaveable { mutableStateOf(defaultOpen) }
    Column {
        DropdownMenu(
            expanded = open,
            onExpandedChange = { open = it },
            trigger = { Button(label, onClick = { open = !open }, variant = ButtonVariant.Outline) },
            modifier = Modifier.width(224.dp),
            align = PopupAlign.Start,
        ) {
            content()
        }
    }
}

@Composable
fun DropdownMenuCheckboxes() {
    var statusBar by rememberSaveable { mutableStateOf(true) }
    var activityBar by rememberSaveable { mutableStateOf(false) }
    var panel by rememberSaveable { mutableStateOf(false) }
    DropdownMenuDemo("View") {
        DropdownMenuLabel("Appearance")
        DropdownMenuSeparator()
        DropdownMenuCheckboxItem("Status bar", checked = statusBar, onCheckedChange = { statusBar = it })
        DropdownMenuCheckboxItem("Activity bar", checked = activityBar, onCheckedChange = { activityBar = it }, enabled = false)
        DropdownMenuCheckboxItem("Panel", checked = panel, onCheckedChange = { panel = it })
    }
}

Radio group

Radio items that pick one value.

iOS
Dropdown Menu (radio group) on iOS in light modeDropdown Menu (radio group) on iOS in dark mode
import * as React from "react"
import { View } from "react-native"
import { Button } from "@/components/ui/button"
import { DropdownMenu, DropdownMenuContent, DropdownMenuLabel, DropdownMenuRadioGroup, DropdownMenuRadioItem, DropdownMenuSeparator, DropdownMenuTrigger } from "@/components/ui/dropdown-menu"
import { Text } from "@/components/ui/text"

function OpenButton({ label = "Open menu" }: { label?: string }) {
  return (
    <DropdownMenuTrigger render={<Button variant="outline" />}>
      <Text>{label}</Text>
    </DropdownMenuTrigger>
  )
}

function RadioGroupDemo() {
  const [position, setPosition] = React.useState("bottom")
  return (
    <View className="items-start">
      <DropdownMenu>
        <OpenButton label="Panel position" />
        <DropdownMenuContent className="w-56" align="start">
          <DropdownMenuLabel>Panel position</DropdownMenuLabel>
          <DropdownMenuSeparator />
          <DropdownMenuRadioGroup value={position} onValueChange={setPosition}>
            <DropdownMenuRadioItem value="top">
              <Text>Top</Text>
            </DropdownMenuRadioItem>
            <DropdownMenuRadioItem value="bottom">
              <Text>Bottom</Text>
            </DropdownMenuRadioItem>
            <DropdownMenuRadioItem value="right">
              <Text>Right</Text>
            </DropdownMenuRadioItem>
          </DropdownMenuRadioGroup>
        </DropdownMenuContent>
      </DropdownMenu>
    </View>
  )
}

export function DropdownMenuRadioGroupExample() {
  return (
    <RadioGroupDemo />
  )
}
import EsuissUI
import SwiftUI

struct DropdownMenuRadioGroupDemo: View {
  @State private var position = "bottom"

  var body: some View {
    DropdownMenu {
      DropdownMenuLabel("Panel position")
      DropdownMenuSeparator()
      DropdownMenuRadioGroup(selection: $position) {
        DropdownMenuRadioItem("Top", value: "top")
        DropdownMenuRadioItem("Bottom", value: "bottom")
        DropdownMenuRadioItem("Right", value: "right")
      }
    } label: {
      Text("Panel position")
    }
    .buttonStyle(.variant(.outline))
  }
}
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.Button
import com.esuiss.ui.components.ButtonVariant
import com.esuiss.ui.components.DropdownMenu
import com.esuiss.ui.components.DropdownMenuLabel
import com.esuiss.ui.components.DropdownMenuRadioGroup
import com.esuiss.ui.components.DropdownMenuRadioItem
import com.esuiss.ui.components.DropdownMenuSeparator
import com.esuiss.ui.components.PopupAlign

@Composable
fun DropdownMenuDemo(
    label: String,
    defaultOpen: Boolean = false,
    content: @Composable () -> Unit,
) {
    var open by rememberSaveable { mutableStateOf(defaultOpen) }
    Column {
        DropdownMenu(
            expanded = open,
            onExpandedChange = { open = it },
            trigger = { Button(label, onClick = { open = !open }, variant = ButtonVariant.Outline) },
            modifier = Modifier.width(224.dp),
            align = PopupAlign.Start,
        ) {
            content()
        }
    }
}

@Composable
fun DropdownMenuRadio() {
    var position by rememberSaveable { mutableStateOf("bottom") }
    DropdownMenuDemo("Panel position") {
        DropdownMenuLabel("Panel position")
        DropdownMenuSeparator()
        DropdownMenuRadioGroup(value = position, onValueChange = { position = it }) {
            DropdownMenuRadioItem("top", "Top")
            DropdownMenuRadioItem("bottom", "Bottom")
            DropdownMenuRadioItem("right", "Right")
        }
    }
}

Inset

Items aligned with inset.

iOS
Dropdown Menu (inset) on iOS in light modeDropdown Menu (inset) on iOS in dark mode
import { View } from "react-native"
import { Button } from "@/components/ui/button"
import { DropdownMenu, DropdownMenuContent, DropdownMenuItem, DropdownMenuLabel, DropdownMenuSeparator, DropdownMenuTrigger } from "@/components/ui/dropdown-menu"
import { Text } from "@/components/ui/text"

function OpenButton({ label = "Open menu" }: { label?: string }) {
  return (
    <DropdownMenuTrigger render={<Button variant="outline" />}>
      <Text>{label}</Text>
    </DropdownMenuTrigger>
  )
}

export function DropdownMenuInsetExample() {
  return (
    <View className="items-start">
      <DropdownMenu>
        <OpenButton />
        <DropdownMenuContent className="w-56" align="start">
          <DropdownMenuLabel inset>Actions</DropdownMenuLabel>
          <DropdownMenuItem inset>
            <Text>Copy</Text>
          </DropdownMenuItem>
          <DropdownMenuItem inset>
            <Text>Paste</Text>
          </DropdownMenuItem>
          <DropdownMenuSeparator />
          <DropdownMenuItem inset variant="destructive">
            <Text>Delete</Text>
          </DropdownMenuItem>
        </DropdownMenuContent>
      </DropdownMenu>
    </View>
  )
}
import EsuissUI
import SwiftUI

struct DropdownMenuInsetExample: View {
  var body: some View {
    DropdownMenu {
      DropdownMenuLabel("Actions", inset: true)
      DropdownMenuItem("Copy", inset: true) {}
      DropdownMenuItem("Paste", inset: true) {}
      DropdownMenuSeparator()
      DropdownMenuItem("Delete", variant: .destructive, inset: true) {}
    } label: {
      Text("Open menu")
    }
    .buttonStyle(.variant(.outline))
  }
}
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.Button
import com.esuiss.ui.components.ButtonVariant
import com.esuiss.ui.components.DropdownMenu
import com.esuiss.ui.components.DropdownMenuItem
import com.esuiss.ui.components.DropdownMenuLabel
import com.esuiss.ui.components.DropdownMenuSeparator
import com.esuiss.ui.components.MenuItemVariant
import com.esuiss.ui.components.PopupAlign

@Composable
fun DropdownMenuDemo(
    label: String,
    defaultOpen: Boolean = false,
    content: @Composable () -> Unit,
) {
    var open by rememberSaveable { mutableStateOf(defaultOpen) }
    Column {
        DropdownMenu(
            expanded = open,
            onExpandedChange = { open = it },
            trigger = { Button(label, onClick = { open = !open }, variant = ButtonVariant.Outline) },
            modifier = Modifier.width(224.dp),
            align = PopupAlign.Start,
        ) {
            content()
        }
    }
}

@Composable
fun DropdownMenuInset() {
    DropdownMenuDemo("Open menu") {
        DropdownMenuLabel("Actions", inset = true)
        DropdownMenuItem("Copy", onClick = {}, inset = true)
        DropdownMenuItem("Paste", onClick = {}, inset = true)
        DropdownMenuSeparator()
        DropdownMenuItem("Delete", onClick = {}, inset = true, variant = MenuItemVariant.Destructive)
    }
}

API Reference

01

DropdownMenu

The root that manages the open state.

PropTypeDefaultDescription
openboolean-Whether the menu is open, for controlled use.
defaultOpenbooleanfalseWhether the menu starts open when uncontrolled.
onOpenChange(open: boolean, details) => void-Called when the menu opens or closes.
modalbooleantrueBlocks interaction with the rest of the screen while open.
disabledbooleanfalsePrevents the menu from opening.
02

DropdownMenuContent

Floating menu card.

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

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

03

DropdownMenuSubTrigger

Row that expands the submenu.

PropTypeDefaultDescription
childrenReact.ReactNode-
iconClassNamestring-Extra classes for the caret icon.
insetboolean-Adds start padding.

Also accepts the props of React.ComponentProps<typeof MenuPrimitive.SubmenuTrigger>.

04

DropdownMenuSubContent

Items of the submenu.

Also accepts the props of React.ComponentProps<typeof MenuPrimitive.Popup>.

05

DropdownMenuItem

Action row.

PropTypeDefaultDescription
insetboolean-Adds start padding to align with icon items.
variant"default" | "destructive""default"default or destructive.

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

06

DropdownMenuCheckboxItem

Row with a check mark that toggles.

PropTypeDefaultDescription
childrenReact.ReactNode-
insetboolean-Adds start padding.

Also accepts the props of React.ComponentProps<typeof MenuPrimitive.CheckboxItem>.

07

DropdownMenuRadioItem

Row that selects one value in its group.

PropTypeDefaultDescription
childrenReact.ReactNode-
insetboolean-Adds start padding.

Also accepts the props of React.ComponentProps<typeof MenuPrimitive.RadioItem>.

08

DropdownMenuLabel

Heading for a group.

PropTypeDefaultDescription
insetboolean-Adds start padding.

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

09

DropdownMenuSeparator

Line between sections.

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

Accessibility

  • The trigger has the button role.
  • The popup has the menu role; items use menuitem, checkbox and radio roles, and radio groups use radiogroup.
  • Groups are labelled by their DropdownMenuLabel, and separators have the separator role.

Notes

  • Uses bg-popover, and bg-accent for an open submenu trigger; a highlighted item fades to opacity-80 and a disabled one to opacity-50; light and dark follow the system appearance.
  • The fade-in respects the system Reduce Motion setting.
iOS
Dropdown Menu (open by default) on iOS in light modeDropdown Menu (open by default) on iOS in dark mode
import { ChatCircleIcon, CreditCardIcon, EnvelopeIcon, GearIcon, SignOutIcon, UserIcon, UserPlusIcon } from "phosphor-react-native"
import { View } from "react-native"
import { Button } from "@/components/ui/button"
import { DropdownMenu, DropdownMenuContent, DropdownMenuGroup, DropdownMenuItem, DropdownMenuLabel, DropdownMenuSeparator, DropdownMenuSub, DropdownMenuSubContent, DropdownMenuSubTrigger, DropdownMenuTrigger } from "@/components/ui/dropdown-menu"
import { Icon } from "@/components/ui/icon"
import { Text } from "@/components/ui/text"

function OpenButton({ label = "Open menu" }: { label?: string }) {
  return (
    <DropdownMenuTrigger render={<Button variant="outline" />}>
      <Text>{label}</Text>
    </DropdownMenuTrigger>
  )
}

export function DropdownMenuOpenByDefaultExample() {
  return (
    <View className="items-start">
      <DropdownMenu defaultOpen>
        <OpenButton />
        <DropdownMenuContent className="w-56" align="start">
          <DropdownMenuLabel>My account</DropdownMenuLabel>
          <DropdownMenuGroup>
            <DropdownMenuItem>
              <Icon as={UserIcon} className="size-4" />
              <Text>Profile</Text>
            </DropdownMenuItem>
            <DropdownMenuItem>
              <Icon as={CreditCardIcon} className="size-4" />
              <Text>Billing</Text>
            </DropdownMenuItem>
            <DropdownMenuItem>
              <Icon as={GearIcon} className="size-4" />
              <Text>Settings</Text>
            </DropdownMenuItem>
          </DropdownMenuGroup>
          <DropdownMenuSeparator />
          <DropdownMenuSub>
            <DropdownMenuSubTrigger>
              <View className="flex-row items-center gap-2">
                <Icon as={UserPlusIcon} className="size-4" />
                <Text>Invite users</Text>
              </View>
            </DropdownMenuSubTrigger>
            <DropdownMenuSubContent>
              <DropdownMenuItem>
                <Icon as={EnvelopeIcon} className="size-4" />
                <Text>Email</Text>
              </DropdownMenuItem>
              <DropdownMenuItem>
                <Icon as={ChatCircleIcon} className="size-4" />
                <Text>Message</Text>
              </DropdownMenuItem>
            </DropdownMenuSubContent>
          </DropdownMenuSub>
          <DropdownMenuSeparator />
          <DropdownMenuItem variant="destructive">
            <Icon as={SignOutIcon} className="size-4" />
            <Text>Log out</Text>
          </DropdownMenuItem>
        </DropdownMenuContent>
      </DropdownMenu>
    </View>
  )
}
import EsuissUI
import PhosphorSwift
import SwiftUI

struct DropdownMenuOpenByDefaultExample: View {
  var body: some View {
    DropdownMenuAccount(open: true)
  }
}

struct DropdownMenuAccount: View {
  @State private var open: Bool

  init(open: Bool = false) {
    _open = State(initialValue: open)
  }

  var body: some View {
    DropdownMenu(isPresented: $open) {
      DropdownMenuLabel("My account")
      DropdownMenuGroup {
        DropdownMenuItem("Profile", icon: Ph.user.regular) {}
        DropdownMenuItem("Billing", icon: Ph.creditCard.regular) {}
        DropdownMenuItem("Settings", icon: Ph.gear.regular) {}
      }
      DropdownMenuSeparator()
      DropdownMenuSub("Invite users", icon: Ph.userPlus.regular) {
        DropdownMenuItem("Email", icon: Ph.envelope.regular) {}
        DropdownMenuItem("Message", icon: Ph.chatCircle.regular) {}
      }
      DropdownMenuSeparator()
      DropdownMenuItem("Log out", icon: Ph.signOut.regular, variant: .destructive) {}
    } label: {
      Text("Open menu")
    }
    .buttonStyle(.variant(.outline))
  }
}
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.adamglin.PhosphorIcons
import com.adamglin.phosphoricons.Regular
import com.adamglin.phosphoricons.regular.ChatCircle
import com.adamglin.phosphoricons.regular.CreditCard
import com.adamglin.phosphoricons.regular.Envelope
import com.adamglin.phosphoricons.regular.Gear
import com.adamglin.phosphoricons.regular.SignOut
import com.adamglin.phosphoricons.regular.User
import com.adamglin.phosphoricons.regular.UserPlus
import com.esuiss.ui.components.Button
import com.esuiss.ui.components.ButtonVariant
import com.esuiss.ui.components.DropdownMenu
import com.esuiss.ui.components.DropdownMenuGroup
import com.esuiss.ui.components.DropdownMenuItem
import com.esuiss.ui.components.DropdownMenuLabel
import com.esuiss.ui.components.DropdownMenuSeparator
import com.esuiss.ui.components.DropdownMenuSub
import com.esuiss.ui.components.MenuItemVariant
import com.esuiss.ui.components.PopupAlign

@Composable
fun DropdownMenuOpenByDefaultExample() {
    DropdownMenuAccount(defaultOpen = true)
}

@Composable
fun DropdownMenuDemo(
    label: String,
    defaultOpen: Boolean = false,
    content: @Composable () -> Unit,
) {
    var open by rememberSaveable { mutableStateOf(defaultOpen) }
    Column {
        DropdownMenu(
            expanded = open,
            onExpandedChange = { open = it },
            trigger = { Button(label, onClick = { open = !open }, variant = ButtonVariant.Outline) },
            modifier = Modifier.width(224.dp),
            align = PopupAlign.Start,
        ) {
            content()
        }
    }
}

@Composable
fun DropdownMenuAccount(defaultOpen: Boolean) {
    DropdownMenuDemo("Open menu", defaultOpen) {
        DropdownMenuLabel("My account")
        DropdownMenuGroup {
            DropdownMenuItem("Profile", onClick = {}, icon = PhosphorIcons.Regular.User)
            DropdownMenuItem("Billing", onClick = {}, icon = PhosphorIcons.Regular.CreditCard)
            DropdownMenuItem("Settings", onClick = {}, icon = PhosphorIcons.Regular.Gear)
        }
        DropdownMenuSeparator()
        DropdownMenuSub("Invite users", icon = PhosphorIcons.Regular.UserPlus) {
            DropdownMenuItem("Email", onClick = {}, icon = PhosphorIcons.Regular.Envelope)
            DropdownMenuItem("Message", onClick = {}, icon = PhosphorIcons.Regular.ChatCircle)
        }
        DropdownMenuSeparator()
        DropdownMenuItem("Log out", onClick = {}, icon = PhosphorIcons.Regular.SignOut, variant = MenuItemVariant.Destructive)
    }
}
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 {
  ChatCircleIcon,
  CreditCardIcon,
  EnvelopeIcon,
  GearIcon,
  SignOutIcon,
  TrashIcon,
  UserIcon,
  UserPlusIcon,
} from "@phosphor-icons/react"
import { Button } from "@/components/ui/button"
import {
  DropdownMenu,
  DropdownMenuContent,
  DropdownMenuGroup,
  DropdownMenuItem,
  DropdownMenuLabel,
  DropdownMenuSeparator,
  DropdownMenuShortcut,
  DropdownMenuSub,
  DropdownMenuSubContent,
  DropdownMenuSubTrigger,
  DropdownMenuTrigger,
} from "@/components/ui/dropdown-menu"

export default function DropdownMenuDemo() {
  return (
    <DropdownMenu>
      <DropdownMenuTrigger render={<Button variant="outline" />}>
        Open menu
      </DropdownMenuTrigger>
      <DropdownMenuContent className="w-56">
        <DropdownMenuGroup>
          <DropdownMenuLabel>My account</DropdownMenuLabel>
          <DropdownMenuItem>
            <UserIcon />
            Profile
            <DropdownMenuShortcut mod>⇧P</DropdownMenuShortcut>
          </DropdownMenuItem>
          <DropdownMenuItem>
            <CreditCardIcon />
            Billing
            <DropdownMenuShortcut mod>B</DropdownMenuShortcut>
          </DropdownMenuItem>
          <DropdownMenuItem>
            <GearIcon />
            Settings
            <DropdownMenuShortcut mod>,</DropdownMenuShortcut>
          </DropdownMenuItem>
        </DropdownMenuGroup>
        <DropdownMenuSeparator />
        <DropdownMenuSub>
          <DropdownMenuSubTrigger>
            <UserPlusIcon />
            Invite users
          </DropdownMenuSubTrigger>
          <DropdownMenuSubContent>
            <DropdownMenuItem>
              <EnvelopeIcon />
              Email
            </DropdownMenuItem>
            <DropdownMenuItem>
              <ChatCircleIcon />
              Message
            </DropdownMenuItem>
          </DropdownMenuSubContent>
        </DropdownMenuSub>
        <DropdownMenuItem disabled>API access</DropdownMenuItem>
        <DropdownMenuSeparator />
        <DropdownMenuItem variant="destructive">
          <TrashIcon />
          Delete account
        </DropdownMenuItem>
        <DropdownMenuItem>
          <SignOutIcon />
          Log out
          <DropdownMenuShortcut mod>⇧Q</DropdownMenuShortcut>
        </DropdownMenuItem>
      </DropdownMenuContent>
    </DropdownMenu>
  )
}
components/ui/dropdown-menu.tsx
"use client"

import { Menu as MenuPrimitive } from "@base-ui/react/menu"
import { CaretRightIcon, CheckIcon } from "@phosphor-icons/react"
import type * as React from "react"
import { useModifierKey } from "@/hooks/use-platform"
import { cn } from "@/lib/utils"

function DropdownMenu({ ...props }: MenuPrimitive.Root.Props) {
  return <MenuPrimitive.Root data-slot="dropdown-menu" {...props} />
}

function DropdownMenuPortal({ ...props }: MenuPrimitive.Portal.Props) {
  return <MenuPrimitive.Portal data-slot="dropdown-menu-portal" {...props} />
}
import { Button } from "@/components/ui/button"
import {
  DropdownMenu,
  DropdownMenuContent,
  DropdownMenuGroup,
  DropdownMenuItem,
  DropdownMenuLabel,
  DropdownMenuShortcut,
  DropdownMenuTrigger,
} from "@/components/ui/dropdown-menu"

export default function DropdownMenuInset() {
  return (
    <DropdownMenu>
      <DropdownMenuTrigger render={<Button variant="outline" />}>
        Edit
      </DropdownMenuTrigger>
      <DropdownMenuContent className="w-56">
        <DropdownMenuGroup>
          <DropdownMenuLabel inset>Clipboard</DropdownMenuLabel>
          <DropdownMenuItem inset>
            Cut
            <DropdownMenuShortcut mod>X</DropdownMenuShortcut>
          </DropdownMenuItem>
          <DropdownMenuItem inset>
            Copy
            <DropdownMenuShortcut mod>C</DropdownMenuShortcut>
          </DropdownMenuItem>
          <DropdownMenuItem inset>
            Paste
            <DropdownMenuShortcut mod>V</DropdownMenuShortcut>
          </DropdownMenuItem>
        </DropdownMenuGroup>
      </DropdownMenuContent>
    </DropdownMenu>
  )
}
import * as React from "react"
import { Button } from "@/components/ui/button"
import {
  DropdownMenu,
  DropdownMenuContent,
  DropdownMenuGroup,
  DropdownMenuLabel,
  DropdownMenuRadioGroup,
  DropdownMenuRadioItem,
  DropdownMenuTrigger,
} from "@/components/ui/dropdown-menu"

function RadioGroupExample(props: React.ComponentProps<typeof DropdownMenu>) {
  const [position, setPosition] = React.useState("bottom")

  return (
    <DropdownMenu {...props}>
      <DropdownMenuTrigger render={<Button variant="outline" />}>
        Panel position
      </DropdownMenuTrigger>
      <DropdownMenuContent className="w-56">
        <DropdownMenuGroup>
          <DropdownMenuLabel>Position</DropdownMenuLabel>
          <DropdownMenuRadioGroup value={position} onValueChange={setPosition}>
            <DropdownMenuRadioItem value="top">Top</DropdownMenuRadioItem>
            <DropdownMenuRadioItem value="bottom">Bottom</DropdownMenuRadioItem>
            <DropdownMenuRadioItem value="right">Right</DropdownMenuRadioItem>
          </DropdownMenuRadioGroup>
        </DropdownMenuGroup>
      </DropdownMenuContent>
    </DropdownMenu>
  )
}

export default function DropdownMenuRadioGroupExample() {
  return <RadioGroupExample />
}
import * as React from "react"
import { Button } from "@/components/ui/button"
import {
  DropdownMenu,
  DropdownMenuCheckboxItem,
  DropdownMenuContent,
  DropdownMenuGroup,
  DropdownMenuLabel,
  DropdownMenuTrigger,
} from "@/components/ui/dropdown-menu"

function CheckboxesExample(props: React.ComponentProps<typeof DropdownMenu>) {
  const [statusBar, setStatusBar] = React.useState(true)
  const [activityBar, setActivityBar] = React.useState(false)
  const [panel, setPanel] = React.useState(false)

  return (
    <DropdownMenu {...props}>
      <DropdownMenuTrigger render={<Button variant="outline" />}>
        View
      </DropdownMenuTrigger>
      <DropdownMenuContent className="w-56">
        <DropdownMenuGroup>
          <DropdownMenuLabel>Appearance</DropdownMenuLabel>
          <DropdownMenuCheckboxItem
            checked={statusBar}
            onCheckedChange={setStatusBar}
          >
            Status bar
          </DropdownMenuCheckboxItem>
          <DropdownMenuCheckboxItem
            checked={activityBar}
            onCheckedChange={setActivityBar}
          >
            Activity bar
          </DropdownMenuCheckboxItem>
          <DropdownMenuCheckboxItem
            checked={panel}
            onCheckedChange={setPanel}
            disabled
          >
            Panel
          </DropdownMenuCheckboxItem>
        </DropdownMenuGroup>
      </DropdownMenuContent>
    </DropdownMenu>
  )
}

export default function DropdownMenuCheckboxes() {
  return <CheckboxesExample />
}
uniwind/components/ui/dropdown-menu.tsx
import { cn } from "cn"
import { CaretRightIcon, CheckIcon } from "phosphor-react-native"
import * as React from "react"
import { Platform, View } from "react-native"
import { FadeIn, ReduceMotion } from "react-native-reanimated"
import { FullWindowOverlay as RNFullWindowOverlay } from "react-native-screens"
import * as MenuPrimitive from "@/components/primitives/menu"
import { resolveClassName } from "@/components/primitives/use-render"
import { AnimatedView } from "@/components/ui/animated-view"
import { Icon } from "@/components/ui/icon"
import { TextClassContext } from "@/components/ui/text"

const DropdownMenu = MenuPrimitive.Root

const DropdownMenuPortal = MenuPrimitive.Portal
nativewind/components/ui/dropdown-menu.tsx
import { cn } from "cn"
import { CaretRightIcon, CheckIcon } from "phosphor-react-native"
import * as React from "react"
import { Platform, View } from "react-native"
import { FadeIn, ReduceMotion } from "react-native-reanimated"
import { FullWindowOverlay as RNFullWindowOverlay } from "react-native-screens"
import * as MenuPrimitive from "@/components/primitives/menu"
import { resolveClassName } from "@/components/primitives/use-render"
import { AnimatedView } from "@/components/ui/animated-view"
import { Icon } from "@/components/ui/icon"
import { TextClassContext } from "@/components/ui/text"

const DropdownMenu = MenuPrimitive.Root

const DropdownMenuPortal = MenuPrimitive.Portal
stylesheet/components/ui/dropdown-menu.tsx
import type { IconProps as PhosphorIconProps } from "phosphor-react-native"
import { CaretRightIcon, CheckIcon } from "phosphor-react-native"
import * as React from "react"
import {
  Platform,
  type StyleProp,
  StyleSheet,
  type TextStyle,
  View,
  type ViewStyle,
} from "react-native"
import { FadeIn, ReduceMotion } from "react-native-reanimated"
import { FullWindowOverlay as RNFullWindowOverlay } from "react-native-screens"
import * as MenuPrimitive from "@/components/primitives/menu"
import { resolveStyle } from "@/components/primitives/use-render"
import { AnimatedView } from "@/components/ui/animated-view"