Combobox 组合框
@lark-ui/core
import { useState } from "react";
import { Combobox } from "@lark-ui/core";

// Basic demo with TriggerInput
export function ComboboxDemo() {
  const [value, setValue] = useState<string | null>("苹果");

  return (
    <Combobox
      value={value}
      onValueChange={(v) => setValue(v as string | null)}
      items={fruits}
    >
      <Combobox.TriggerInput placeholder="请选择" />
      <Combobox.Content>
        <Combobox.Empty />
        <Combobox.List>
          {(item: string) => (
            <Combobox.Item key={item} value={item}>
              {item}
            </Combobox.Item>
          )}
        </Combobox.List>
      </Combobox.Content>
    </Combobox>
  );
}

安装

统一导出

import { Combobox } from "@lark-ui/core";

按需导出

import { Combobox } from "@lark-ui/core/components/combobox";

用法

import { useState } from "react";
import { Combobox } from "@lark-ui/core";

const fruits = ["Apple", "Banana", "Cherry", "Date", "Elderberry"];

export default function Example() {
  const [value, setValue] = useState<string | null>(null);

  return (
    <Combobox value={value} onValueChange={setValue} items={fruits}>
      <Combobox.TriggerInput placeholder="Select a fruit" />
      <Combobox.Content>
        <Combobox.Empty />
        <Combobox.List>
          {(item) => (
            <Combobox.Item key={item} value={item}>
              {item}
            </Combobox.Item>
          )}
        </Combobox.List>
      </Combobox.Content>
    </Combobox>
  );
}

示例

尺寸

组合框支持四种尺寸变体,与输入框(Input)组件保持一致:xs、sm、base(默认)和 lg。

import { useState } from "react";
import { Combobox } from "@lark-ui/core";

/** 演示组合框的不同尺寸变体:xs、sm、base 和 lg。 */
export function ComboboxSizesDemo() {
  const [smValue, setSmValue] = useState<string | null>(null);
  const [baseValue, setBaseValue] = useState<string | null>(null);

  return (
    <div className="flex flex-wrap items-center gap-4">
      <Combobox
        size="sm"
        value={smValue}
        onValueChange={(v) => setSmValue(v as string | null)}
        items={fruits.slice(0, 8)}
      >
        <Combobox.TriggerInput placeholder="小(sm)" />
        <Combobox.Content>
          <Combobox.Empty />
          <Combobox.List>
            {(item: string) => (
              <Combobox.Item key={item} value={item}>
                {item}
              </Combobox.Item>
            )}
          </Combobox.List>
        </Combobox.Content>
      </Combobox>
      <Combobox
        size="base"
        value={baseValue}
        onValueChange={(v) => setBaseValue(v as string | null)}
        items={fruits.slice(0, 8)}
      >
        <Combobox.TriggerInput placeholder="标准(默认)" />
        <Combobox.Content>
          <Combobox.Empty />
          <Combobox.List>
            {(item: string) => (
              <Combobox.Item key={item} value={item}>
                {item}
              </Combobox.Item>
            )}
          </Combobox.List>
        </Combobox.Content>
      </Combobox>
    </div>
  );
}

尺寸同样适用于 TriggerValue(内嵌可搜索变体):

import { useState } from "react";
import { Combobox } from "@lark-ui/core";
import { languages, Language } from "./data/languages";

/** 演示组合框使用 TriggerValue(内部可搜索)时的尺寸变体。 */
export function ComboboxSizesSearchableInsideDemo() {
  const [smValue, setSmValue] = useState<Language>(languages[0]);
  const [baseValue, setBaseValue] = useState<Language>(languages[1]);

  return (
    <div className="flex flex-wrap items-center gap-4">
      <Combobox
        size="sm"
        value={smValue}
        onValueChange={(v) => setSmValue(v as Language)}
        items={languages}
      >
        <Combobox.TriggerValue className="w-[160px]" />
        <Combobox.Content>
          <Combobox.Input placeholder="搜索" />
          <Combobox.Empty />
          <Combobox.List>
            {(item: Language) => (
              <Combobox.Item key={item.value} value={item}>
                {item.emoji} {item.label}
              </Combobox.Item>
            )}
          </Combobox.List>
        </Combobox.Content>
      </Combobox>
      <Combobox
        size="base"
        value={baseValue}
        onValueChange={(v) => setBaseValue(v as Language)}
        items={languages}
      >
        <Combobox.TriggerValue className="w-[180px]" />
        <Combobox.Content>
          <Combobox.Input placeholder="搜索" />
          <Combobox.Empty />
          <Combobox.List>
            {(item: Language) => (
              <Combobox.Item key={item.value} value={item}>
                {item.emoji} {item.label}
              </Combobox.Item>
            )}
          </Combobox.List>
        </Combobox.Content>
      </Combobox>
    </div>
  );
}

