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.

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:

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.

ChipBar.tsx
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/:

shadcn
npx shadcn@latest add https://react-horizontal-scrolling-menu.dev/r/chip-bar.json

Exemplos relacionados

Todos os exemplos (21)