@szhsin/react-menu

repository·master·Indexed 22 days ago

https://github.com/szhsin/react-menu

A lightweight, unstyled React library for building accessible and keyboard-friendly menu components, including dropdowns, hover menus, and context menus. It implements the WAI-ARIA menu pattern and supports unlimited submenu nesting, radio and checkbox items, and server-side rendering (SSR). The library provides core components such as Menu, MenuItem, MenuButton, SubMenu, and ControlledMenu, as well as hooks like useMenuState for state management.

Tokens
3.6K
Snippets
12
Records
20
Agent score
75%

What's inside @szhsin/react-menu

  1. Overview of React-Menu features

    master

    React-Menu is an accessible, unstyled React menu library with the following capabilities:

    • Menu Types: Supports dropdown, hover, and context menus.
    • Menu Items: Includes support for radio and checkbox menu items.
    • Nesting: Allows for unlimited submenu nesting.
    • Accessibility: Implements the WAI-ARIA menu pattern and provides full keyboard interaction support.
    • Compatibility: Compatible with React 18+ concurrent rendering and supports server-side rendering (SSR).
    • Styling: Lightweight and unstyled, allowing for complete custom styling.
  2. Server-side render a menu and its items

    master

    By default, a Menu and its items are not mounted into the DOM until the menu is opened for the first time. To ensure that a menu and all its descendants are rendered into the HTML string during server-side rendering (SSR), set the initialMounted prop to true.

    If you are using ControlledMenu with the useMenuState hook, pass initialMounted: true within the configuration object.

    <Menu initialMounted />
  3. Set attributes or event handlers on a submenu label

    master

    To apply additional HTML attributes (like className or style) or event handlers (like onClick) to the element rendering the submenu label, use the itemProps prop on the SubMenu component.

    <SubMenu
      itemProps={{
        className: 'submenu-label',
        style: { backgroundColor: 'lightblue' },
        onClick: () => console.log('Clicked!')
      }}
    />
  4. Use `gap` and `shift` instead of `offsetX` and `offsetY`

    master

    In v4, the offsetX and offsetY props have been replaced by gap and shift. These new props automatically account for the menu's placement direction, eliminating the need to manually calculate offsets based on whether the menu is appearing on the top, bottom, left, or right of the anchor.

    To create a 10-pixel gap between a menu and its anchor, use the gap prop instead of conditional offsetX/offsetY logic.

    <Menu gap={10} />
  5. Use `arrowProps` instead of `arrowClassName` and `arrowStyle`

    master

    In v4, the individual arrowClassName and arrowStyle props have been consolidated into a single arrowProps object. This new prop allows you to pass any attributes or event handlers directly onto the menu's arrow element.

    Instead of passing class names and styles as top-level props, wrap them in an object passed to arrowProps.

    <Menu
      arrowProps={{
        className: 'my-custom-arrow',
        style: { backgroundColor: 'blue' }
      }}
    />
  6. Add icons or images to a submenu label

    master

    The label prop of the SubMenu component is not limited to plain strings; it accepts any valid JSX element. This allows you to compose complex labels containing images, icons, or other React components.

    <SubMenu
      label={
        <>
          <img src="edit.png" alt="edit" />
          Edit
          <ArrowIcon />
        </>
      }
    />
  7. Handle ES6+ distribution for older environments

    master
    Starting with v4, React-Menu targets ES6+ to reduce bundle size. If your application must support older environments like IE 11, you must configure your bundler (e.g., Webpack with Babel) to transpile the @szhsin/react-menu package from node_modules into ES5 code.
  8. Basic usage of React-Menu

    master

    To create a menu, import the core components: Menu, MenuItem, MenuButton, and SubMenu. The Menu component requires a menuButton prop which accepts a component (like MenuButton) to trigger the menu. You can nest SubMenu components within a Menu to create unlimited levels of submenus.

    import { Menu, MenuItem, MenuButton, SubMenu } from '@szhsin/react-menu';
    
    export default function App() {
      return (
        <Menu menuButton={<MenuButton>Open menu</MenuButton>}>
          <MenuItem>New File</MenuItem>
          <MenuItem>Save</MenuItem>
          <SubMenu label="Edit">
            <MenuItem>Cut</MenuItem>
            <MenuItem>Copy</MenuItem>
            <MenuItem>Paste</MenuItem>
          </SubMenu>
          <MenuItem>Print...</MenuItem>
        </Menu>
      );
    }
  9. Use ControlledMenu for external state management

    master

    The ControlledMenu component allows you to manage the open/closed state of a menu externally. This is achieved by passing an anchorRef and a state object (which must contain an onClose function) via restProps. When the menu should close, the component calls onClose with a reason.

    Key Props

    PropTypeDescription
    anchorRefRefObjectA ref to the element that triggers the menu (the anchor).
    stateObjectAn object containing the current visibility state. If state is falsy, the menu renders nothing.
    onCloseFunctionA callback function called when the menu closes. It receives an object with { key, reason }.
    onItemClickFunctionA callback triggered when a menu item is clicked.
    portalboolean | ObjectIf true, renders the menu into document.body. If an object, can specify a target element.
    unmountOnClosebooleanWhether to unmount the menu when it is closed.
    reposition'auto' | stringControls how the menu repositions itself.
    submenuOpenDelaynumberDelay in milliseconds before opening a submenu.
    submenuCloseDelaynumberDelay in milliseconds before closing a submenu.
    transitionstring | ObjectConfiguration for menu transitions.
    transitionTimeoutnumberTimeout for transitions in milliseconds.
    themingObjectTheming configuration for the menu.
    containerPropsObjectProps to be passed to the menu container element.
    aria-labelstringAccessibility label for the menu.
    classNamestringCSS class for the menu container.
    boundingBoxRefRefObjectRef to a bounding box used for positioning calculations.
    boundingBoxPaddingnumberPadding applied to the bounding box.
    viewScrollstringControls how the menu handles scrolling (e.g., 'initial').
    initialMountedbooleanWhether the menu should be initially mounted.
  10. Use React-Menu hooks

    master

    React-Menu provides hooks for managing interaction and state. The following hooks are exported from the main entry point:

    • useClick: For handling click interactions.
    • useHover: For handling hover interactions.
    • useMenuState: For managing the internal state of a menu.
  11. Control the Menu imperatively via instanceRef

    master

    You can control a Menu instance programmatically by passing a ref to the instanceRef prop. This exposes methods to open and close the menu from your own logic.

    Exposed Methods

    • openMenu(position): Opens the menu.
    • closeMenu(): Closes the menu.
    const menuRef = useRef<{ openMenu: () => void; closeMenu: () => void }>(null);
    
    // ...
    
    <Menu 
      instanceRef={menuRef} 
      menuButton={<button>Open Menu</button>} 
    />
    
    // Later in your code:
    // menuRef.current?.openMenu();
    // menuRef.current?.closeMenu();
    useImperativeHandle(instanceRef, () => ({
        openMenu,
        closeMenu: () => toggleMenu(false)
    }));