可搜索选项(内嵌)

位于弹层内部的可搜索选择器组件,允许用户筛选并选择。

import { useState } from "react";
import { Combobox } from "@lark-ui/core";
import { languages, Language } from "./data/languages";

// Searchable inside popup with TriggerValue
export function ComboboxSearchableInsideDemo() {
  const [value, setValue] = useState<Language>(languages[0]);

  return (
    <Combobox
      value={value}
      onValueChange={(v) => setValue(v as Language)}
      items={languages}
    >
      <Combobox.TriggerValue className="w-[200px]" />
      <Combobox.Content>
        <Combobox.Input placeholder="搜索语言" />
        <Combobox.Empty />
        <Combobox.List>
          {(item: Language) => (
            <Combobox.Item key={item.value} value={item}>
              {item.emoji} {item.label}
            </Combobox.Item>
          )}
        </Combobox.List>
      </Combobox.Content>
    </Combobox>
  );
}

带占位符的可搜索选择器

使用带 placeholder 属性的 TriggerValue 可创建类似选择器(Select)的可搜索字段。 在选中某个值之前,显示该占位符。

import { useState } from "react";
import { Combobox } from "@lark-ui/core";
import { languages, Language } from "./data/languages";

/** 演示使用带占位符的 TriggerValue,行为类似可搜索的选择器字段。
 * 在选中某个值之前始终显示占位符。 */
export function ComboboxSearchableSelectDemo() {
  const [value, setValue] = useState<Language | null>(null);

  return (
    <Combobox
      value={value}
      onValueChange={(v) => setValue(v as Language | null)}
      items={languages}
    >
      <Combobox.TriggerValue className="w-[200px]" placeholder="选择语言" />
      <Combobox.Content>
        <Combobox.Input placeholder="搜索语言" />
        <Combobox.Empty />
        <Combobox.List>
          {(item: Language) => (
            <Combobox.Item key={item.value} value={item}>
              {item.emoji} {item.label}
            </Combobox.Item>
          )}
        </Combobox.List>
      </Combobox.Content>
    </Combobox>
  );
}

对象选项集合

使用 Combobox.createItems() 从应用对象派生稳定的值和标签。

import { useState } from "react";
import { Combobox } from "@lark-ui/core";

/** 演示从应用对象派生组合框(Combobox)的值和标签。 */
export function ComboboxCreateItemsDemo() {
  const [value, setValue] = useState<DatabaseItem | null>(null);

  return (
    <Combobox
      value={value}
      onValueChange={(nextValue) => setValue(nextValue as DatabaseItem | null)}
      items={databaseItems}
    >
      <Combobox.TriggerValue className="w-[240px]" placeholder="选择数据库" />
      <Combobox.Content>
        <Combobox.Input placeholder="搜索数据库" />
        <Combobox.Empty>未找到数据库。</Combobox.Empty>
        <Combobox.List>
          {(database: DatabaseItem) => (
            <Combobox.Item key={database.value} value={database}>
              {database.label}
            </Combobox.Item>
          )}
        </Combobox.List>
      </Combobox.Content>
    </Combobox>
  );
}

自定义触发器

使用带 render 属性的 Combobox.Trigger,可用你自己的元素替换默认的类输入框触发器。搭配 Combobox.Value 可显示所选值。适用于账号切换器、侧边栏导航,或任何默认外观不适用的场景。

import { useState } from "react";
import { CaretUpDownIcon } from "@phosphor-icons/react";
import { Combobox, Button } from "@lark-ui/core";
import { languages, Language } from "./data/languages";

export function ComboboxCustomTriggerDemo() {
  const [value, setValue] = useState<Language>(languages[0]);

  return (
    <Combobox
      value={value}
      onValueChange={(v) => setValue(v as Language)}
      items={languages}
    >
      <Combobox.Trigger render={<Button variant="ghost" size="sm" />}>
        <Combobox.Value>
          <span className="truncate">
            {value.emoji} {value.label}
          </span>
        </Combobox.Value>
        <CaretUpDownIcon size={14} className="shrink-0 text-lark-subtle" />
      </Combobox.Trigger>
      <Combobox.Content>
        <Combobox.Input placeholder="搜索语言" />
        <Combobox.Empty />
        <Combobox.List>
          {(item: Language) => (
            <Combobox.Item key={item.value} value={item}>
              {item.emoji} {item.label}
            </Combobox.Item>
          )}
        </Combobox.List>
      </Combobox.Content>
    </Combobox>
  );
}

