Paleta de Comandos
Regras
- Use a biblioteca
cmdk — pesquisa fuzzy rápida, acessível e componível integrada
- Atalho:
Cmd+K (Mac) / Ctrl+K (Windows/Linux) — registre um ouvinte global de teclado
- Agrupe ações: Navegação (páginas), Comandos (ações), Recentes (últimos usados) — exiba cabeçalhos de grupo
- Navegação por teclado: setas para mover, Enter para selecionar, Escape para fechar — tudo gerenciado pelo
cmdk
- Pesquisa fuzzy:
cmdk filtra automaticamente — destaque de correspondência com ranqueamento baseado em pontuação
- Itens recentes: armazene os últimos 5-10 itens selecionados em
localStorage — mostre no topo quando a entrada estiver vazia
- Ações dinâmicas: registre ações de diferentes partes do aplicativo — sensível ao contexto (mostre ações diferentes em páginas diferentes)
- Estado de carregamento: resultados de pesquisa assíncrona (ex.: pesquisa via API) — exiba um indicador de carregamento na área de resultados
Padrões
import { Command } from "cmdk";
import { useEffect, useState } from "react";
import { useRouter } from "next/navigation";
function CommandPalette() {
const [open, setOpen] = useState(false);
const router = useRouter();
useEffect(() => {
const handleKeyDown = (e: KeyboardEvent) => {
if (e.key === "k" && (e.metaKey || e.ctrlKey)) {
e.preventDefault();
setOpen((prev) => !prev);
}
};
document.addEventListener("keydown", handleKeyDown);
return () => document.removeEventListener("keydown", handleKeyDown);
}, []);
return (
<Command.Dialog open={open} onOpenChange={setOpen} label="Paleta de comandos">
<Command.Input placeholder="Pesquise ou digite um comando..." />
<Command.List>
<Command.Empty>Nenhum resultado encontrado.</Command.Empty>
<Command.Group heading="Recentes">
{recentItems.map((item) => (
<Command.Item key={item.id} onSelect={() => { router.push(item.href); setOpen(false); }}>
{item.label}
</Command.Item>
))}
</Command.Group>
<Command.Group heading="Navegação">
<Command.Item onSelect={() => { router.push("/dashboard"); setOpen(false); }}>
Dashboard
</Command.Item>
<Command.Item onSelect={() => { router.push("/settings"); setOpen(false); }}>
Configurações
</Command.Item>
</Command.Group>
<Command.Group heading="Comandos">
<Command.Item onSelect={() => { toggleTheme(); setOpen(false); }}>
Alternar modo escuro
</Command.Item>
<Command.Item onSelect={() => { logout(); setOpen(false); }}>
Sair
</Command.Item>
</Command.Group>
</Command.List>
</Command.Dialog>
);
}
Evite
- Criar sua própria pesquisa fuzzy —
cmdk lida com filtragem, pontuação e ranqueamento prontos para uso
- Esquecer
e.preventDefault() no Cmd+K — sem isso, a pesquisa padrão ou barra de endereços do navegador será aberta
- Ignorar o tratamento da tecla Escape —
cmdk gerencia isso via onOpenChange, mas garanta que o foco retorne ao elemento anterior
- Ações apenas estáticas — registre ações dinamicamente a partir do contexto da página (ex.: "Editar este post" apenas na página do post)
- Sem estado vazio — sempre exiba "Nenhum resultado encontrado" quando a pesquisa não encontrar correspondências