Uma barra de chips de filtro que rola, em React
A fileira de chips embaixo de toda barra de busca — tópicos do YouTube, filtros de loja, seletores de tags — é um contêiner de rolagem de uma linha só, cheio de botões de alternância. Os 10% difíceis são o que acontece nas bordas: novos chips aparecendo fora da tela, arrastes que não podem alternar nada, e setas que sabem quando são inúteis.
Adicione um filtro — a fileira rola o novo chip até a área visível sozinha.
Edite este exemplo ao vivo no Storybook
Os casos extremos são a funcionalidade
Qualquer fileira flex com overflow-x: auto rola. Uma barra de chips justifica sua existência nos detalhes:
- Um chip adicionado fora da tela precisa se anunciar. A demo rola até cada novo chip com
apiRef.current.scrollToItem(el, 'smooth', 'end')após a renderização — o exemplo add-item-and-scroll-to-it é exatamente essa conexão. - Arraste para rolar, clique para alternar — nunca os dois. Usuários de desktop arrastam a fileira como uma superfície de toque; soltar sobre um chip não pode alterná-lo. A receita de arraste rastreia o gesto e suprime exatamente esse clique.
- Setas só quando úteis.
useLeftArrowVisible/useRightArrowVisibleestão conectadas ao mesmo IntersectionObserver que tudo o mais, então as setas se desativam nas bordas reais — inclusive depois que chips são adicionados ou removidos.
O estado continua nas suas mãos
A biblioteca rola; ela não é dona da seleção. Os chips são seus botões — aria-pressed para alternâncias de múltipla seleção, estado simples para seleção única — e o menu só precisa que cada um carregue um itemId. Isso significa que o estado dos chips se compõe com o que você já tem: parâmetros de busca da URL, uma biblioteca de formulários, um modelo de filtro controlado pelo servidor. Excluir um chip é remover um item; animar sua saída é o exemplo items-animation.
Mobile: um aviso sobre a rolagem do body
Em telas de toque, um swipe horizontal dentro da barra pode arrastar a página para o lado junto em alguns navegadores. Se isso acontecer, o exemplo prevent-body-scroll mostra o touch-action e a contenção de overscroll para travar isso — só CSS, sem biblioteca de gestos.
O padrão, no mínimo
Chips são botões de alternância com um itemId; uma ref para a API do menu rola um chip recém-adicionado até a área visível.
function ChipBar({ options }: { options: string[] }) {
const apiRef = React.useRef<publicApiType>(null);
const [selected, setSelected] = React.useState<string[]>([]);
const toggle = (id: string) =>
setSelected((cur) =>
cur.includes(id) ? cur.filter((c) => c !== id) : [...cur, id],
);
// A chip appended off-screen scrolls itself into view.
const addChip = (id: string) => {
toggle(id);
requestAnimationFrame(() => {
const el = apiRef.current?.getItemElementById(id);
if (el) apiRef.current?.scrollToItem(el, 'smooth', 'end');
});
};
return (
<ScrollMenu apiRef={apiRef}>
{options.map((id) => (
<Chip itemId={id} key={id} pressed={selected.includes(id)}
onToggle={() => toggle(id)} />
))}
</ScrollMenu>
);
}Ou instale como um componente shadcn
O item de registro chip-bar entrega isso como um componente controlado — options, selected, onSelectedChange — estilizado com Tailwind no seu components/ui/:
npx shadcn@latest add https://react-horizontal-scrolling-menu.dev/r/chip-bar.json