O menu horizontal que sabe o que está visível

Um menu de rolagem do React construído sobre a própria rolagem do navegador — rastreamento de visibilidade por item, setas, arraste e uma API imperativa completa. 5,7 kB gzip.

Sci-Fi
Comedy
Drama
Horror
Docs
Kids
Action
Indie
Anime
Classics
Thriller
Reality

getVisible() → ['scifi', 'comedy', 'drama', 'horror', 'docs', 'kids']

This is the library, live — drag it. Dimmed tiles are the ones useIsVisible reports as off-screen.

npm install react-horizontal-scrolling-menu347k downloads/mês5,7 kB min+gzipReact 16.8 – 19MIT

Autoplay, sem motor de carrossel

Não existe uma prop autoplay — este carril é uma receita sobre a API pública: a linha clonada nas duas pontas, um salto de scrollLeft na emenda e um temporizador chamando scrollNext(). Ele pausa no hover, no foco e em abas ocultas, fica parado sob movimento reduzido — e você pode arrastá-lo, até para trás, através da emenda.

Tokyo
Oslo
Lima
Cairo
Sydney
Quito
Seoul
Porto
Denver
Hanoi

Um menu, não um carrossel

Embla, Swiper e keen-slider reimplementam a rolagem em JavaScript para construir sliders de imagens — pontos de snap, física de mola, um loop de render. Esta biblioteca não entrega nada disso. Ela usa a rolagem nativa do navegador e adiciona a única coisa que o navegador não dá: saber exatamente quais itens estão na tela.

A ferramenta errada para um slider de imagens em tela cheia — use Embla ou Swiper lá. A ferramenta certa para linhas de categorias, faixas de abas, filtros de chips e qualquer linha de coisas sobre a qual seu app precise raciocinar.

Rolagem nativa

Inércia, barra de rolagem, toque, roda e acessibilidade vêm do navegador, não de um motor de física. A linha rola antes de seu JavaScript hidratar — cada demo desta página é renderizada no servidor.

Rastreamento de visibilidade

O IntersectionObserver informa quais itens estão na tela. useIsVisible(itemId) inscreve um componente em um item — sem matemática de posição de rolagem, e só os itens afetados são re-renderizados.

Imperativo quando você precisa

scrollToItem, scrollNext, scrollPrev, busca por id ou índice — pelo contexto dentro do menu, ou pelo apiRef de fora dele.

Seus componentes, seu CSS

Setas, header, footer e cada item são componentes que você escreve. A largura do item é seu CSS. A biblioteca entrega 210 bytes de estilos de layout e sai do caminho.

Início rápido

Um arquivo, sem configuração: itens com itemId, duas setas lendo VisibilityContext e a importação da folha de estilos.

App.tsx
import React from 'react';
import {
  ScrollMenu,
  VisibilityContext,
  type publicApiType,
} from 'react-horizontal-scrolling-menu';
import 'react-horizontal-scrolling-menu/dist/styles.css';

const items = Array.from({ length: 10 }, (_, i) => `item-${i + 1}`);

export function App() {
  return (
    <ScrollMenu LeftArrow={LeftArrow} RightArrow={RightArrow}>
      {items.map((id) => (
        <Card itemId={id} key={id} title={id} />
      ))}
    </ScrollMenu>
  );
}

function LeftArrow() {
  const visibility = React.useContext<publicApiType>(VisibilityContext);
  const isFirstVisible = visibility.useIsVisible('first', true);
  return (
    <button
      disabled={isFirstVisible}
      onClick={() => visibility.scrollPrev()}
    >

    </button>
  );
}

function RightArrow() {
  const visibility = React.useContext<publicApiType>(VisibilityContext);
  const isLastVisible = visibility.useIsVisible('last', false);
  return (
    <button
      disabled={isLastVisible}
      onClick={() => visibility.scrollNext()}
    >

    </button>
  );
}

