Menu ngang mà biết cái gì đang hiển thị

Một menu cuộn React được xây trên chính cuộn của trình duyệt — theo dõi khả năng hiển thị theo từng mục, mũi tên, kéo và một API mệnh lệnh đầy đủ. 5.7 kB gzip.

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 lượt tải/tháng5.7 kB min+gzipReact 16.8 – 19MIT

Tự phát, không cần engine carousel

Không có prop autoplay — ray này là một recipe trên API công khai: hàng được nhân bản ra hai đầu, một cú nhảy scrollLeft tại đường nối và một bộ hẹn giờ gọi scrollNext(). Nó tạm dừng khi hover, focus và trên các tab bị ẩn, đứng yên dưới chế độ giảm chuyển động — và bạn có thể kéo nó, thậm chí ngược lại, qua đường nối.

Tokyo
Oslo
Lima
Cairo
Sydney
Quito
Seoul
Porto
Denver
Hanoi

Một menu, không phải carousel

Embla, Swiper và keen-slider triển khai lại việc cuộn bằng JavaScript để xây slider ảnh — điểm snap, vật lý lò xo, một vòng lặp render. Thư viện này không gửi kèm bất cứ thứ gì trong số đó. Nó dùng cuộn gốc của trình duyệt và thêm thứ duy nhất trình duyệt không cho bạn: biết chính xác mục nào đang trên màn hình.

Sai công cụ cho một slider ảnh toàn màn hình — hãy dùng Embla hoặc Swiper ở đó. Đúng công cụ cho các hàng danh mục, dải tab, bộ lọc chip và bất kỳ hàng nội dung nào mà ứng dụng của bạn cần xử lý.

Cuộn gốc

Quán tính, thanh cuộn, cảm ứng, con lăn và khả năng tiếp cận đến từ trình duyệt, không phải một engine vật lý. Hàng cuộn trước khi JavaScript của bạn hydrate — mọi bản demo trên trang này được render trên server.

Theo dõi khả năng hiển thị

IntersectionObserver báo cáo mục nào đang trên màn hình. useIsVisible(itemId) đăng ký một component với một mục — không tính toán vị trí cuộn, và chỉ các mục bị ảnh hưởng mới render lại.

Mệnh lệnh khi bạn cần

scrollToItem, scrollNext, scrollPrev, tra cứu theo id hoặc index — qua context trong menu, hoặc apiRef từ bên ngoài.

Component của bạn, CSS của bạn

Mũi tên, header, footer và mọi mục là component bạn viết. Chiều rộng mục là CSS của bạn. Thư viện gửi kèm 210 byte style bố cục và tránh ra khỏi đường.

Bắt đầu nhanh

Một file, không cấu hình: các mục với itemId, hai mũi tên đọc VisibilityContext và import stylesheet.

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 bắt buộc trên mọi mục — đó là cách theo dõi hoạt động. key của React hoạt động như một phương án dự phòng.

styles.css là một import riêng; bundle JS không bao giờ tiêm CSS.

Chiều rộng mục đến từ CSS của riêng bạn — menu không đo lường gì cả.

Đọc ví dụ bắt đầu đầy đủ

Hoặc giao cho coding agent của bạn

Các mô hình được huấn luyện trên các phiên bản cũ vẫn tìm đến visibleElements, các mục Separator và một prop Arrows — tất cả đã bị xóa nhiều năm trước — và bịa ra một prop autoplay chưa từng tồn tại. Để ngăn điều đó, gói này gửi kèm tám file SKILL.md: hướng dẫn theo nhiệm vụ mà agent của bạn tải theo yêu cầu qua TanStack Intent, được lập phiên bản cùng thư viện thay vì cùng trang này.

npx @tanstack/intent@latest install

Chạy một lần trong dự án đã cài gói. Agent của bạn sau đó khám phá các kỹ năng từ node_modules/react-horizontal-scrolling-menu/skills/.

  • menu-setupMenu đầu tiên hoạt động, mũi tên, import CSS bắt buộc
  • menu-visibilityCái gì đang trên màn hình và trạng thái mũi tên ở hai đầu
  • menu-scrollingscrollToItem, apiRef, phân trang từng trang
  • menu-interactionsKéo, con lăn và cảm ứng — và các factory xử lý của chúng
  • menu-recipesTự phát, vòng lặp vô hạn, tải thêm: recipe, không phải props
  • menu-transitions-rtlThời gian hoạt ảnh, easing tùy chỉnh, phải sang trái
  • menu-testing-ssrNext.js và RSC, mock Jest, Playwright
  • menu-migrationNâng cấp code trước v8 và các API mà mô hình vẫn bịa ra

Có gì trong hộp

  • Hook khả năng hiển thị theo từng mục — useIsVisible(itemId)
  • Helper first / last cho trạng thái mũi tên
  • scrollToItem · scrollNext · scrollPrev
  • apiRef để điều khiển từ ngoài menu
  • Đầu vào kéo, con lăn, cảm ứng và thanh cuộn
  • Phát hiện thêm/xóa động
  • Slot Header và Footer
  • Helper phân trang slidingWindow + getItemsPos
  • Hỗ trợ phải sang trái
  • Hàm chuyển tiếp tùy chỉnh
  • An toàn với SSR — trang này chứng minh
  • TypeScript-first — publicApiType được export
  • Một API ổn định qua React 16.8 – 19

Không có trong hộp

  • Vật lý snap và lò xo
  • Slider ảnh toàn màn hình
  • Lightbox

Đó thuộc về thế giới slider ảnh — Embla và Swiper làm tốt. Vòng lặp vô hạntự phát cũng không phải props — chúng là recipe: khoảng sáu mươi dòng của API công khai mỗi cái, có thể chỉnh sửa trực tiếp trong Storybook. Ray gần đầu trang này chính là recipe đó đang chạy. Cái này vẫn là một menu.

Được tải 347,516 lần tháng trước bởi khoảng 20,000 kho lưu trữ — được bảo trì từ 2018.

788 sao trên GitHubĐược giới thiệu trên React Status #257Đang dùng trong sản phẩm tại Our World in Data

Mọi ví dụ đều có thể chỉnh sửa, trong trình duyệt của bạn

Storybook kiêm vai trò sân chơi: mỗi story gửi kèm một trình soạn thảo Monaco được nạp các định nghĩa kiểu thực của thư viện. Thay đổi code, xem nó render lại — không cần tài khoản sandbox, không cần thiết lập cục bộ.

Được xây dựng và duy trì bởi Aleksandr Smyshliaev

Xuất bản lần đầu năm 2018, cùng API công khai qua React 16.8 đến 19. Aleksandr là kỹ sư frontend — React, Next.js, TypeScript — hiện sẵn sàng cho công việc theo hợp đồng và toàn thời gian.