rc-component menu is the Ant Design menu primitive, unpackaged
🧭 Accessible React menu primitives for navigation, command surfaces, and nested item trees.
At a glance
- What is it?
- @rc-component/menu is the headless React menu that Ant Design builds its own Menu on, offering horizontal, vertical and inline modes, controlled selection and open keys, and an items configuration API. It suits engineers who want Ant Design's menu behaviour without Ant Design's styling, and it is a low level component with no visual design of its own.
- Who is it for?
- Adopt @rc-component/menu when you want the menu behaviour Ant Design ships with, in your own design system, and reach for Ant Design's Menu instead if you also want the styling and the rest of the component set. Verify first which props you actually need controlled, because the API splits cleanly between defaultSelectedKeys and selectedKeys, defaultOpenKeys and openKeys, and mixing the two halves is where the bugs live.
- 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 46 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
This is the layer underneath Ant Design's own Menu
The highlights list ends with the sentence that tells you what this package is: it is used by Ant Design as the shared menu foundation. The README also states it is part of the Ant Design ecosystem. So when you use the Menu component in Ant Design, this is the code doing the work, and the Ant Design component is a styled layer over it. That positioning explains both the design and the limitations. The package draws no icons of its own, sets no colours, and ships only a minimal stylesheet, and the API table is full of behaviour knobs with no visual counterpart. It is also why the props look the way they do. Things like builtinPlacements, getPopupContainer, popupRender, subMenuOpenDelay and subMenuCloseDelay are exposed because a design system needs to place its own popups and its own overlays, and the primitive has to let it. The name changed to @rc-component/menu on npm while the repository is still react-component/menu, which is a naming inconsistency worth remembering when you are reading issues.
Three display modes, and the difference is not cosmetic
The mode prop accepts 'horizontal', 'vertical' or 'inline', with vertical as the default. The three behave differently in ways that affect interaction, not just appearance. A vertical menu is a stack of items, and a horizontal menu lays items out across the top, which means a sub menu in horizontal mode opens as a popup below the parent rather than expanding in place. Inline mode is the collapsible sidebar pattern, where sub menus expand in place within the menu column, and that is what the inlineCollapsed and inlineIndent props exist for, with the default indent width of 24. The triggerSubMenuAction prop decides whether a sub menu opens on hover, which is the default, or on click. The delay props subMenuOpenDelay and subMenuCloseDelay, both 0.1 seconds, exist so that a menu can avoid flickering while the pointer travels across items. Because the mode changes how sub menus are presented, changing it is not a cosmetic refactor, it changes the component's event and focus behaviour.
Controlled and uncontrolled state are two separate API halves
The state model is the part that will bite you if you do not read it. There is a controlled and an uncontrolled variant for each of the three state groups, and the controlled version wins when it is present. Selection: selectedKeys is the controlled list of selected item keys and defaultSelectedKeys is the initial one, with selectable defaulting to true and multiple defaulting to false, which means single selection unless you opt into multiple. Open state: openKeys is the controlled list of open sub menu keys and defaultOpenKeys is the initial one. Active state: activeKey is the controlled active item key, and defaultActiveFirst, false by default, decides whether the first enabled item gets focus when there is no active key. The onSelect, onDeselect, onClick and onOpenChange callbacks report changes. The practical rule is that a menu in a React application almost always wants the controlled form, because the parent usually needs to know what is selected to route, and mixing the two forms leaves you with a component that will not update from props.
The items API replaced children, but the old form still works
Configuration comes from an items prop that takes an ItemType array, and the README notes legacy children support alongside it, so MenuItem and SubMenu children remain usable. The items form is the direction of travel because it can express structure the children form could not, and the ItemType union is now the contract: an item variant with a key, optional label, disabled flag, itemIcon and extra content; a submenu variant with a string key, a label and children; a group variant with a label and optional children; and a divider variant with no fields. A minimal items configuration looks like this:
const items: MenuProps['items'] = [
{ key: 'home', label: 'Home' },
{
key: 'docs',
label: 'Docs',
children: [
{ key: 'quick-start', label: 'Quick start' },
{ key: 'api', label: 'API' },
],
},
];Two details in the type comments are easy to miss. Item keys accept React.Key and are normalized to strings in event info, so a numeric key comes back as a string in the event payload. Sub menu keys are strings specifically so they match openKeys and defaultOpenKeys, which are string arrays. That asymmetry is a small thing that will cost an afternoon if you rely on numeric sub menu keys.
Popups, overflow and the slots that let you restyle it
Three mechanisms handle the parts of a menu that a simple list cannot. Sub menus in vertical and horizontal modes open in a popup layer, and you control where that layer mounts with getPopupContainer, what the alignment options are through builtinPlacements, and what the popup contains through popupRender. forceSubMenuRender lets you render popup sub menus before they are opened, which matters when you need the DOM to exist for measurement. Separately, when a menu is too wide, items collapse into an overflow sub menu, drawn with the overflowedIndicator, which defaults to an ellipsis, and disabledOverflow turns that measurement off. For styling, the package exposes semantic classNames and styles slots for 'list' and 'listTitle', plus prefixCls which defaults to rc-menu and rootClassName for the root wrapper. Semantic slots are the reason this package is usable outside Ant Design: you can target specific structural parts without writing fragile descendant selectors against markup you did not write.
Keyboard focus is handled through a small ref API
Accessibility here is less about ARIA attributes and more about focus management, and the ref surface shows it. The focus method, which takes optional FocusOptions, focuses the active item or the first enabled item. The findItem method takes a key and returns the DOM element for that item or null, which is how you can drive focus programmatically to a specific entry. The list property exposes the root menu list element, an HTMLUListElement. These are the hooks a keyboard shortcut system needs, for example a command palette style surface where an arrow key or a type-ahead should move the active item, and they are the reason the package describes itself as accessible React menu primitives for navigation, command surfaces and nested item trees. The direction prop takes 'ltr' or 'rtl' for layout direction, and the disabled prop disables all menu interactions at once rather than per item. One caveat to note: the README does not spell out a full keyboard interaction table, so if you need a documented statement of which key does what, you should check the demo site.
The dependency set is small and specific
Installing it is a single command:
npm install @rc-component/menuThe package.json shows five runtime dependencies, and each one maps to a piece of behaviour rather than being incidental. @rc-component/motion provides the CSS motion used for the sub menu open and close transitions, configured through the motion and defaultMotions props where defaultMotions lets you set motion config per menu mode. @rc-component/overflow handles the overflow measurement that decides when items collapse. @rc-component/trigger is the popup positioning layer that the sub menu popups are built on. @rc-component/util provides the shared helpers. clsx handles conditional class name assembly. The build tooling is father, with .fatherrc.js and .fatherrc.ts, and the package publishes both a CommonJS entry at ./lib/index and an ES module entry at ./es/index, along with the compiled CSS and Less assets. The test runner is rc-test with jest.config.js present, and the type check is tsc with a separate tsc script, and eslint is configured through eslint.config.mjs.
Where this package is the wrong choice
Two situations argue against it. The first is a team that simply wants a menu that looks right. Ant Design's Menu already wraps this package with the styling, the theme tokens, the icon handling and the design decisions, and re-implementing those on top of the primitive is exactly the work the primitive exists to let you avoid, unless you are a design system team. The second is a project that does not want a headless component at all. There is no visual output here, so a small application that needs one dropdown in one style is carrying a dependency tree and a props surface for very little. A further limitation to plan around is the name mismatch. The npm package is @rc-component/menu and the import path is that, while the repository, the homepage and the older documentation still refer to react-component/menu, so a search for the project will lead you to a mix of both. The version numbering also tells you where this sits in its life. The repository is not archived and the last push was on 2026-09-07, with releases 1.6.0 on 2026-09-01, 1.5.0 on 2026-08-19 and 1.4.1 on 2026-06-03, and the licence is MIT, so there is no cost to depending on it, but there is also no compatibility promise attached to a major version, since 1.x is still 1.x.
Editorial conclusion
Adopt @rc-component/menu when you want the menu behaviour Ant Design ships with, in your own design system, and reach for Ant Design's Menu instead if you also want the styling and the rest of the component set. Verify first which props you actually need controlled, because the API splits cleanly between defaultSelectedKeys and selectedKeys, defaultOpenKeys and openKeys, and mixing the two halves is where the bugs live. Pin the version you depend on, since the package is published as @rc-component/menu while the repository and import path still use the older react-component name.
Frequently asked questions
How do I install @rc-component/menu?
The documented install is npm install @rc-component/menu. The package is published under the @rc-component scope even though the repository is still named react-component/menu.
What is the difference between defaultSelectedKeys and selectedKeys?
defaultSelectedKeys is the initial uncontrolled value and selectedKeys is the controlled list of selected item keys, and the controlled form wins when it is present. The same split applies to defaultOpenKeys versus openKeys, and to defaultActiveFirst versus activeKey for the active item.
What is the difference between vertical, horizontal and inline modes?
The mode prop accepts 'horizontal', 'vertical' or 'inline', with vertical as the default. Inline mode expands sub menus in place and supports inlineCollapsed and inlineIndent, the latter defaulting to 24, while vertical and horizontal modes open sub menus as popups.
Can I still use MenuItem and SubMenu children?
Yes. The highlights note that the items configuration API comes with legacy children support, so the MenuItem and SubMenu children form remains usable alongside the items array.
How do I style the menu without Ant Design?
The package exposes semantic classNames and styles slots for 'list' and 'listTitle', plus prefixCls which defaults to rc-menu and rootClassName for the root wrapper, so you can target structural parts without relying on descendant selectors.
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-menu)Was this useful?