@rc-component/tabs: the React tabs component behind Ant Design
📑 Flexible React tabs with overflow, editable items, custom tab bars, and accessible panels.
At a glance
- What is it?
- @rc-component/tabs is the headless-ish React tabs component that Ant Design's own Tabs is built on, with overflow handled by a dropdown, editable tabs, custom tab bars, four tab positions with RTL support and semantic classNames and styles slots. It suits a team that wants Ant Design's tab behaviour without adopting the whole design system, and it is not a drop-in for Ant Design's Tabs API.
- Who is it for?
- @rc-component/tabs fits a React team that wants tab behaviour with overflow, editing and custom rendering but does not want Ant Design's styling system, since the package ships the behaviour and the Less styles separately and exposes semantic classNames and styles slots for your own CSS.
- Can I use it commercially?
- Yes. MIT is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
- Is it still maintained?
- Yes. The repository last received commits 39 days ago.
- What is it written in?
- Mainly TypeScript, according to GitHub's language statistics.
Answers come from the project's GitHub data, last synced on October 23, 2026, and from our analysis. They are not legal advice.
Editorial analysis
The component underneath Ant Design's Tabs
@rc-component/tabs is described as flexible React tabs with overflow, editable items, custom tab bars and accessible panels, and it is part of the Ant Design ecosystem. That second fact is the most useful thing about it for anyone choosing a tabs library.
This is the standalone package that Ant Design's own Tabs is built on, and it is also the package behind several other rc-component libraries in the same family. The npm package is scoped as @rc-component/tabs, its homepage is react-component.github.io/tabs, and the author recorded in the manifest is yiminghe, who is also the author of rc-dialog and rc-menu in the same ecosystem.
The distinction matters because Ant Design's Tabs is a styled, opinionated component: it brings Ant Design's design tokens, its CSS-in-JS styling approach and its own prop conventions. @rc-component/tabs is the behaviour underneath, with the styles kept separate. The package manifest makes that visible: the published files are assets/*.css, lib and es, with main pointing at ./lib/index and module at ./es/index, so you get a CommonJS build, an ES module build and the stylesheet, and nothing else.
The consequences of that separation run through the whole API. There is no theme object and no token prop. Instead there is a prefixCls that defaults to 'rc-tabs', and there are classNames and styles props taking a Partial<Record<SemanticName, ...>>, which is the modern way to let a consumer restyle a component without overriding its internals with selectors.
So the choice between this and antd is a choice between owning the styling and inheriting it. Owning it means writing the CSS for tab positions, the ink bar indicator, the overflow dropdown trigger and the editable-tab add button yourself.
Overflow, editing and the tab bar as a replaceable part
The Highlights section lists four capabilities, and each of them corresponds to a specific piece of machinery.
Overflow is handled with a dropdown operation menu. When the tabs do not fit the available width, the ones that fall outside are moved into an overflow menu. The API for that menu is documented separately as MoreProps, and it accepts an icon for the more trigger plus a popupRender callback whose info argument carries restTabs, all the overflowed tabs, and an onClose function. Other dropdown props are passed through from rc-dropdown, including trigger and overlayClassName.
Editable tabs are supported, configured through the editable prop, and each Tab item can be closable or carry a custom closeIcon. The Tab-level props also include disabled and forceRender, the latter rendering a panel before it becomes active.
Custom tab bars are supported through renderTabBar, which is the escape hatch for projects that need a tab bar this component does not provide. There is also tabBarExtraContent for content beside the tabs, an indicator prop for the size and alignment of the ink bar, and tabBarGutter and tabBarStyle for spacing and styling.
Positions and direction are first-class. tabPosition accepts 'left', 'right', 'top' or 'bottom' and defaults to 'top', and direction accepts 'ltr' or 'rtl' and defaults to 'ltr'. The Highlights put it plainly: top, bottom, left and right tab positions with RTL layouts are supported. For a component library this is not a small feature, because a left tab bar has to rotate the indicator and reorder the keyboard navigation, and RTL has to flip the overflow direction.
Animation is also configurable rather than fixed. The animated prop takes a boolean or an AnimatedConfig, and it defaults to { inkBar: true, tabPane: false }, which means the ink bar moves by default but the panel content does not animate unless you ask for it.
Installation and a first render
The install is a single dependency:
npm install @rc-component/tabsThe usage example shows the shape of the API. You pass an items array where each item has a key, a label and children, and you choose the initially active tab with defaultActiveKey:
import Tabs from '@rc-component/tabs';
import '@rc-component/tabs/assets/index.css';
const items = [
{ key: 'overview', label: 'Overview', children: 'Overview content' },
{ key: 'settings', label: 'Settings', children: 'Settings content' },
];
export default () => <Tabs items={items} defaultActiveKey="overview" />;Two details in that snippet are the whole onboarding. The stylesheet is a separate import, which is the visible consequence of this being the unstyled component rather than the Ant Design one. And the items array carries the panel content inline, so a tab's content is defined next to its label rather than as a separate child element.
The API table in the readme confirms the controlled and uncontrolled pair: activeKey is the controlled active tab key and onChange fires when the active tab changes, while defaultActiveKey sets the initial one. There are also onTabClick, which reports the clicked key along with the event, and onTabScroll, which fires when the tab navigation scrolls.
A live preview is published at tabs.react-component.vercel.app. For the local site, the readme documents running the dumi site, which is the documentation tool in this ecosystem:
npm install
npm startand then opening http://localhost:8000. The dumi configuration lives in .dumirc.ts at the repository root and the deployment target is described in vercel.json.
Semantic slots, a11y and the Dropdown 2 migration
Two areas of the API deserve a closer look before you commit to a version: the semantic styling hooks, and the breaking change in the overflow menu.
The semantic slots are classNames and styles, both typed as Partial<Record<SemanticName, string>> and Partial<Record<SemanticName, React.CSSProperties>> respectively, and the readme describes them as semantic class names and semantic styles for panel and navigation customization. The same pattern appears at the Tab level, where each panel has its own className and style. For a component that ships no theme, these are the only sanctioned way to restyle it, and the named slots mean you are not forced to reach into descendant selectors and hope the internals do not change.
Accessibility is handled through two props and one dependency. The locale prop takes TabsLocale and is described as accessibility locale text, which is how the ARIA labels for things like the more trigger get translated. getPopupContainer is a function taking a node and returning an HTMLElement, described as the popup container resolver, which is how you place the overflow dropdown inside a scrolling parent or a positioned container.
The breaking change is in the more dropdown. The readme states plainly that Dropdown 2 uses more.open and more.onOpenChange, and that the previous more.visible and more.onVisibleChange props are no longer supported and must be migrated when upgrading. That is a hard cut, not a deprecation, and it is the single thing to check before pinning a version.
There is also a destroyOnHidden prop on both the Tabs root and the individual Tab, defaulting to false, which destroys inactive panels rather than keeping them mounted. That is the prop to reach for when a panel holds a video element or a subscription, and the fact that it exists at both levels means you can unmount one heavy panel without unmounting all of them.
How the package is built and released
The manifest describes a toolchain that is consistent across the rc-component family, and it explains a few things about the shape of the package.
The build is father, the rc-component build tool, and the compile script runs father build followed by a Less compile step. The stylesheet is therefore generated from a Less source rather than authored as plain CSS, which is worth knowing if you want to customise it: the shipped assets/index.css is a build artefact, and changing it means either overriding with the semantic slots or rebuilding Less yourself.
There are two output formats, lib for CommonJS and es for ES modules, and the stylesheet is shipped alongside them. The package is side-effect free in the sense that importing the JavaScript does not require the stylesheet to be loaded, which is why the usage example has to import it explicitly.
Testing runs through rc-test, the shared test runner, with a coverage script that adds --coverage, and jest.config.js at the root. The dev dependencies show @testing-library/react, @testing-library/jest-dom and @testing-library/dom, so the tests are written against the testing library rather than enzyme, and the type check is tsc --noEmit. Linting is eslint over src/ and docs/examples/, and there is a husky prepare hook plus a .husky/ directory and .prettierrc.
Release is handled by @rc-component/np through the rc-np command, invoked from prepublishOnly after the compile step. The README documents the release step as npm run prepublishOnly, and the description says the release flow is handled after the package build.
The recent releases show a renaming in progress. The published versions are @rc-component/[email protected] from 2026-09-01, then 2.0.0 on 2026-09-09 and 2.1.0 on 2026-09-11, with the manifest version at 2.1.0. The major version bump is consistent with the Dropdown 2 prop change, and the scoped name with the rc-component organisation reflects the same move that the ant-design scoped packages made.
Where @rc-component/tabs is the wrong choice
The first limitation is the styling work. This package gives you behaviour and a generated stylesheet, and if you want a tab bar that matches your design system, you are writing the CSS. Ant Design's Tabs gives you a token-driven version of that, and the rc-tabs prefix class and semantic slots are a workable path but they are work. A team that would rather spend its time on features than on tab chrome should use antd.
The second is the dependency footprint of a family library. The runtime dependencies are @rc-component/dropdown, @rc-component/menu, @rc-component/motion, @rc-component/resize-observer, @rc-component/util and clsx. That is a real dependency tree for what looks like a simple component, and each of those carries its own version constraints. The dropdown and menu in particular are where the overflow menu lives, which is why a change in their behaviour shows up here as a breaking change in more props.
The third is the Dropdown 2 migration. more.visible and more.onVisibleChange are gone, and the replacement is more.open with more.onOpenChange. Any code that controlled the overflow menu directly has to change, and the readme says so without offering a compatibility path.
The fourth is the version churn. Three releases in ten days in September 2026, with a major version bump inside that window, and the package name itself has changed from the older rc-tabs convention to the @rc-component scope. If your project pins dependencies strictly, this is a package whose pin will need revisiting after a major.
The fifth is that the component does very little on its own. There is no built-in loading state, no lazy panel loading strategy, no keyboard shortcut handling documented in the readme, no form integration and no router integration. The API table is a list of styling and layout props plus a handful of events, and that is the whole contract. Anything beyond that, a tab strip that persists across routes, a tab set driven by the URL, is application code you write.
Editorial conclusion
@rc-component/tabs fits a React team that wants tab behaviour with overflow, editing and custom rendering but does not want Ant Design's styling system, since the package ships the behaviour and the Less styles separately and exposes semantic classNames and styles slots for your own CSS. It is the wrong choice if you are already using antd, because antd's own Tabs is this component and brings the design system with it, and it is the wrong choice if you cannot absorb the Dropdown 2 migration, because more.visible and more.onVisibleChange are no longer supported. Before adopting it, check the version you pin against the Dropdown 2 change, note that the last push was on 2026-09-14 with 2.1.0 published on 2026-09-11, and plan for the styling work yourself, since the shipped stylesheet is built from Less sources.
Frequently asked questions
How do I install @rc-component/tabs?
Run npm install @rc-component/tabs. The stylesheet is a separate import, so you also add import '@rc-component/tabs/assets/index.css'; alongside the component import.
What is the difference between @rc-component/tabs and Ant Design's Tabs?
Ant Design's Tabs is built on this package and adds Ant Design's design tokens and styling, while @rc-component/tabs ships the behaviour with a separately imported stylesheet and a prefixCls of rc-tabs. The package publishes assets/*.css, lib and es, with main at ./lib/index and module at ./es/index.
How does @rc-component/tabs handle tabs that do not fit?
Overflowing tabs move into a dropdown operation menu configured through the more prop. MoreProps accepts an icon and a popupRender callback whose info argument provides restTabs, all overflowed tabs, and an onClose function, and other rc-dropdown props are passed through.
What changed in version 2.0.0 of @rc-component/tabs?
The readme states that Dropdown 2 uses more.open and more.onOpenChange, and that the previous more.visible and more.onVisibleChange props are no longer supported and must be migrated when upgrading. The releases go from 1.14.0 on 2026-09-01 to 2.0.0 on 2026-09-09 and 2.1.0 on 2026-09-11.
What licence is @rc-component/tabs released under?
MIT, with the licence stated in the readme and the LICENSE file at the root of the repository. The homepage is react-component.github.io/tabs and the last push to the repository was on 2026-09-14.
Official sources
Add this badge to your README
If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.
[](https://hysenlabs.com/projects/react-component-tabs)Was this useful?