分组

使用 Group 和 GroupLabel 组件将选项按类别分组。

import { useState } from "react";
import { Combobox } from "@lark-ui/core";

// Grouped items demo
export function ComboboxGroupedDemo() {
  const [value, setValue] = useState<ServerLocation | null>(null);

  return (
    <Combobox
      value={value}
      onValueChange={(v) => setValue(v as ServerLocation | null)}
      items={servers}
    >
      <Combobox.TriggerInput className="w-[200px]" placeholder="选择服务器" />
      <Combobox.Content>
        <Combobox.Empty />
        <Combobox.List>
          {(group: ServerLocationGroup) => (
            <Combobox.Group key={group.value} items={group.items}>
              <Combobox.GroupLabel>{group.value}</Combobox.GroupLabel>
              <Combobox.Collection>
                {(item: ServerLocation) => (
                  <Combobox.Item key={item.value} value={item}>
                    {item.label}
                  </Combobox.Item>
                )}
              </Combobox.Collection>
            </Combobox.Group>
          )}
        </Combobox.List>
      </Combobox.Content>
    </Combobox>
  );
}

多选

允许用户从列表中选择多个选项。

import { useState } from "react";
import { Combobox, Text, Button } from "@lark-ui/core";

export function ComboboxMultipleDemo() {
  const [value, setValue] = useState<BotItem[]>([]);

  return (
    <div className="flex gap-2">
      <Combobox
        value={value}
        onValueChange={setValue}
        items={bots}
        isItemEqualToValue={(bot: BotItem, selected: BotItem) =>
          bot.value === selected.value
        }
        multiple
      >
        <Combobox.TriggerMultipleWithInput
          className="w-[400px]"
          placeholder="选择机器人"
          renderItem={(selected: BotItem) => (
            <Combobox.Chip key={selected.value}>{selected.label}</Combobox.Chip>
          )}
          inputSide="right"
        />
        <Combobox.Content className="max-h-[200px] min-w-auto overflow-y-auto">
          <Combobox.Empty />
          <Combobox.List>
            {(item: BotItem) => (
              <Combobox.Item key={item.value} value={item}>
                <div className="flex gap-2">
                  <Text>{item.label}</Text>
                  <Text variant="secondary">{item.author}</Text>
                </div>
              </Combobox.Item>
            )}
          </Combobox.List>
        </Combobox.Content>
      </Combobox>
      <Button variant="primary">提交</Button>
    </div>
  );
}

搭配 Field 使用

使用内置的 Field 包装器添加标签和描述。

选择你偏好的数据库

import { useState } from "react";
import { Combobox } from "@lark-ui/core";

export function ComboboxWithFieldDemo() {
  const [value, setValue] = useState<DatabaseItem | null>(null);

  return (
    <div className="w-80">
      <Combobox
        items={databases}
        value={value}
        onValueChange={setValue}
        label="数据库"
        description="选择你偏好的数据库"
      >
        <Combobox.TriggerInput placeholder="选择数据库" />
        <Combobox.Content>
          <Combobox.Empty />
          <Combobox.List>
            {(item: DatabaseItem) => (
              <Combobox.Item key={item.value} value={item}>
                {item.label}
              </Combobox.Item>
            )}
          </Combobox.List>
        </Combobox.Content>
      </Combobox>
    </div>
  );
}

禁用

传入 disabled 属性可阻止交互。对 TriggerInput 和 TriggerValue 均有效。

import { Combobox } from "@lark-ui/core";
import { languages, Language } from "./data/languages";

export function ComboboxDisabledDemo() {
  return (
    <div className="flex flex-wrap items-start gap-4">
      <Combobox value="苹果" items={fruits} disabled>
        <Combobox.TriggerInput className="w-[200px]" placeholder="选择水果" />
        <Combobox.Content>
          <Combobox.Empty />
          <Combobox.List>
            {(item: string) => (
              <Combobox.Item key={item} value={item}>
                {item}
              </Combobox.Item>
            )}
          </Combobox.List>
        </Combobox.Content>
      </Combobox>

      <Combobox value={languages[0]} items={languages} disabled>
        <Combobox.TriggerValue className="w-[200px]" />
        <Combobox.Content>
          <Combobox.Input placeholder="搜索" />
          <Combobox.Empty />
          <Combobox.List>
            {(item: Language) => (
              <Combobox.Item key={item.value} value={item}>
                {item.emoji} {item.label}
              </Combobox.Item>
            )}
          </Combobox.List>
        </Combobox.Content>
      </Combobox>
    </div>
  );
}

