Options
All
  • Public
  • Public/Protected
  • All
Menu

React Headless Nested Menu Logo

React Headless Nested Menu

A useful headless component (hook) that gives you all the functions you need to create a multi-level menu using your own components!

Features

  • Only functionality, no need to fight with CSS classes and overrides to customize your menu.
  • Created in TypeScript, so you get types out of the box.
  • Fully configurable behavior (open on click or hover).

React Headless Nested Menu Logo

Installation

yarn add react-headless-nested-menu

Usage

You can import the generated bundle to use the whole library generated by this starter:

import React from "react"; import { useNestedMenu } from "react-headless-nested-menu"; function App() { const { getToggleButtonProps, getMenuProps, getItemProps, getOpenTriggerProps, getCloseTriggerProps, getMenuOffsetStyles, isOpen, isSubMenuOpen, toggleMenu } = useNestedMenu({ items }); const [item, setItem] = useState<MenuItem>(); // your custom function to render items const renderItem = (item: MenuItem) => ( <div {...getItemProps(item)} className="relative my-1 first:mt-0 last:mb-0" {...getOpenTriggerProps("onPointerEnter", item)} onClick={(event) => { event.stopPropagation(); setItem(item); toggleMenu(); }} > <div className={classnames( "flex flex-row justify-between items-center rounded-lg flex-1 h-8 flex items-center px-2", { "text-gray-600 hover:text-gray-800 hover:bg-gray-200": !isSubMenuOpen( item ), "text-gray-800 bg-gray-200": isSubMenuOpen(item) } )} > {item.label} {item.subMenu && <Chevron />} </div> {/* Only show submenu when there's a submenu & it's open */} {item.subMenu && isSubMenuOpen(item) && renderMenu(item.subMenu, item)} </div> ); // your custom function to render menus (root menu & sub-menus) const renderMenu = (items: Items, parentItem?: MenuItem) => ( <div {...getMenuProps(parentItem)} style={{ position: "absolute", ...getMenuOffsetStyles(parentItem) }} className={classnames( "bg-white p-2 shadow-lg rounded-lg select-none border border-gray-100 relative z-10", { "ms-2": typeof parentItem === "undefined", //for root menu "-mt-3": typeof parentItem !== "undefined" //for submenus only } )} {...getCloseTriggerProps("onPointerLeave", parentItem)} > <div>{items.map((item) => renderItem(item))}</div> {/* add hit area */} {parentItem && ( <div style={{ position: "absolute", top: -8, bottom: -8, left: -8, right: -8, zIndex: -1 }} ></div> )} </div> ); return ( <div className="w-64 p-4 rounded-lg flex flex-col ms-4 mt-4"> <button className="text-gray-600 border-2 border-gray-600 rounded-lg h-10 focus:outline-none" {...getToggleButtonProps()} > {item ? item.label : "Open Menu"} </button> {isOpen && renderMenu(items)} </div> ); } const rootElement = document.getElementById("root"); React.render(<App />, rootElement);

To do

  • Improve documentation.
  • Add more example.
  • Add tests.
  • Use popper for positioning menus.

Examples

Credits

Index

Other Interfaces

Types Interfaces

Type aliases

Hooks Functions

Type aliases

Action

Action: ToggleAction | OpenPathAction | ClosePathAction

Items

Items: MenuItem[]

Hooks Functions

Const useNestedMenu

  • useNestedMenu(__namedParameters: { defaultOpenPath: string[]; isOpen: boolean; items: Items }): { closePath: closePath; currentPath: ReadonlyArray<string>; getCloseTriggerProps: getCloseTriggerProps; getItemPath: getItemPath; getItemProps: getItemProps; getMenuOffsetStyles: getMenuOffsetStyles; getMenuProps: getMenuProps; getOpenTriggerProps: getOpenTriggerProps; getToggleButtonProps: getToggleButtonProps; getToggleTriggerProps: getToggleTriggerProps; isOpen: boolean; isSubMenuOpen: isSubMenuOpen; openPath: openPath; toggleMenu: toggleMenu }
  • Parameters

    • __namedParameters: { defaultOpenPath: string[]; isOpen: boolean; items: Items }
      • defaultOpenPath: string[]
      • isOpen: boolean
      • items: Items

    Returns { closePath: closePath; currentPath: ReadonlyArray<string>; getCloseTriggerProps: getCloseTriggerProps; getItemPath: getItemPath; getItemProps: getItemProps; getMenuOffsetStyles: getMenuOffsetStyles; getMenuProps: getMenuProps; getOpenTriggerProps: getOpenTriggerProps; getToggleButtonProps: getToggleButtonProps; getToggleTriggerProps: getToggleTriggerProps; isOpen: boolean; isSubMenuOpen: isSubMenuOpen; openPath: openPath; toggleMenu: toggleMenu }

    • closePath: closePath
    • currentPath: ReadonlyArray<string>
    • getCloseTriggerProps: getCloseTriggerProps
    • getItemPath: getItemPath
    • getItemProps: getItemProps
    • getMenuOffsetStyles: getMenuOffsetStyles
    • getMenuProps: getMenuProps
    • getOpenTriggerProps: getOpenTriggerProps
    • getToggleButtonProps: getToggleButtonProps
    • getToggleTriggerProps: getToggleTriggerProps
    • isOpen: boolean
    • isSubMenuOpen: isSubMenuOpen
    • openPath: openPath
    • toggleMenu: toggleMenu

Legend

  • Property

Generated using TypeDoc