Le menu horizontal qui sait ce qui est visible

Un menu à défilement React construit sur le propre défilement du navigateur — suivi de visibilité par élément, flèches, glisser et une API impérative complète. 5,7 kB gzippé.

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 téléchargements/mois5,7 kB min+gzipReact 16.8 – 19MIT

Lecture automatique, sans moteur de carrousel

Il n’y a pas de prop autoplay — ce rail est une recette sur l’API publique : la rangée clonée aux deux extrémités, un saut de scrollLeft à la couture et un minuteur appelant scrollNext(). Il se met en pause au survol, au focus et sur les onglets cachés, reste immobile sous mouvement réduit — et vous pouvez le faire glisser, même en arrière, à travers la couture.

Tokyo
Oslo
Lima
Cairo
Sydney
Quito
Seoul
Porto
Denver
Hanoi

Un menu, pas un carrousel

Embla, Swiper et keen-slider réimplémentent le défilement en JavaScript pour construire des sliders d’images : points d’accroche, physique de ressort, boucle de rendu. Cette bibliothèque ne fournit rien de tout cela. Elle s’appuie sur le défilement natif du navigateur et ajoute la seule chose que le navigateur ne donne pas : savoir exactement quels éléments sont à l’écran.

Le mauvais outil pour un slider d’images plein écran — utilisez Embla ou Swiper là-bas. Le bon outil pour les rangées de catégories, les onglets, les filtres à puces et toute rangée d’éléments dont votre application a besoin de raisonner.

Défilement natif

L’inertie, la barre de défilement, le toucher, la molette et l’accessibilité viennent du navigateur, pas d’un moteur physique. La rangée défile avant que votre JavaScript ne s’hydrate — chaque démo de cette page est rendue côté serveur.

Suivi de visibilité

IntersectionObserver signale quels éléments sont à l’écran. useIsVisible(itemId) abonne un composant à un élément — aucun calcul de position de défilement, et seuls les éléments concernés sont re-rendus.

Impératif quand vous en avez besoin

scrollToItem, scrollNext, scrollPrev, recherche par id ou index — via le contexte dans le menu, ou apiRef depuis l’extérieur.

Vos composants, votre CSS

Les flèches, l’en-tête, le pied de page et chaque élément sont des composants que vous écrivez. La largeur de l’élément est votre CSS. La bibliothèque fournit 210 octets de styles de mise en page et s’écarte du chemin.

Démarrage rapide

Un fichier, aucune configuration : des éléments avec itemId, deux flèches lisant VisibilityContext et l’import de la feuille de styles.

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 est requis sur chaque élément — c’est ainsi que fonctionne le suivi. La key de React sert de solution de repli.

styles.css est un import séparé ; le bundle JS n’injecte jamais de CSS.

La largeur de l’élément vient de votre propre CSS — le menu ne mesure rien.

Lire l’exemple complet de démarrage

Ou confiez-le à votre agent de code

Les modèles entraînés sur d’anciennes versions recherchent encore visibleElements, les éléments Separator et une prop Arrows — tous supprimés il y a des années — et inventent une prop autoplay qui n’a jamais existé. Pour mettre fin à cela, le paquet embarque huit fichiers SKILL.md : des guides par tâche que votre agent charge à la demande via TanStack Intent, versionnés avec la bibliothèque plutôt qu’avec cette page.

npx @tanstack/intent@latest install

Exécutez une fois dans un projet où le paquet est déjà installé. Votre agent découvre ensuite les compétences depuis node_modules/react-horizontal-scrolling-menu/skills/.

  • menu-setupUn premier menu fonctionnel, des flèches, l’import CSS requis
  • menu-visibilityCe qui est à l’écran et l’état des flèches aux extrémités
  • menu-scrollingscrollToItem, apiRef, pagination page par page
  • menu-interactionsGlisser, molette et toucher — et leurs fabriques de gestionnaires
  • menu-recipesLecture auto, boucle infinie, charger plus : des recettes, pas des props
  • menu-transitions-rtlRythme d’animation, easing personnalisé, droite à gauche
  • menu-testing-ssrNext.js et RSC, mocks Jest, Playwright
  • menu-migrationMettre à niveau le code antérieur à v8 et les API que les modèles inventent encore

Ce qu’il y a dans la boîte

  • Hooks de visibilité par élément — useIsVisible(itemId)
  • Helpers first / last pour l’état des flèches
  • scrollToItem · scrollNext · scrollPrev
  • apiRef pour le contrôle hors du menu
  • Entrée par glisser, molette, toucher et barre de défilement
  • Détection dynamique d’ajout/suppression
  • Emplacements Header et Footer
  • Helpers de pagination slidingWindow + getItemsPos
  • Prise en charge de droite à gauche
  • Fonctions de transition personnalisées
  • Sûr pour le SSR — cette page le prouve
  • TypeScript-first — publicApiType exporté
  • Une API stable de React 16.8 à 19

Pas dans la boîte

  • Physique d’accroche et de ressort
  • Sliders d’images plein écran
  • Lightboxes

Cela appartient au monde des sliders d’images — Embla et Swiper le font bien. La boucle infinie et la lecture automatique ne sont pas non plus des props — ce sont des recettes : environ soixante lignes de l’API publique chacune, modifiables en direct dans Storybook. Le rail en haut de cette page est exactement cette recette en cours d’exécution. Cela reste un menu.

Téléchargé 347 516 fois le mois dernier par environ 20 000 dépôts — maintenu depuis 2018.

788 étoiles sur GitHubPrésenté dans React Status #257En production chez Our World in Data

Chaque exemple est modifiable, dans votre navigateur

Storybook fait office de terrain de jeu : chaque histoire est livrée avec un éditeur Monaco chargé des vraies définitions de types de la bibliothèque. Changez le code, regardez-le se re-rendre — sans compte sandbox ni configuration locale.

Créé et maintenu par Aleksandr Smyshliaev

Publié pour la première fois en 2018, la même API publique de React 16.8 à 19. Aleksandr est ingénieur frontend — React, Next.js, TypeScript — actuellement ouvert aux missions en freelance et à temps plein.