禁用的选项

为单个 Combobox.Item 传入 disabled 属性可使其不可选。禁用的行会以弱化样式呈现,并在键盘导航选择时被跳过。

import { useState } from "react";
import { Combobox, Text } from "@lark-ui/core";

/** 演示禁用单个条目。`Combobox.Item` 上的 `disabled` 属性会阻止点击和键盘选择,
 * 并以弱化文字和禁止光标渲染该行。适用于展示存在但用户无法选择的选项
 * (例如受权限限制、只读或已在别处使用)。 */
export function ComboboxDisabledItemsDemo() {
  type DatabaseItemWithDisabled = DatabaseItem & {
    disabled?: boolean;
    reason?: string;
  };

  const items: DatabaseItemWithDisabled[] = [
    { value: "postgres", label: "PostgreSQL" },
    { value: "mysql", label: "MySQL" },
    { value: "mariadb", label: "MariaDB", disabled: true, reason: "Beta" },
    { value: "mongodb", label: "MongoDB" },
    {
      value: "cassandra",
      label: "Apache Cassandra",
      disabled: true,
      reason: "即将推出",
    },
    { value: "redis", label: "Redis" },
    { value: "d1", label: "Lark Base" },
  ];

  const [value, setValue] = useState<DatabaseItemWithDisabled | null>(null);

  return (
    <div className="w-80">
      <Combobox value={value} onValueChange={setValue} items={items}>
        <Combobox.TriggerInput placeholder="选择数据库" />
        <Combobox.Content>
          <Combobox.Empty />
          <Combobox.List>
            {(item: DatabaseItemWithDisabled) => (
              <Combobox.Item
                key={item.value}
                value={item}
                disabled={item.disabled}
              >
                <span>
                  {item.label}
                  {item.reason && (
                    <Text variant="secondary" size="xs" as="span">
                      {" — "}
                      {item.reason}
                    </Text>
                  )}
                </span>
              </Combobox.Item>
            )}
          </Combobox.List>
        </Combobox.Content>
      </Combobox>
    </div>
  );
}

错误状态

通过 error 属性展示校验错误。

请选择一个数据库
import { useState } from "react";
import { Combobox } from "@lark-ui/core";

export function ComboboxErrorDemo() {
  const [value, setValue] = useState<DatabaseItem | null>(null);

  return (
    <div className="w-80">
      <Combobox
        items={databases}
        value={value}
        onValueChange={setValue}
        label="数据库"
        error={{ message: "请选择一个数据库", match: true }}
      >
        <Combobox.TriggerInput placeholder="选择数据库" />
        <Combobox.Content>
          <Combobox.Empty />
          <Combobox.List>
            {(item: DatabaseItem) => (
              <Combobox.Item key={item.value} value={item}>
                {item.label}
              </Combobox.Item>
            )}
          </Combobox.List>
        </Combobox.Content>
      </Combobox>
    </div>
  );
}

过滤

默认情况下,过滤不区分大小写和重音符号,底层由 Intl.Collator 提供支持。对于字符串选项,无需自定义 filter。

当需要按对象选项的某个属性进行过滤时,请使用 Combobox.useFilter() 以 保留内置的不区分重音符号的匹配能力:

function LanguagePicker() {
  const { contains } = Combobox.useFilter();

  const filter = useCallback(
    (item: Language, query: string) => contains(item.label, query),
    [contains],
  );

  return (
    <Combobox items={languages} filter={filter}>
      {/* ... */}
    </Combobox>
  );
}

如需完全关闭过滤(例如结果来自服务端),传入 filter={null}:

<Combobox items={results} filter={null}>
  ...
</Combobox>

对象选项集合

当选项是应用对象而非基本类型值时,请使用 Combobox.createItems()。它能派生稳定的选项值和标签,同时把源对象保留给列表渲染器使用。

import { useState } from "react";
import { Combobox } from "@lark-ui/core";

type Fruit = { id: string; label: string };

const fruits = Combobox.createItems<Fruit>(
  [
    { id: "apple", label: "Apple" },
    { id: "banana", label: "Banana" },
  ],
  {
    getValue: (fruit) => fruit.id,
    getLabel: (fruit) => fruit.label,
  },
);

