Select

Show a menu of items that users can select from.

yarnnpmbunpnpm
import React from 'react'
import { Check, ChevronDown, ChevronUp } from '@tamagui/lucide-icons'
import type { FontSizeTokens, SelectProps } from 'tamagui'
import { Adapt, Label, Select, Sheet, XStack, YStack, getFontSize } from 'tamagui'
import { LinearGradient } from 'tamagui/linear-gradient'
export function SelectDemo() {
return (
<YStack gap="$4">
<XStack ai="center" gap="$4">
<Label htmlFor="select-demo-1" f={1} miw={80}>
Custom
</Label>
<SelectDemoItem id="select-demo-1" />
</XStack>
<XStack ai="center" gap="$4">
<Label htmlFor="select-demo-2" f={1} miw={80}>
Native
</Label>
<SelectDemoItem id="select-demo-2" native />
</XStack>
</YStack>
)
}
export function SelectDemoItem(props: SelectProps) {
const [val, setVal] = React.useState('apple')
return (
<Select value={val} onValueChange={setVal} disablePreventBodyScroll {...props}>
<Select.Trigger width={220} iconAfter={ChevronDown}>
<Select.Value placeholder="Something" />
</Select.Trigger>
<Adapt when="sm" platform="touch">
<Sheet native={!!props.native} modal dismissOnSnapToBottom animationConfig={{ type: 'spring', damping: 20, mass: 1.2, stiffness: 250, }} >
<Sheet.Frame>
<Sheet.ScrollView>
<Adapt.Contents />
</Sheet.ScrollView>
</Sheet.Frame>
<Sheet.Overlay animation="lazy" enterStyle={{ opacity: 0 }} exitStyle={{ opacity: 0 }} />
</Sheet>
</Adapt>
<Select.Content zIndex={200000}>
<Select.ScrollUpButton alignItems="center" justifyContent="center" position="relative" width="100%" height="$3" >
<YStack zIndex={10}>
<ChevronUp size={20} />
</YStack>
<LinearGradient start={[0, 0]} end={[0, 1]} fullscreen colors={['$background', 'transparent']} borderRadius="$4" />
</Select.ScrollUpButton>
<Select.Viewport // to do animations: // animation="quick" // animateOnly={['transform', 'opacity']} // enterStyle={{ o: 0, y: -10 }} // exitStyle={{ o: 0, y: 10 }} minWidth={200} >
<Select.Group>
<Select.Label>Fruits</Select.Label>
{/* for longer lists memoizing these is useful */}
{React.useMemo(
() =>
items.map((item, i) => {
return (
<Select.Item index={i} key={item.name} value={item.name.toLowerCase()} >
<Select.ItemText>{item.name}</Select.ItemText>
<Select.ItemIndicator marginLeft="auto">
<Check size={16} />
</Select.ItemIndicator>
</Select.Item>
)
}),
[items]
)}
</Select.Group>
{/* Native gets an extra icon */}
{props.native && (
<YStack position="absolute" right={0} top={0} bottom={0} alignItems="center" justifyContent="center" width={'$4'} pointerEvents="none" >
<ChevronDown size={getFontSize((props.size as FontSizeTokens) ?? '$true')} />
</YStack>
)}
</Select.Viewport>
<Select.ScrollDownButton alignItems="center" justifyContent="center" position="relative" width="100%" height="$3" >
<YStack zIndex={10}>
<ChevronDown size={20} />
</YStack>
<LinearGradient start={[0, 0]} end={[0, 1]} fullscreen colors={['transparent', '$background']} borderRadius="$4" />
</Select.ScrollDownButton>
</Select.Content>
</Select>
)
}
const items = [
{ name: 'Apple' },
{ name: 'Pear' },
{ name: 'Blackberry' },
{ name: 'Peach' },
{ name: 'Apricot' },
{ name: 'Melon' },
{ name: 'Honeydew' },
{ name: 'Starfruit' },
{ name: 'Blueberry' },
{ name: 'Raspberry' },
{ name: 'Strawberry' },
{ name: 'Mango' },
{ name: 'Pineapple' },
{ name: 'Lime' },
{ name: 'Lemon' },
{ name: 'Coconut' },
{ name: 'Guava' },
{ name: 'Papaya' },
{ name: 'Orange' },
{ name: 'Grape' },
{ name: 'Jackfruit' },
{ name: 'Durian' },
]

