code/tamagui.dev/data/docs/components/select/1.141.0.mdx
<Highlights
features={[
Comes with styling, yet completely customizable and themeable.,
Accepts animations, themes, size props and more.,
Accessible, keyboard navigable, full-featured.,
]}
/>
Select is already installed in tamagui, or you can install it independently:
npm install @tamagui/select
import { Select } from 'tamagui' // or '@tamagui/select'
export default () => (
<Select defaultValue="">
<Select.Trigger>
<Select.Value placeholder="Search..." />
</Select.Trigger>
<Select.FocusScope loop trapped focusOnIdle={true}>
<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.FocusScope>
</Select>
)
By default, Select uses a portal-based mechanism to display the selected item's text in the trigger. This happens in a client-side effect, which can cause hydration mismatches when using SSR.
To fix this, use the renderValue prop to provide the label synchronously during render:
const items = [
{ value: 'apple', label: 'Apple' },
{ value: 'orange', label: 'Orange' },
]
// Create a lookup function
const getLabel = (value: string) => items.find((item) => item.value === value)?.label
export function MySelect() {
const [value, setValue] = React.useState('apple')
return (
<Select value={value} onValueChange={setValue} renderValue={getLabel}>
<Select.Trigger>
<Select.Value placeholder="Select a fruit..." />
</Select.Trigger>
<Select.Content>
<Select.Viewport>
{items.map((item, i) => (
<Select.Item key={item.value} value={item.value} index={i}>
<Select.ItemText>{item.label}</Select.ItemText>
</Select.Item>
))}
</Select.Viewport>
</Select.Content>
</Select>
)
}
When renderValue is provided, it's called synchronously during render, ensuring the server and client render the same content.
Contains every component for the select:
<PropsTable
data={[
{
name: 'id',
type: 'string',
description: Optional for usage with Label,
},
{
name: 'size',
type: 'SizeTokens',
description: Set the size of itself and pass to all inner elements,
},
{
name: 'children',
type: 'React.ReactNode',
description: Select children API components,
},
{
name: 'value',
type: 'string',
description: Controlled value,
},
{
name: 'defaultValue',
type: 'string',
description: Default value,
},
{
name: 'onValueChange',
type: '(value: string) => void',
description: Callback on value change,
},
{
name: 'open',
type: 'boolean',
description: Controlled open value,
},
{
name: 'defaultOpen',
type: 'boolean',
description: Default open value,
},
{
name: 'onOpenChange',
type: '(open: boolean) => void',
description: Callback on open change,
},
{
name: 'dir',
type: 'Direction',
description: Direction of text display,
},
{
name: 'name',
type: 'string',
description: For use in forms,
},
{
name: 'native',
type: 'NativeValue',
description: If passed, will render a native component instead of the custom one. Currently only \web` is supported., }, { name: 'renderValue', type: '(value: string) => React.ReactNode', description: Render function for the selected value. Use this for SSR support to avoid hydration mismatches. When provided, this is called synchronously during render to display the selected value.`,
},
]}
/>
Extends ListItem to give sizing, icons, and more.
Extends Paragraph, adding:
<PropsTable
data={[
{
name: 'placeholder',
type: 'string',
description: Optional placeholder to show when no value selected,
},
]}
/>
Main container for Select content, used to contain the up/down arrows, no API beyond children.
Inside Content first, displays when you can scroll up, stuck to the top.
Extends YStack.
Inside Content last, displays when you can scroll down, stuck to the bottom.
Extends YStack.
Extends ThemeableStack. Contains scrollable content items as children.
<PropsTable
data={[
{
name: 'disableScroll',
type: 'boolean',
description: Removes ability to scroll and all style and functionality related to scrolling,
},
{
name: 'unstyled',
type: 'boolean',
description: Removes all default styles,
},
]}
/>
Extends YStack. Use only when grouping together items, alongside a Label as the first child.
Extends ListItem. Used to label Groups.
Extends ListItem. Used to add selectable values to the list. Must provide an index as React Native doesn't give any escape hatch for us to configure that automatically.
<PropsTable
data={[
{
name: 'index',
type: 'number',
required: true,
description: Incrementally starting from 0, matching its appearance in the list.,
},
{
name: 'value',
type: 'string',
description: `Provide a value that will be passed on selection.`,
},
]} />
Extends Paragraph. Used inside Item to provide unselectable text that will show above once selected in the parent Select.
Provides access to the underlying FocusScope component used by Select for focus management. Can be used to control focus behavior from a parent component.
<PropsTable
data={[
{
name: 'enabled',
type: 'boolean',
default: 'true',
description: Whether focus management is enabled,
},
{
name: 'loop',
type: 'boolean',
default: 'false',
description: When true, tabbing from last item will focus first tabbable and shift+tab from first item will focus last tabbable,
},
{
name: 'trapped',
type: 'boolean',
default: 'false',
description: When true, focus cannot escape the focus scope via keyboard, pointer, or programmatic focus,
},
{
name: 'focusOnIdle',
type: 'boolean | number',
default: 'false',
description: When true, waits for idle before focusing. When a number, waits that many ms. This prevents reflows during animations,
},
{
name: 'onMountAutoFocus',
type: '(event: Event) => void',
description: Event handler called when auto-focusing on mount. Can be prevented,
},
{
name: 'onUnmountAutoFocus',
type: '(event: Event) => void',
description: Event handler called when auto-focusing on unmount. Can be prevented,
},
]}
/>
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="maxMd" platform="touch">
<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>
)