function FruitPicker() {
  const [value, setValue] = useState<Fruit | null>(null);

  return (
    <Combobox items={fruits} value={value} onValueChange={setValue}>
      <Combobox.TriggerInput placeholder="Select a fruit" />
      <Combobox.Content>
        <Combobox.List>
          {(fruit) => (
            <Combobox.Item key={fruit.id} value={fruit}>
              {fruit.label}
            </Combobox.Item>
          )}
        </Combobox.List>
      </Combobox.Content>
    </Combobox>
  );
}

在模块作用域创建静态集合。对于运行时变化的数据,请把源数据作为依赖来 memoize Combobox.createItems()。

自定义下拉高度

默认情况下,Combobox.Content 的最大高度为 24rem(384px)或可用视口空间中的较小值。内容超过该高度时,下拉层会自动滚动。

如需自定义最大高度,向 Combobox.Content 传入 className:

// Shorter dropdown (200px)
<Combobox.Content className="max-h-[200px]">

// Taller dropdown (500px)
<Combobox.Content className="max-h-[500px]">

// Use Tailwind presets
<Combobox.Content className="max-h-64">  // 256px
<Combobox.Content className="max-h-96">  // 384px (same as default)

API 参考

Combobox

可搜索选择器的根组件。

属性类型默认值说明
size"xs" | "sm" | "base" | "lg""base"组合框触发器的尺寸。与 Input 组件的尺寸一致。 - `"xs"` — 适合紧凑界面的超小尺寸(h-5 / 20px) - `"sm"` — 适合次要字段的小尺寸(h-6.5 / 26px) - `"base"` — 默认尺寸(h-9 / 36px) - `"lg"` — 适合突出字段的大尺寸(h-10 / 40px)
inputSide"right" | "top""right"多选模式下文本输入框相对于标签块的位置。 - `"right"` — 输入框内联在标签块右侧 - `"top"` — 输入框位于标签块上方
items*T[]-下拉菜单中要展示的选项数组
valueT | T[]-当前选中的值
childrenReactNode-组合框的内容(触发器、内容、选项)
classNamestring-额外的 CSS 类名
labelReactNode-组合框的标签内容(启用 Field 包裹层)——可以是字符串或任意 React 节点
requiredboolean-组合框是否必填
labelTooltipReactNode-通过信息图标显示在标签旁的工具提示内容
descriptionReactNode-显示在组合框下方的辅助说明文字
errorstring | object-错误信息或校验错误对象
onValueChange(value: T | T[]) => void-选中项变化时的回调
multipleboolean-是否允许多选
isItemEqualToValue(item: T, value: T) => boolean-用于比较选项的自定义相等判断函数

Combobox.Content

列表的下拉容器。

属性类型默认值
classNamestring-
alignComboboxBase.Positioner.Props["align"]-
alignOffsetComboboxBase.Positioner.Props["alignOffset"]-
sideComboboxBase.Positioner.Props["side"]-
sideOffsetComboboxBase.Positioner.Props["sideOffset"]-
anchorComboboxBase.Positioner.Props["anchor"]-
positionMethodComboboxBase.Positioner.Props["positionMethod"]-
collisionAvoidanceComboboxBase.Positioner.Props["collisionAvoidance"]-
collisionBoundaryComboboxBase.Positioner.Props["collisionBoundary"]-
collisionPaddingComboboxBase.Positioner.Props["collisionPadding"]-
stickyComboboxBase.Positioner.Props["sticky"]-
disableAnchorTrackingComboboxBase.Positioner.Props["disableAnchorTracking"]-
containerPortalContainer-

Combobox.Item

单个可选项。

属性类型默认值

该组件没有专有属性,支持标准 HTML 属性。

其他子组件

  • Combobox.TriggerInput - 单选的输入框触发器
  • Combobox.TriggerValue - 显示所选值的按钮触发器
  • Combobox.TriggerMultipleWithInput - 带标签的多选
  • Combobox.Input - 下拉层内的搜索输入框
  • Combobox.List - 支持 render 属性的列表容器
  • Combobox.Group - 用于分类选项的分组容器
  • Combobox.GroupLabel - 分组的标题标签
  • Combobox.Collection - 分组内的选项容器
  • Combobox.Chip - 已选项的标签
  • Combobox.Empty - 空状态文案
  • Combobox.createItems - 面向对象选项的集合辅助函数