Features

  • Comes with styling, yet completely customizable and themeable.

  • Accepts animations, themes, size props and more.

  • Accessible, keyboard navigable, full-featured.

Installation

Select is already installed in tamagui, or you can install it independently:

yarn add @tamagui/select

Anatomy

import { Select } from 'tamagui' // or '@tamagui/select'
export default () => (
<Select defaultValue="">
<Select.Trigger>
<Select.Value placeholder="Search..." />
</Select.Trigger>
<Select.Content>
<Select.ScrollUpButton />
<Select.Viewport>
<Select.Group>
<Select.Label />
<Select.Item>
<Select.ItemText />
</Select.Item>
</Select.Group>
</Select.Viewport>
<Select.ScrollDownButton />
</Select.Content>
</Select>
)

Note that Select only works on Native using the Adapt component to adapt it to a Sheet. See below for docs.

API Reference

Select

Contains every component for the select:

Props

  • id

    string

    Optional for usage with Label

  • size

    SizeTokens

    Set the size of itself and pass to all inner elements

  • children

    React.ReactNode

    Select children API components

  • value

    string

    Controlled value

  • defaultValue

    string

    Default value

  • onValueChange

    (value: string) => void

    Callback on value change

  • open

    boolean

    Controlled open value

  • defaultOpen

    boolean

    Default open value

  • onOpenChange

    (open: boolean) => void

    Callback on open change

  • dir

    Direction

    Direction of text display

  • name

    string

    For use in forms

  • native

    NativeValue

    If passed, will render a native component instead of the custom one. Currently only `web` is supported.

  • Select.Trigger

    Extends ListItem to give sizing, icons, and more.

    Select.Value

    Extends Paragraph, adding:

    Props

  • placeholder

    string

    Optional placeholder to show when no value selected

  • SelectContent

    Main container for Select content, used to contain the up/down arrows, no API beyond children.

    Select.ScrollUpButton

    Inside Content first, displays when you can scroll up, stuck to the top.

    Extends YStack.

    Select.ScrollDownButton

    Inside Content last, displays when you can scroll down, stuck to the bottom.

    Extends YStack.

    Select.Viewport

    Extends ThemeableStack. Contains scrollable content items as children.

    Props

  • disableScroll

    boolean

    Removes ability to scroll and all style and functionality related to scrolling

  • unstyled

    boolean

    Removes all default styles

  • Make sure to not pass height prop as that is managed internally because of UX reasons and having a fixed height will break that behaviour

    Select.Group

    Extends YStack. Use only when grouping together items, alongside a Label as the first child.

    Select.Label

    Extends ListItem. Used to label Groups.

    Select.Item

    Extends ListItem. Used to add selectable values ot the list. Must provide an index as React Native doesn't give any escape hatch for us to configure that automatically.

    Props

  • index (required)

    number

    Incrementally starting from 0, matching its appearance in the list.

  • value

    string

    Provide a value that will be passed on selection.

  • Select.ItemText

    Extends Paragraph. Used inside Item to provide unselectable text that will show above once selected in the parent Select.

    Select.Sheet

    When used alongside <Adapt />, Select will render as a sheet when that breakpoint is active.

    This is the only way to render a Select on Native for now, as mobile apps tend to show Select very differently from web and Tamagui wants to present the right abstractions for each platform.

    See Sheet for more props.

    Must use Select.Adapt.Contents inside the Select.Sheet.Frame to insert the contents given to Select.Content

    import { Select } from 'tamagui' // or '@tamagui/select'
    export default () => (
    <Select defaultValue="">
    <Select.Trigger>
    <Select.Value placeholder="Search..." />
    </Select.Trigger>
    <Adapt when="sm" platform="touch">
    {/* or <Select.Sheet> */}
    <Sheet>
    <Sheet.Frame>
    <Adapt.Contents />
    </Sheet.Frame>
    <Sheet.Overlay />
    </Sheet>
    </Adapt>
    <Select.Content>
    <Select.ScrollUpButton />
    <Select.Viewport>
    <Select.Group>
    <Select.Label />
    <Select.Item>
    <Select.ItemText />
    </Select.Item>
    </Select.Group>
    </Select.Viewport>
    <Select.ScrollDownButton />
    </Select.Content>
    </Select>
    )