function Card({ itemId, title }: { itemId: string; title: string }) {
  const visibility = React.useContext<publicApiType>(VisibilityContext);
  const isVisible = visibility.useIsVisible(itemId);
  return (
    <div className="card" data-visible={isVisible}>
      <div>{title}</div>
      <div>visible: {String(isVisible)}</div>
    </div>
  );
}

The code on the left, running:

item-1
visible: true
item-2
visible: true
item-3
visible: true
item-4
visible: false
item-5
visible: false
item-6
visible: false
item-7
visible: false
item-8
visible: false
item-9
visible: false
item-10
visible: false

itemId é obrigatório em cada item — é assim que o rastreamento funciona. A key do React funciona como fallback.

styles.css é uma importação separada; o bundle JS nunca injeta CSS.

A largura do item vem do seu próprio CSS — o menu não mede nada.

Ler o exemplo completo de primeiros passos

Ou entregue ao seu agente de código

Modelos treinados em versões antigas ainda procuram visibleElements, itens Separator e uma prop Arrows — tudo removido há anos — e inventam uma prop autoplay que nunca existiu. Para impedir isso, o pacote envia oito arquivos SKILL.md: orientação por tarefa que seu agente carrega sob demanda via TanStack Intent, versionada com a biblioteca em vez de com esta página.

npx @tanstack/intent@latest install

Execute uma vez em um projeto que já tenha o pacote instalado. Seu agente então descobre as habilidades em node_modules/react-horizontal-scrolling-menu/skills/.

  • menu-setupUm primeiro menu funcional, setas, a importação CSS necessária
  • menu-visibilityO que está na tela e o estado das setas nas bordas
  • menu-scrollingscrollToItem, apiRef, paginação página a página
  • menu-interactionsArraste, roda e toque — e suas fábricas de handlers
  • menu-recipesAutoplay, loop infinito, carregar mais: receitas, não props
  • menu-transitions-rtlTempo de animação, easing personalizado, direita para esquerda
  • menu-testing-ssrNext.js e RSC, mocks do Jest, Playwright
  • menu-migrationAtualizar código anterior à v8 e as APIs que os modelos ainda inventam

O que vem na caixa

  • Hooks de visibilidade por item — useIsVisible(itemId)
  • Helpers first / last para o estado das setas
  • scrollToItem · scrollNext · scrollPrev
  • apiRef para controle de fora do menu
  • Entrada por arraste, roda, toque e barra de rolagem
  • Detecção dinâmica de adicionar/remover
  • Slots Header e Footer
  • Helpers de paginação slidingWindow + getItemsPos
  • Suporte a direita para esquerda
  • Funções de transição personalizadas
  • Seguro para SSR — esta página prova
  • TypeScript-first — publicApiType exportado
  • Uma API estável do React 16.8 ao 19

Não vem na caixa

  • Física de snap e de mola
  • Sliders de imagens em tela cheia
  • Lightboxes

Isso pertence ao mundo dos sliders de imagens — Embla e Swiper fazem bem. O loop infinito e o autoplay também não são props — são receitas: cerca de sessenta linhas da API pública cada, editáveis ao vivo no Storybook. O carril perto do topo desta página é exatamente essa receita em execução. Isto continua sendo um menu.

Baixado 347.516 vezes no mês passado por cerca de 20.000 repositórios — mantido desde 2018.

788 estrelas no GitHubEm destaque no React Status #257Em produção no Our World in Data

Cada exemplo é editável, no seu navegador

O Storybook funciona como um playground: cada história vem com um editor Monaco carregado com as definições de tipo reais da biblioteca. Mude o código, veja-o re-renderizar — sem conta de sandbox, sem configuração local.

Construído e mantido por Aleksandr Smyshliaev

Publicado pela primeira vez em 2018, a mesma API pública do React 16.8 ao 19. Aleksandr é engenheiro frontend — React, Next.js, TypeScript — atualmente aberto a trabalho por contrato e em tempo integral.