import { useState } from "react";
import { Select } from "@lark-ui/core";
/** 带可见标签的基础选择器(Select)——推荐用法。 */
export function SelectBasicDemo() {
const [value, setValue] = useState("apple");
return (
<Select
label="最喜爱的水果"
className="w-[200px]"
value={value}
onValueChange={(v) => setValue(v ?? "apple")}
items={{ apple: "苹果", banana: "香蕉", cherry: "樱桃" }}
/>
);
}安装
统一导出
import { Select } from "@lark-ui/core";按需导出
import { Select } from "@lark-ui/core/components/select";用法
import { Select } from "@lark-ui/core";
export default function Example() {
const [value, setValue] = useState("apple");
return (
<Select
label="Favorite Fruit"
value={value}
onValueChange={(v) => setValue(v ?? "apple")}
items={{ apple: "Apple", banana: "Banana", cherry: "Cherry" }}
/>
);
}示例
基础用法
带可见标签的选择器(Select)。提供 label 属性后,选择器会自动包裹在 Field 包装器中,
标签显示在其上方。
import { useState } from "react";
import { Select } from "@lark-ui/core";
/** 带可见标签的基础选择器(Select)——推荐用法。 */
export function SelectBasicDemo() {
const [value, setValue] = useState("apple");
return (
<Select
label="最喜爱的水果"
className="w-[200px]"
value={value}
onValueChange={(v) => setValue(v ?? "apple")}
items={{ apple: "苹果", banana: "香蕉", cherry: "樱桃" }}
/>
);
}尺寸
使用 size 属性可以与输入框(Input)的尺寸保持一致(xs、sm、base、lg)。
import { Select } from "@lark-ui/core";
/** 选择器触发器的尺寸(xs/sm/base/lg),与输入框(Input)和组合框(Combobox)保持一致。 */
export function SelectSizesDemo() {
return (
<div className="grid gap-4">
<div className="flex items-center gap-3">
<span className="w-10 text-sm text-lark-subtle">xs</span>
<Select
aria-label="选择尺寸 xs"
size="xs"
className="w-[200px]"
placeholder="请选择……"
items={{ a: "选项 A", b: "选项 B" }}
/>
</div>
<div className="flex items-center gap-3">
<span className="w-10 text-sm text-lark-subtle">sm</span>
<Select
aria-label="选择尺寸 sm"
size="sm"
className="w-[200px]"
placeholder="请选择……"
items={{ a: "选项 A", b: "选项 B" }}
/>
</div>
<div className="flex items-center gap-3">
<span className="w-10 text-sm text-lark-subtle">base</span>
<Select
aria-label="选择尺寸 base"
size="base"
className="w-[200px]"
placeholder="请选择……"
items={{ a: "选项 A", b: "选项 B" }}
/>
</div>
<div className="flex items-center gap-3">
<span className="w-10 text-sm text-lark-subtle">lg</span>
<Select
aria-label="选择尺寸 lg"
size="lg"
className="w-[200px]"
placeholder="请选择……"
items={{ a: "选项 A", b: "选项 B" }}
/>
</div>
</div>
);
}定位
弹层优先选择 side="bottom",当该方向空间不足时会自动翻转。使用 side、align、
sideOffset 和 alignOffset 可以明确固定其位置——碰撞处理仍然生效。
import { Select } from "@lark-ui/core";
/**
* 使用 `side` 和 `align` 固定弹层位置。当所选方向空间不足时,位置仍会自动翻转。
*/
export function SelectPlacementDemo() {
return (
<div className="grid gap-4 sm:grid-cols-2">
<Select
label="side=bottom(默认)"
className="w-[200px]"
defaultValue="earth"
items={planets}
/>
<Select
label="side=top"
side="top"
className="w-[200px]"
defaultValue="earth"
items={planets}
/>
<Select
label="align=end"
align="end"
className="w-[200px]"
defaultValue="earth"
items={planets}
/>
<Select
label="sideOffset=12"
sideOffset={12}
className="w-[200px]"
defaultValue="earth"
items={planets}
/>
</div>
);
}与选中项对齐
设置 alignItemWithTrigger 后,弹层会覆盖在触发元素上,使选中的选项正好位于其上方,
效果与原生 select 一致。选中项之前的选项渲染在触发元素上方,其余选项渲染在下方,
因此弹层可以向上下两个方向延伸。下面的两个选择器都在列表中间选中了 Mars——打开它们
对比一下。当空间不足时,该模式会自动关闭并回退到常规的锚定定位。
在触发器下方打开
选中的选项与触发器对齐
import { Select } from "@lark-ui/core";
/**
* `alignItemWithTrigger` 会让弹层覆盖在触发器上,使选中的选项正好位于触发器上方,
* 与原生 `<select>` 一致。选中项之前的选项渲染在触发器上方,其余选项在下方。
* 打开第二个选择器——「Mars」选在列表中部,因此弹层向上下两个方向展开。
* 空间不足时会回退到常规的锚定定位方式。
*/
export function SelectDynamicPlacementDemo() {
return (
<div className="flex flex-wrap items-start gap-8">
<Select
label="锚定(默认)"
description="在触发器下方打开"
className="w-[200px]"
defaultValue="mars"
items={planets}
/>
<Select
label="与选中项对齐"
description="选中的选项与触发器对齐"
alignItemWithTrigger
className="w-[200px]"
defaultValue="mars"
items={planets}
/>
</div>
);
}无可见标签
当不需要可见标签时(例如在紧凑界面中,或上下文已经明确),请使用 aria-label 以保证无障碍。
import { useState } from "react";
import { Select } from "@lark-ui/core";
/** 没有可见标签的选择器——请使用 aria-label 保证无障碍。 */
export function SelectWithoutLabelDemo() {
const [value, setValue] = useState("apple");
return (
<Select
aria-label="选择一种水果"
className="w-[200px]"
value={value}
onValueChange={(v) => setValue(v ?? "apple")}
items={{ apple: "苹果", banana: "香蕉", cherry: "樱桃" }}
/>
);
}带描述
选择器与 Field 包装器集成,可在输入框下方显示描述文字。
选择最能描述你问题的分类
import { useState } from "react";
import { Select } from "@lark-ui/core";
/** 带标签和描述文字的选择器。 */
export function SelectWithDescriptionDemo() {
const [value, setValue] = useState<string | null>(null);
return (
<Select
label="问题类型"
description="选择最能描述你问题的分类"
className="w-[280px]"
value={value}
onValueChange={(v) => setValue(v as string | null)}
items={{
bug: "缺陷",
documentation: "文档",
feature: "功能",
}}
/>
);
}带错误
传入 error 属性可显示校验错误。存在错误时,它会在界面上取代描述文字。
import { Select } from "@lark-ui/core";
/** 带标签和校验错误的选择器。 */
export function SelectWithErrorDemo() {
return (
<Select
label="问题类型"
error="请选择问题类型"
className="w-[280px]"
value={null}
items={{
bug: "缺陷",
documentation: "文档",
feature: "功能",
}}
/>
);
}占位符
使用 placeholder 属性可以在未选中任何值时显示提示文字。当使用 renderValue 自定义选中值的
显示方式时,值为 null 时会显示占位符,而不会调用 renderValue。
<Select
placeholder="Select a user..."
value={user}
renderValue={(user) => user.name} // Only called when user is not null
/>import { useState } from "react";
import { Select } from "@lark-ui/core";
/** 未选中任何值时显示占位符的选择器。 */
export function SelectPlaceholderDemo() {
const [value, setValue] = useState<string | null>(null);
return (
<Select
label="分类"
placeholder="请选择分类……"
className="w-[200px]"
value={value}
onValueChange={(v) => setValue(v as string | null)}
items={{
bug: "缺陷",
documentation: "文档",
feature: "功能",
}}
/>
);
}带工具提示的标签
使用 labelTooltip 可以在标签旁添加工具提示图标,以补充说明上下文。
import { useState } from "react";
import { Select } from "@lark-ui/core";
/** 带标签工具提示的选择器,用于补充说明。 */
export function SelectWithTooltipDemo() {
const [value, setValue] = useState<string | null>(null);
return (
<Select
label="优先级"
labelTooltip="优先级越高的问题越先得到处理"
placeholder="选择优先级"
className="w-[200px]"
value={value}
onValueChange={(v) => setValue(v as string | null)}
items={{
low: "低",
medium: "中",
high: "高",
critical: "紧急",
}}
/>
);
}自定义渲染
使用 renderValue 可以自定义选中值在触发按钮中的显示方式。当处理的是复杂的对象数据结构
而非简单字符串值时,这会非常有用。
import { useState } from "react";
import { Select } from "@lark-ui/core";
/** 使用自定义渲染展示复杂选项的选择器。 */
export function SelectCustomRenderingDemo() {
const [value, setValue] = useState(languages[0]);
return (
<Select
label="语言"
className="w-[200px]"
renderValue={(v) => (
<span>
{v.emoji} {v.label}
</span>
)}
value={value}
onValueChange={(v) => setValue(v as (typeof languages)[0])}
>
{languages.map((language) => (
<Select.Option key={language.value} value={language}>
{language.emoji} {language.label}
</Select.Option>
))}
</Select>
);
}renderValue 函数仅在选中有值时才会被调用。请使用 placeholder 定义未选中任何值时显示的内容。
选择器会将 value 与 items 逐一比较以找出当前选中项。对于对象类型的选项,默认比较的是是否为
同一引用,而非按值比较。如果希望按值比较对象选项,可以使用 isItemEqualToValue 属性。
<Select
className="w-[200px]"
placeholder="Select a language..."
renderValue={(v) => (
<span>
{v.emoji} {v.label}
</span>
)}
value={value}
onValueChange={(v) => setValue(v)}
// Provides custom comparison logic
isItemEqualToValue={(item, value) => item.value === value.value}
>
{languages.map((language) => (
<Select.Option key={language.value} value={language}>
{language.emoji} {language.label}
</Select.Option>
))}
</Select>加载状态
带加载状态的选择器组件。加载状态通过 loading 属性传给组件。
加载状态
从服务器加载(模拟 2 秒延迟)
import { Select } from "@lark-ui/core";
/** 处于加载状态的选择器。 */
export function SelectLoadingDemo() {
return <Select aria-label="加载中的选择器" className="w-[200px]" loading />;
}多选
使用 multiple 属性开启多选。此时 value 变为已选项组成的数组。请用 placeholder 定义空状态,
用 renderValue 自定义选中项的显示方式。
<Select
multiple
placeholder="Select columns..."
value={selectedColumns}
renderValue={(columns) => columns.join(", ")}
onValueChange={setSelectedColumns}
>
<Select.Option value="name">Name</Select.Option>
<Select.Option value="email">Email</Select.Option>
</Select>import { useState } from "react";
import { Select } from "@lark-ui/core";
/** 用于选择多个值的多选选择器。 */
export function SelectMultipleDemo() {
const [value, setValue] = useState<string[]>(["Name", "Location", "Size"]);
return (
<Select
label="可见列"
className="w-[250px]"
multiple
renderValue={(value) => {
if (value.length > 3) {
return (
<span className="line-clamp-1">
{value.slice(0, 2).join("、") + ` 等 ${value.length - 2} 项`}
</span>
);
}
return <span>{value.join("、")}</span>;
}}
value={value}
onValueChange={(v) => setValue(v as string[])}
>
<Select.Option value="Name">名称</Select.Option>
<Select.Option value="Location">位置</Select.Option>
<Select.Option value="Size">大小</Select.Option>
<Select.Option value="Read">读取</Select.Option>
<Select.Option value="Write">写入</Select.Option>
<Select.Option value="CreatedAt">创建时间</Select.Option>
</Select>
);
}更多示例
选择本文档的主要作者
import { useState } from "react";
import { Select, Text } from "@lark-ui/core";
/** 使用复杂对象值和自定义选项渲染的选择器。 */
export function SelectComplexDemo() {
const [value, setValue] = useState<(typeof authors)[0] | null>(null);
return (
<Select
label="作者"
description="选择本文档的主要作者"
placeholder="选择作者"
className="w-[200px]"
onValueChange={(v) => setValue(v as (typeof authors)[0] | null)}
value={value}
isItemEqualToValue={(item, value) => item?.id === value?.id}
renderValue={(author) => author.name}
>
{authors.map((author) => (
<Select.Option key={author.id} value={author}>
<div className="flex w-[300px] items-center justify-between gap-2">
<Text>{author.name}</Text>
<Text variant="secondary">{author.title}</Text>
</div>
</Select.Option>
))}
</Select>
);
}禁用选项
可以使用 disabled 属性禁用选项。被禁用的选项会置灰,且无法被选中。
import { useState } from "react";
import { Select } from "@lark-ui/core";
/** 带禁用选项(无法被选中)的选择器。 */
export function SelectDisabledOptionsDemo() {
const [value, setValue] = useState<Region | null>(null);
return (
<Select
label="部署区域"
placeholder="请选择区域……"
className="w-[250px]"
value={value}
onValueChange={(v) => setValue(v as Region | null)}
isItemEqualToValue={(item, val) => item.value === val.value}
>
{regions.map((region) => (
<Select.Option
key={region.value}
value={region}
disabled={region.disabled}
>
{region.label}
</Select.Option>
))}
</Select>
);
}禁用选项(通过 items 属性)
items 对象映射属性既接受纯字符串值,也接受带有 disabled 的描述对象。
import { useState } from "react";
import { Select } from "@lark-ui/core";
/** 使用 items 属性并携带禁用描述符的选择器。 */
export function SelectDisabledItemsDemo() {
const [value, setValue] = useState<string | null>("free");
return (
<Select
label="套餐"
className="w-[200px]"
value={value}
onValueChange={(v) => setValue(v as string | null)}
items={{
free: "免费版",
pro: "专业版",
business: { label: "商业版", disabled: true },
enterprise: { label: "企业版", disabled: true },
}}
/>
);
}分组选项
使用 Select.Group、Select.GroupLabel 和 Select.Separator,可以用带标签的标题和视觉分隔线
来组织选项。
import { useState } from "react";
import { Select } from "@lark-ui/core";
/** 按带标签的标题分组的选项组织方式的选择器。 */
export function SelectGroupedDemo() {
const [value, setValue] = useState<Food | null>(null);
return (
<Select
label="食物"
placeholder="选择一种食物……"
className="w-[220px]"
value={value}
onValueChange={(v) => setValue(v as Food | null)}
isItemEqualToValue={(item, val) => item.value === val.value}
>
<Select.Group>
<Select.GroupLabel>水果</Select.GroupLabel>
{foods.fruits.map((food) => (
<Select.Option key={food.value} value={food}>
{food.label}
</Select.Option>
))}
</Select.Group>
<Select.Separator />
<Select.Group>
<Select.GroupLabel>蔬菜</Select.GroupLabel>
{foods.vegetables.map((food) => (
<Select.Option key={food.value} value={food}>
{food.label}
</Select.Option>
))}
</Select.Group>
</Select>
);
}含禁用选项的分组
将分组、分隔线和禁用选项与信息工具提示结合使用,可以清晰区分可选与不可选的条目。
import { useState } from "react";
import { Select } from "@lark-ui/core";
/** 分组选择器,含禁用选项和信息工具提示。 */
export function SelectGroupedWithDisabledDemo() {
const [value, setValue] = useState<ServerRegion | null>(null);
return (
<Select
label="服务器区域"
placeholder="选择区域……"
className="w-[260px]"
value={value}
onValueChange={(v) => setValue(v as ServerRegion | null)}
isItemEqualToValue={(item, val) => item.value === val.value}
>
<Select.Group>
<Select.GroupLabel>可用</Select.GroupLabel>
{serverRegions.available.map((region) => (
<Select.Option key={region.value} value={region}>
{region.label}
</Select.Option>
))}
</Select.Group>
<Select.Separator />
<Select.Group>
<Select.GroupLabel>不可用</Select.GroupLabel>
{serverRegions.unavailable.map((region) => (
<Select.Option key={region.value} value={region} disabled>
{region.label}
</Select.Option>
))}
</Select.Group>
</Select>
);
}长列表(滚动测试)
包含大量选项的选择器组件,用于测试弹层的滚动行为。弹层应平滑滚动,不出现回弹/过度滚动问题。
测试大量选项时的滚动行为
import { useState } from "react";
import { Select } from "@lark-ui/core";
/** 使用长列表的选择器,用于测试弹层滚动行为。 */
export function SelectLongListDemo() {
const [value, setValue] = useState<LongListItem | null>(null);
return (
<Select
label="长列表选择器"
description="测试大量选项时的滚动行为"
placeholder="请选择一个选项……"
className="w-[220px]"
value={value}
onValueChange={(v) => setValue(v as LongListItem | null)}
isItemEqualToValue={(item, val) => item.value === val.value}
>
{longListItems.map((item) => (
<Select.Option key={item.value} value={item}>
{item.label}
</Select.Option>
))}
</Select>
);
}API 参考
Select
| 属性 | 类型 | 默认值 | 说明 |
|---|---|---|---|
| align | "start" | "center" | "end" | - | 弹出层相对指定边缘的对齐方式。 |
| alignItemWithTrigger | boolean | - | 是否让定位层与触发元素重叠,使选中项文字与触发元素的取值文字对齐。仅对鼠标输入生效,空间不足时会自动禁用。 |
| alignOffset | number | OffsetFunction | - | 沿对齐轴的额外偏移量(像素)。也可以传入一个返回偏移量的函数,该函数可读取锚点元素与定位层元素的尺寸,以及它们的边缘和对齐方式。 该函数接收一个 `data` 对象参数,包含以下属性: - `data.anchor`:锚点元素的尺寸,包含 `width` 和 `height` 属性。 - `data.positioner`:定位层元素的尺寸,包含 `width` 和 `height` 属性。 - `data.side`:定位层对齐在锚点元素的哪一侧。 - `data.align`:定位层相对指定边缘的对齐方式。 |
| anchor | ReactNode | - | 弹出层定位所依据的元素。默认情况下,弹出层会相对于触发元素定位。 |
| arrowPadding | number | - | 箭头与弹出层边缘之间保持的最小距离。 用于防止箭头元素超出弹出层的圆角。 |
| collisionAvoidance | CollisionAvoidance | - | 决定弹出层定位时如何处理碰撞。 `side` 控制首选放置轴(`top`/`bottom` 或 `left`/`right`)上的溢出处理: - `'flip'`:能放下时保持请求的边缘,否则尝试相对的一侧(`top` 与 `bottom`,或 `left` 与 `right`)。 - `'shift'`:不改变边缘,保持请求的边缘并在裁剪边界内移动弹出层,使其保持可见。 - `'none'`:不修正边缘轴上的溢出。 `align` 控制对齐轴(`start`/`center`/`end`)上的溢出处理: - `'flip'`:保持边缘,但当请求的对齐方式溢出时互换 `start` 与 `end`。 - `'shift'`:保持边缘和请求的对齐方式,然后沿对齐轴微调弹出层位置以适应空间。 - `'none'`:不修正对齐轴上的溢出。 `fallbackAxisSide` 控制首选轴无法容纳时在垂直轴上的回退行为: - `'start'`:允许回退到垂直轴,并优先尝试逻辑起始侧(LTR 下先 `top` 后 `bottom`,或先 `left` 后 `right`)。 - `'end'`:允许回退到垂直轴,并优先尝试逻辑结束侧(LTR 下先 `bottom` 后 `top`,或先 `right` 后 `left`)。 - `'none'`:不回退到垂直轴。 当 `side` 为 `'shift'` 时,显式设置 `align` 只支持 `'shift'` 或 `'none'`。省略 `align` 时默认为 `'flip'`。 |
| collisionBoundary | Boundary | - | 限定弹出层活动区域的元素或矩形。 |
| collisionPadding | Padding | - | 与碰撞边界边缘之间保持的额外间距。 |
| disableAnchorTracking | boolean | - | 是否禁止弹出层跟随定位锚点的布局变化。 |
| positionMethod | "absolute" | "fixed" | - | 使用哪个 CSS `position` 属性。 |
| side | "top" | "bottom" | "left" | "right" | "inline-end" | "inline-start" | - | 弹出层对齐在锚点元素的哪一侧。为避免碰撞可能会自动改变。 |
| sideOffset | number | OffsetFunction | - | 锚点元素与弹出层之间的距离(像素)。也可以传入一个返回距离的函数,该函数可读取锚点元素与定位层元素的尺寸,以及它们的边缘和对齐方式。 该函数接收一个 `data` 对象参数,包含以下属性: - `data.anchor`:锚点元素的尺寸,包含 `width` 和 `height` 属性。 - `data.positioner`:定位层元素的尺寸,包含 `width` 和 `height` 属性。 - `data.side`:定位层对齐在锚点元素的哪一侧。 - `data.align`:定位层相对指定边缘的对齐方式。 |
| sticky | boolean | - | 当锚点元素滚出视口后,是否让弹出层保持在视口内。 |
| className | string | - | 通过 `cn()` 合并的额外 CSS 类名。 |
| render | ReactNode | - | 替换触发元素,同时保留 Select 的行为。 |
| size | "xs" | "sm" | "base" | "lg" | "base" | 选择器触发器的尺寸。与 Input 组件的尺寸一致。 |
| label | ReactNode | - | 选择器的标签内容。提供后会在选择器上方启用带可见标签的 Field 包裹层。若需要无障碍支持但不想显示标签,请改用 `aria-label`。 |
| hideLabel | boolean | - | - |
| placeholder | string | - | 未选中任何值时显示的占位符文字。 |
| loading | boolean | - | 为 `true` 时,用骨架加载指示器代替选中值显示。 |
| disabled | boolean | - | 选择器是否禁用。 |
| required | boolean | - | 选择器是否必填。为 `false` 时显示“(optional)”文字。 |
| labelTooltip | ReactNode | - | 通过信息图标显示在标签旁的工具提示内容。 |
| value | T | - | 当前选中的值(受控模式)。 |
| children | ReactNode | - | 在下拉菜单中渲染的 `Select.Option` 元素。 |
| description | ReactNode | - | 显示在选择器下方的辅助说明文字。 |
| error | string | object | - | 错误信息字符串,或带 `match` 键的校验错误对象。 |
| onValueChange | (value: T) => void | - | 选中项变化时的回调 |
| defaultValue | T | - | 非受控模式下的初始值 |
| renderValue | (value: T) => ReactNode | - | 返回 ReactNode 的函数,用于格式化触发元素中显示的选中值。使用对象值时必填。空状态请使用 `placeholder`。 |
| items | Record<string, string> | Array<{ label: ReactNode; value: T }> | - | 弹出层中渲染的选项数据结构。接受普通对象映射(`{ key: "Label" }`)或 `{ label, value }` 数组,用于对象或复杂值。 |
| isItemEqualToValue | (item: T, value: T) => boolean | - | 用于比较选项的自定义相等判断函数。值为对象时必填,因为对象引用(`===`)在不同渲染之间无法匹配。 |
Select.Option
| 属性 | 类型 | 默认值 |
|---|
该组件没有专有属性,支持标准 HTML 属性。
Select.Group
使用具有无障碍语义的 role="group" 将相关选项组合在一起。与 Select.GroupLabel 搭配使用
可提供可见标题。
Select.GroupLabel
Select.Group 的可见标题。为了无障碍,它会自动与父级分组关联。
Select.Separator
选项分组之间的视觉分隔线。渲染时带有 role="separator"。