Un riel de categorías para tu tienda, en React

Los rieles de categorías —la fila pulsable de departamentos sobre la cuadrícula de una tienda— son los contenedores de scroll con más tráfico en e-commerce, y son menús, no carruseles: cada mosaico es un enlace, nada encaja a la fuerza, y que medio mosaico asome en el borde es justo lo que invita a desplazarse.

Arrastra el riel o usa las flechas: se desactivan en los extremos reales de la fila.

Por qué el scroll nativo gana en una tienda

Los rieles de tienda viven sobre el pliegue, en páginas donde peleas por cada punto de Lighthouse. Un motor de carrusel distribuye decenas de kilobytes de emulación de gestos para hacer lo que el navegador ya hace de forma nativa; esta librería pesa ≈5.7 kB min+gzip y deja el scroll a la plataforma, así que no hay parpadeo de hidratación: el riel se desplaza antes de que se cargue tu JavaScript, lo que también significa que funciona en el HTML renderizado en el servidor que ven tus rastreadores. Esta misma página es la prueba, renderizada en el servidor: la demo de arriba se desplaza con JavaScript desactivado.

La página de comparación tiene la tabla completa frente a Swiper, Embla, keen-slider y react-slick.

El seguimiento de visibilidad es una función de tienda

La visibilidad por elemento suena como un detalle de implementación hasta que la aplicas al merchandising:

Ajústalo a tu sistema de diseño

Los mosaicos son tus componentes —tarjetas con imagen, círculos, píldoras de texto— cada uno con un itemId. La altura y el ancho vienen de tu CSS; el menú no impone dimensiones. Avanza de un elemento en uno como un slider de producto con one-item-scroll, muestra un indicador de progreso del scroll, o distribúyelo en RTL para tiendas en árabe y hebreo con el ejemplo RTL: el riel es composición, no configuración.

El patrón, en su forma mínima

Mosaicos con un itemId, flechas a partir de los hooks de visibilidad: el riel completo tiene menos de cuarenta líneas.

CategoryRail.tsx
function CategoryRail({ categories }: { categories: Category[] }) {
  return (
    <ScrollMenu LeftArrow={LeftArrow} RightArrow={RightArrow}>
      {categories.map((category) => (
        <CategoryTile itemId={category.id} key={category.id} {...category} />
      ))}
    </ScrollMenu>
  );
}

function CategoryTile({ itemId, name, image }: CategoryTileProps) {
  // Lazy images for free: placeholders until the tile is on screen.
  const visibility = React.useContext<publicApiType>(VisibilityContext);
  const isVisible = visibility.useIsVisible(itemId, false);
  return (
    <a href={`/category/${itemId}`} className="rail-tile">
      {isVisible ? <img src={image} alt="" /> : <div className="ph" />}
      <span>{name}</span>
    </a>
  );
}

function LeftArrow() {
  const visibility = React.useContext<publicApiType>(VisibilityContext);
  const atStart = visibility.useLeftArrowVisible();
  return (
    <button disabled={atStart} onClick={() => visibility.scrollPrev()}>

    </button>
  );
}

O instálalo como componente shadcn

El elemento base del registro scroll-menu es este riel —flechas con estilo shadcn, arrastre para desplazar, barra de scroll oculta— instalado en tu components/ui/ y con estilos según tus tokens:

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

Ejemplos relacionados

Todos los ejemplos (21)