Fila horizontal estilo Netflix en React

La fila de pósters que recorres en cualquier plataforma de streaming no es un carrusel: es un menú. Nada encaja a la fuerza, nada se reproduce solo; usa el desplazamiento nativo con inercia, con flechas superpuestas encima. Eso es exactamente lo que ofrece react-horizontal-scrolling-menu: tus tarjetas, scroll nativo y visibilidad por elemento para que las flechas sepan cuándo ocultarse.

Arrástrala o pasa el cursor sobre la fila: las flechas aparecen sobre los bordes y cada una desaparece cuando se llega a ese extremo de la fila.

Por qué esto no es tarea de un carrusel

Una fila estilo Netflix nunca muestra una sola diapositiva a la vez. Los elementos se cortan a propósito en los bordes: el póster cortado es la señal de que "hay más". Los motores de carrusel luchan contra esto: controlan la capa de gestos con transformaciones de JavaScript, encajan en los límites de cada diapositiva y reimplementan la inercia que el navegador de tus usuarios ya tiene. En una fila de tarjetas pulsables, todo eso es sobrecarga innecesaria.

El scroll nativo te da inercia, tacto, trackpad y barra de desplazamiento gratis. Las dos cosas que no te da son las flechas superpuestas y saber qué tarjetas están en pantalla, y esas son justo las dos cosas que añade esta librería, mediante useIsVisible por elemento y un estado de flechas consciente de los bordes.

Los tres detalles que venden el efecto

Escalarlo: filas diferidas y rieles largos

Las interfaces de streaming apilan docenas de filas con cientos de tarjetas. Como los elementos son DOM normal dentro de un contenedor de scroll nativo, nada se vuelve a renderizar al desplazarse: el ejemplo de rendimiento mueve 300 elementos sin virtualización. La visibilidad por elemento también te da carga diferida de imágenes gratis: renderiza un marcador de posición hasta que useIsVisible indique que la tarjeta está en pantalla.

Si tu fila debe dar la vuelta al llegar al final, ese es el único caso en el que la semántica de diapositivas realmente ayuda: consulta la receta de bucle infinito para la versión de ~60 líneas hecha a mano, antes de recurrir a un motor de carrusel.

El patrón, en su forma mínima

Superpón flechas sobre una fila de scroll nativo: la demo de arriba es esta misma estructura más estilos. El código completo, listo para usar, con arrastre y desvanecido en los bordes, se distribuye como el componente shadcn de abajo.

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); */

O instálalo como componente shadcn

El elemento del registro media-row es exactamente este patrón —flechas al pasar el cursor, desvanecido con gradiente en los bordes, arrastre para desplazar— como componente con estilos Tailwind en tu components/ui/, listo para que lo edites:

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

Ejemplos relacionados

Todos los ejemplos (21)