---
title: "Tab cuộn React — không cần Material UI"
description: "Tab cuộn trong React với cuộn gốc trình duyệt: tab đang chọn tự căn giữa, mũi tên chỉ xuất hiện khi cần, nội dung tab tự do. Demo trực tiếp và mã nguồn."
canonical: "https://react-horizontal-scrolling-menu.dev/vi/scrollable-tabs"
image: "https://react-horizontal-scrolling-menu.dev/og.png"
---

# Tab cuộn React cuộn giống như trình duyệt

Một dải tab hết vừa ngay khi sản phẩm của bạn vượt quá sáu tab. Cách khắc phục không phải là font nhỏ hơn — mà là một dải có thể cuộn: phần tràn được xử lý bởi trình duyệt, nhấp vào một tab sẽ căn giữa nó, và mũi tên chỉ hiện lên khi còn chỗ để đi.

Overview

Getting started

Visibility

Arrows

Drag to scroll

RTL

apiRef

Helpers

TypeScript

Testing

Changelog

FAQ

Nhấp vào một tab gần cạnh — nó tự cuộn về giữa.

[Chỉnh sửa ví dụ này trực tiếp trong Storybook](https://asmyshlyaev177.github.io/react-horizontal-scrolling-menu/?path=/story/examples-centeronclick--center-on-click)

## Hành vi duy nhất quan trọng: căn giữa khi chọn

Một dải tab cuộn được sống hay chết dựa vào điều xảy ra khi bạn nhấp vào một tab ở cạnh: nó cần trượt về giữa, để lộ các tab lân cận ở cả hai bên. Đó là một lệnh gọi duy nhất ở đây — `scrollToItem(el, 'smooth', 'center')` — được nối trong [ví dụ căn giữa khi click](https://react-horizontal-scrolling-menu.dev/vi/examples/center-on-click.md). Khôi phục tab đang chọn khi mount là cùng lệnh gọi đó với `'auto'`, được minh họa trong [lưu & khôi phục vị trí](https://react-horizontal-scrolling-menu.dev/vi/examples/save-restore-position.md).

Các mũi tên đến từ cùng dữ liệu hiển thị: `useLeftArrowVisible` chỉ là false khi tab đầu tiên nằm ngoài màn hình, vì vậy mũi tên trái render chính xác khi nó hữu ích. Không cần code đo lường, không cần resize observer của riêng bạn.

## Nếu bạn đang vượt quá khả năng của tab cuộn MUI

Tab `variant="scrollable"` của Material UI là câu trả lời đúng bên trong hệ thống thiết kế Material — cho đến khi "tab" của bạn không còn là tab nữa. MUI hàn dải này vào ngữ nghĩa Tabs: một cặp `value`/`onChange`, các tab panel, và nút cuộn mà MUI mặc định ẩn trên mobile. Ngay khi dải của bạn chứa chip, thẻ, avatar hay nội dung hỗn hợp, hoặc cần kéo để cuộn, hoặc cần biết item nào đang hiển thị, bạn đang chống lại component thay vì sử dụng nó.

Thư viện này là tầng bên dưới điều đó: một dải cuộn có theo dõi khả năng hiển thị, không có quan điểm gì về "tab" là gì. Tab của bạn là bất kỳ component nào có `itemId` — style nó bằng Tailwind, `styled` của riêng MUI, hoặc CSS thuần. Trạng thái lựa chọn vẫn thuộc về bạn, hệt như demo ở trên giữ nó trong một `useState` duy nhất.

## Khả năng truy cập gần như miễn phí — chú ý hai lỗ hổng

Vì dải này là một vùng chứa cuộn gốc, focus bàn phím, thứ tự đọc của trình đọc màn hình và RTL đều đến từ nền tảng — di chuyển focus qua các tab tự cuộn chúng vào tầm nhìn mà không cần code, và [RTL](https://react-horizontal-scrolling-menu.dev/vi/examples/rtl.md) không cần cấu hình gì. Hai điều vẫn thuộc trách nhiệm của bạn, giống như bất kỳ UI tab nào: chọn mẫu ARIA của bạn (`role="tablist"` nếu panel thật sự chuyển đổi, `aria-current` nếu "tab" là điều hướng), và giữ việc ngăn click của công thức [kéo để cuộn](https://react-horizontal-scrolling-menu.dev/vi/examples/mouse-drag.md) để một lần thả kéo không bao giờ kích hoạt một tab.

## Mẫu hình, tối giản

Tab là các nút thuần với `itemId`; chọn một tab sẽ căn giữa nó. Đó là toàn bộ ý tưởng — demo ở trên thêm styling và kéo.

ScrollTabs.tsx

```
function ScrollTabs({ tabs }: { tabs: string[] }) {
  const [active, setActive] = React.useState(tabs[0]);
  return (
    <ScrollMenu LeftArrow={LeftArrow} RightArrow={RightArrow}>
      {tabs.map((tab) => (
        <Tab itemId={tab} key={tab} label={tab}
          active={tab === active} onSelect={() => setActive(tab)} />
      ))}
    </ScrollMenu>
  );
}

function Tab({ itemId, label, active, onSelect }: TabProps) {
  const api = React.useContext<publicApiType>(VisibilityContext);
  return (
    <button
      aria-current={active}
      onClick={(ev) => {
        onSelect();
        // The behavior that makes tabs feel native: selecting an
        // edge tab glides it to the center of the strip.
        api.scrollToItem(ev.currentTarget, 'smooth', 'center');
      }}
    >
      {label}
    </button>
  );
}
```

## Hoặc cài đặt nó như một component shadcn

Registry item [scroll-tabs](https://react-horizontal-scrolling-menu.dev/r/scroll-tabs.json) cung cấp mẫu hình này theo hướng dữ liệu — truyền `tabs`, `value`, `onValueChange` — dưới dạng component có thể chỉnh sửa trong `components/ui/` của bạn:

shadcn

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

## Các ví dụ liên quan

-   [Căn giữa mục được bấmscrollToItem với inline: center — pattern tab cuộn được.](https://react-horizontal-scrolling-menu.dev/vi/examples/center-on-click.md)
-   [Lưu và khôi phục vị trí cuộnGiữ độ lệch cuộn qua các lần unmount và tải lại trang.](https://react-horizontal-scrolling-menu.dev/vi/examples/save-restore-position.md)
-   [Kéo để cuộn bằng chuộtKéo chuột mà vẫn để các cú bấm mục hoạt động.](https://react-horizontal-scrolling-menu.dev/vi/examples/mouse-drag.md)

[Tất cả 21 ví dụ](https://react-horizontal-scrolling-menu.dev/vi/examples.md)

---

More examples: <https://react-horizontal-scrolling-menu.dev/examples.md>
Library summary for LLMs: <https://react-horizontal-scrolling-menu.dev/llms.txt>
