Combobox
Campo de texto com lista filtrada enquanto o usuário digita (autocomplete).
'use client';
import { ComboBox, ComboBoxContent, ComboBoxInput, ComboBoxItem } from '@/components/ui/combobox';
const cidades = [
{ id: 'sp', nome: 'São Paulo — SP' },
{ id: 'rj', nome: 'Rio de Janeiro — RJ' },
{ id: 'bh', nome: 'Belo Horizonte — MG' },
{ id: 'cwb', nome: 'Curitiba — PR' },
{ id: 'poa', nome: 'Porto Alegre — RS' },
{ id: 'ssa', nome: 'Salvador — BA' },
{ id: 'rec', nome: 'Recife — PE' },
{ id: 'for', nome: 'Fortaleza — CE' },
{ id: 'gyn', nome: 'Goiânia — GO' },
{ id: 'bsb', nome: 'Brasília — DF' },
];
export default function ComboboxDemo() {
return (
<ComboBox name="cidade" label="Cidade" description="Digite para filtrar." className="w-full max-w-xs">
<ComboBoxInput placeholder="Buscar cidade…" />
<ComboBoxContent items={cidades}>{(item) => <ComboBoxItem>{item.nome}</ComboBoxItem>}</ComboBoxContent>
</ComboBox>
);
}Instalação
npx shadcn@latest add @lenstech/comboboxUso
import { ComboBox, ComboBoxContent, ComboBoxInput, ComboBoxItem } from "@/components/ui/combobox"<ComboBox name="cidade" label="Cidade">
<ComboBoxInput placeholder="Buscar cidade…" />
<ComboBoxContent>
<ComboBoxItem id="cwb">Curitiba — PR</ComboBoxItem>
<ComboBoxItem id="sp">São Paulo — SP</ComboBoxItem>
</ComboBoxContent>
</ComboBox>O filtro já vem pronto (contém, sem diferenciar maiúsculas/acentos conforme o idioma) e a lista mostra "Nenhum resultado encontrado." quando nada combina.
Dois valores: o item escolhido e o texto digitado
value / onChange guardam o id do item selecionado; inputValue / onInputChange guardam o texto do
campo. Normalmente você só precisa do primeiro. No formulário, name envia o id (use formValue="text" para
enviar o texto). As props selectedKey / onSelectionChange estão obsoletas no React Aria.
Exemplos
Com seções e abrindo ao focar
menuTrigger="focus" abre a lista assim que o campo recebe foco — bom para listas curtas.
'use client';
import {
ComboBox,
ComboBoxContent,
ComboBoxInput,
ComboBoxItem,
ComboBoxSection,
} from '@/components/ui/combobox';
export default function ComboboxSections() {
return (
<ComboBox label="Produto" menuTrigger="focus" className="w-full max-w-xs">
<ComboBoxInput placeholder="Buscar produto…" />
<ComboBoxContent>
<ComboBoxSection title="Pães">
<ComboBoxItem id="frances">Pão francês</ComboBoxItem>
<ComboBoxItem id="integral">Pão integral</ComboBoxItem>
<ComboBoxItem id="ciabatta">Ciabatta</ComboBoxItem>
</ComboBoxSection>
<ComboBoxSection title="Doces">
<ComboBoxItem id="sonho">Sonho</ComboBoxItem>
<ComboBoxItem id="bolo">Bolo de cenoura</ComboBoxItem>
<ComboBoxItem id="brigadeiro">Brigadeiro</ComboBoxItem>
</ComboBoxSection>
</ComboBoxContent>
</ComboBox>
);
}Aceita valor livre
Com allowsCustomValue, o usuário pode digitar algo que não está na lista.
'use client';
import { ComboBox, ComboBoxContent, ComboBoxInput, ComboBoxItem } from '@/components/ui/combobox';
const bairros = ['Centro', 'Batel', 'Água Verde', 'Portão', 'Bigorrilho', 'Santa Felicidade'];
export default function ComboboxCustomValue() {
return (
<ComboBox
name="bairro"
label="Bairro"
allowsCustomValue
description="Não encontrou? Digite o nome do bairro."
className="w-full max-w-xs"
>
<ComboBoxInput placeholder="Ex.: Centro" />
<ComboBoxContent>
{bairros.map((bairro) => (
<ComboBoxItem key={bairro} id={bairro}>
{bairro}
</ComboBoxItem>
))}
</ComboBoxContent>
</ComboBox>
);
}Controlado com conteúdo rico
Se o item tiver mais que texto, informe textValue — é ele que aparece no campo e é usado no filtro.
Nenhum cliente selecionado.
'use client';
import { useState } from 'react';
import type { Key } from 'react-aria-components';
import { ComboBox, ComboBoxContent, ComboBoxInput, ComboBoxItem } from '@/components/ui/combobox';
const clientes = [
{ id: 1, nome: 'Padaria Dourada', cidade: 'Curitiba' },
{ id: 2, nome: 'Clínica Sorriso Feliz', cidade: 'Londrina' },
{ id: 3, nome: 'Oficina do Zé', cidade: 'Maringá' },
{ id: 4, nome: 'Escritório Andrade Contabilidade', cidade: 'Cascavel' },
];
export default function ComboboxControlled() {
const [cliente, setCliente] = useState<Key | null>(null);
const atual = clientes.find((c) => c.id === cliente);
return (
<div className="flex w-full max-w-xs flex-col gap-3">
<ComboBox label="Cliente" value={cliente} onChange={setCliente}>
<ComboBoxInput placeholder="Buscar cliente…" />
<ComboBoxContent items={clientes}>
{(c) => (
<ComboBoxItem textValue={c.nome}>
<div className="flex flex-col gap-0.5">
<span>{c.nome}</span>
<span className="text-xs text-muted-foreground">{c.cidade}</span>
</div>
</ComboBoxItem>
)}
</ComboBoxContent>
</ComboBox>
<p className="text-sm text-muted-foreground">
{atual ? `Selecionado: ${atual.nome} (${atual.cidade})` : 'Nenhum cliente selecionado.'}
</p>
</div>
);
}Obrigatório
'use client';
import { Button } from '@/components/ui/button';
import { ComboBox, ComboBoxContent, ComboBoxInput, ComboBoxItem } from '@/components/ui/combobox';
import { Form } from '@/components/ui/form';
const estados = [
{ id: 'pr', nome: 'Paraná' },
{ id: 'sc', nome: 'Santa Catarina' },
{ id: 'rs', nome: 'Rio Grande do Sul' },
];
export default function ComboboxInvalid() {
return (
<Form onSubmit={(e) => e.preventDefault()} className="w-full max-w-xs">
<ComboBox
name="estado"
label="Estado de atendimento"
isRequired
errorMessage="Atendemos apenas os estados da região Sul."
>
<ComboBoxInput placeholder="Buscar estado…" />
<ComboBoxContent items={estados}>{(e) => <ComboBoxItem>{e.nome}</ComboBoxItem>}</ComboBoxContent>
</ComboBox>
<Button type="submit" className="self-start">
Verificar cobertura
</Button>
</Form>
);
}Desabilitado
'use client';
import { ComboBox, ComboBoxContent, ComboBoxInput, ComboBoxItem } from '@/components/ui/combobox';
export default function ComboboxDisabled() {
return (
<ComboBox label="Vendedor responsável" defaultValue="marcos" isDisabled className="w-full max-w-xs">
<ComboBoxInput />
<ComboBoxContent>
<ComboBoxItem id="marcos">Marcos Oliveira</ComboBoxItem>
<ComboBoxItem id="julia">Júlia Ferreira</ComboBoxItem>
</ComboBoxContent>
</ComboBox>
);
}Quando usar
- Use para listas longas (cidades, clientes, produtos) ou quando o usuário já sabe o que procura.
- Não use com poucas opções — use
SelectouRadio Group. - Não use para busca livre que leva a uma página de resultados — use
Search Field.
API
ComboBox
Aceita todas as props do ComboBox do React Aria, mais:
Prop
Type
ComboBoxInput
Aceita as props do Input do React Aria (placeholder…), mais groupClassName (classes da caixa externa com o
botão de abrir).
ComboBoxContent
Aceita as props do ListBox (items…), mais:
Prop
Type
ComboBoxItem e ComboBoxSection
O mesmo que ListBoxItem e ListBoxSection.