react ~9 KB 0 deps v0.4.0 ↗ GitHub ↗

@mshafiqyajid/react-dropdown-menu

Headless dropdown menu hook and styled component. Items with icons and dividers, full placement set with -start/-end alignment on all four sides, flip + shift + offset + collisionPadding, full keyboard navigation, portal positioning, ARIA-compliant.

Playground #

Props
TSX
import { DropdownMenuStyled } from "@mshafiqyajid/react-dropdown-menu/styled";
import "@mshafiqyajid/react-dropdown-menu/styles.css";

<DropdownMenuStyled
  items={items}
/>

Install #

npm install @mshafiqyajid/react-dropdown-menu

Quick start #

import { DropdownMenuStyled } from "@mshafiqyajid/react-dropdown-menu/styled";
import "@mshafiqyajid/react-dropdown-menu/styles.css";

<DropdownMenuStyled
  trigger={<button>Actions ▾</button>}
  items={[
    { label: "Edit",    onClick: () => handleEdit() },
    { label: "Copy",    onClick: () => handleCopy() },
    { label: "", divider: true },
    { label: "Delete",  onClick: () => handleDelete() },
  ]}
/>
<DropdownMenuStyled
  trigger={<button>File</button>}
  items={[
    { label: "New", onClick: () => create() },
    {
      label: "Open recent",
      items: [
        { label: "report.pdf", onClick: () => open("report.pdf") },
        { label: "notes.md",   onClick: () => open("notes.md") },
        { divider: true },
        { label: "Clear",      onClick: () => clearRecents() },
      ],
    },
    { divider: true },
    { label: "Quit", onClick: () => quit() },
  ]}
/>

Parent rows render a chevron and a hover/click flyout. Keyboard: opens, returns, Enter/Space activates. Submenus inherit positioning props from the parent menu.

Headless #

import { useDropdownMenu } from "@mshafiqyajid/react-dropdown-menu";

const items = ["Edit", "Duplicate", "Delete"];
const { triggerProps, menuProps, getItemProps, isOpen } = useDropdownMenu({
  itemCount: items.length,
});

return (
  <>
    <button {...triggerProps}>Actions</button>
    {isOpen && (
      <ul {...menuProps} className="menu">
        {items.map((label, i) => (
          <li key={label} {...getItemProps(i)}>{label}</li>
        ))}
      </ul>
    )}
  </>
);

API #

PropTypeDefaultDescription
triggerReactElementThe trigger element
itemsDropdownMenuItem[]Array of { label, onClick?, icon?, disabled?, divider? }
placementtop | bottom | left | right (each with optional -start / -end)"bottom-start"Menu side and alignment
size"sm" | "md" | "lg""md"Item size
offsetnumber4Gap between trigger and menu
collisionPaddingnumber8Viewport edge margin for flip / shift
flipbooleantrueAuto-flip to opposite side near edges
shiftbooleantruePush back into view along the cross-axis
strategy"absolute" | "fixed""absolute"Positioning strategy
Edit this page on GitHub