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.
Edita este ejemplo en vivo en Storybook
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:
- Imágenes diferidas — renderiza un mosaico marcador de posición hasta que
useIsVisibleindique que está en pantalla. - Analítica de impresiones —
getVisible()(en vivo en la demo del hero de la página de inicio) te dice exactamente qué categorías se vieron, no solo que el riel se renderizó. - Flechas conscientes de los bordes — se desactivan u ocultan en los extremos reales, incluso después de que las categorías se carguen de forma asíncrona, como en el ejemplo de añadir elementos.
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.
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:
npx shadcn@latest add https://react-horizontal-scrolling-menu.dev/r/scroll-menu.json