Linha horizontal estilo Netflix em React

A fileira de pôsteres que você percorre em qualquer site de streaming não é um carrossel — é um menu. Nada se encaixa (snap), nada toca automaticamente; ela usa a rolagem nativa com inércia (momentum), com setas sobrepostas por cima. É exatamente isso que o react-horizontal-scrolling-menu entrega: seus cards, rolagem nativa e visibilidade por item para que as setas saibam quando se esconder.

Arraste, ou passe o mouse sobre a fileira — as setas aparecem gradualmente nas bordas, e cada uma desaparece quando aquele extremo da fileira é alcançado.

Por que isso não é trabalho para um carrossel

Uma fileira estilo Netflix nunca mostra um slide por vez. Os itens são propositalmente cortados nas bordas — o pôster cortado é o indício visual que diz "tem mais". Motores de carrossel lutam contra isso: eles assumem a camada de gestos com transformações em JavaScript, encaixam (snap) nos limites dos slides e reimplementam a inércia que o navegador do usuário já tem. Em uma fileira de cards clicáveis, tudo isso é overhead.

A rolagem nativa te dá inércia, toque, trackpad e barra de rolagem de graça. As duas coisas que ela não te dá são as setas sobrepostas e saber quais cards estão na tela — e são exatamente essas duas coisas que esta biblioteca adiciona, via useIsVisible por item e um estado de seta sensível às bordas.

Os três detalhes que vendem o efeito

Escalando: fileiras preguiçosas e trilhas longas

Interfaces de streaming empilham dezenas de fileiras com centenas de cards. Como os itens são DOM comum dentro de um contêiner de rolagem nativa, nada é renderizado novamente durante a rolagem — o exemplo de performance roda 300 itens sem virtualização. A visibilidade por item também te dá lazy-loading de imagens de graça: renderize um placeholder até que useIsVisible informe que o card está na tela.

Se a sua fileira precisa dar a volta no final, esse é o único lugar em que a semântica de slides realmente ajuda — veja a receita de loop infinito para a versão userland de ~60 linhas antes de recorrer a um motor de carrossel.

O padrão, no mínimo

Setas sobrepostas em uma fileira de rolagem nativa — a demo acima é essa estrutura mais estilização. O código-fonte completo, pronto para uso, com arraste e fade nas bordas, vem como o componente shadcn abaixo.

MediaRow.tsx
function MediaRow({ children }: { children: React.ReactNode }) {
  return (
    <div className="group relative">
      <ScrollMenu Header={<OverlayArrows />} wrapperClassName="edge-fade">
        {children}
      </ScrollMenu>
    </div>
  );
}

// Rendered as Header, so it lives inside the menu's VisibilityContext
// and can sit absolutely positioned over the row ends.
function OverlayArrows() {
  const api = React.useContext<publicApiType>(VisibilityContext);
  const atStart = api.useLeftArrowVisible();
  const atEnd = api.useRightArrowVisible();
  return (
    <>
      <button
        className="overlay-arrow left-0"
        hidden={atStart}
        onClick={() => api.scrollPrev()}
      >

      </button>
      <button
        className="overlay-arrow right-0"
        hidden={atEnd}
        onClick={() => api.scrollNext()}
      >

      </button>
    </>
  );
}

/* .edge-fade — the Netflix edge, one CSS line:
   mask-image: linear-gradient(to right,
     transparent, black 40px, black calc(100% - 40px), transparent); */

Ou instale como um componente shadcn

O item de registro media-row é exatamente esse padrão — setas ao passar o mouse, fade em gradiente nas bordas, arraste para rolar — como um componente estilizado com Tailwind no seu components/ui/, seu para editar:

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

Exemplos relacionados

Todos os exemplos (21)