Skip to main content

Menu

The Menu component is a fully headless and accessible dropdown menu primitive. It lets you build custom context menus, user dropdowns, and more — with full control over markup, behavior, and transitions.

Inspired by Headless UI Menu, but built for Angular.

When to use it

Use Menu when you need a dropdown menu that gives you full control over:

  • Markup and structure — no wrapper <ul> or forced elements
  • Styling — works perfectly with Tailwind, Bootstrap, or any custom design system
  • Behavior — full support for keyboard navigation, focus management, and accessibility
  • Logic — control opening/closing manually or through templates
  • Controlled via Angular templateRefs or Dependency Injection (DI)

Common use cases:

  • Profile dropdowns
  • Action menus (e.g. "Edit", "Duplicate", "Delete")
  • Filter or sort menus
  • Context menus (right-click or dot menus)

Anatomy

A complete Disclosure is composed of three components:

  • Menu - provides shared context for all children.
  • MenuButton - toggles the menu open/closed.
  • MenuItems - contains all the interactive options.
  • MenuItem - is an actionable item that emits selected when clicked

Each element is fully composable and unstyled — you control layout, transitions, icons, and interaction patterns.

Features

  • ✅ Accessible by default (ARIA attributes)
  • ✅ Tailwind-friendly (no styles imposed)
  • ✅ Multiple selectors supported:
    • <Menu>
    • <div ngxMenu>
    • <ngx-headlessui-menu>
  • ✅ Works with both #templateRefs and Angular DI (MenuContextService)
  • ✅ Supports multiple instances per page

Installation

Menu ships as part of the @ngx-headless/ui by default. Install if you haven't already.

npm install @ngx-headless/ui

Import the components directly:

import {
MenuComponent,
MenuButtonComponent,
MenuItemsComponent,
MenuItemComponent
} from "@ngx-headless/ui";

Usage Examples

Template Reference — Single Menu

<Menu #menuRef="ngxMenu">
<MenuButton [class.active]="menuRef.isOpen()">Toggle</MenuButton>
<MenuItems *ngIf="menuRef.isOpen()">
<MenuItem>...</MenuItem>
</MenuItems>
</Menu>

Template Reference — Multiple Menus (ngFor)

<Menu *ngFor="let action of actions" #menuRef="ngxMenu">
<MenuButton [class.active]="menuRef.isOpen()">Toggle</MenuButton>
<MenuItems *ngIf="menuRef.isOpen()">
<MenuItem *ngFor="let item of action.items" > {{item.label}} </MenuItem>
</MenuItems>
</Menu>

Access all instances in the component in your class Root:

...
export class MyComponent {
...

@ViewChildren('menuRef') menuInstances!: QueryList<MenuComponent>;
// ☝️ access to All Menus via #templateRefs

// 🧠 NOTE: You can also use @ViewChild with MenuComponent without QueryList<> to capture a single instance

...
}

Accessibility

  • Fully ARIA-compliant with roles: menu, menuitem, aria-haspopup, aria-expanded
  • Keyboard support (coming soon)
  • Screen reader-friendly structure

Animations

MenuItems can be animated freely using Angular’s built-in animation system.

Angular supports entry/exit transitions using @trigger bindings and the :enter/:leave lifecycle hooks.

👉 See Angular Animations Guide

Example

Add an animation trigger to your component:

import { trigger, transition, style, animate } from "@angular/animations";

@Component({
animations: [
trigger("menuAppear", [
transition(':enter', [
style({ opacity: 0, transform: 'scale(0.9)' }),
animate('100ms ease-out', style({ opacity: 1, transform: 'scale(1)' }))
]),
transition(':leave', [
style({ opacity: 1, transform: 'scale(1)' }),
animate('75ms ease-in', style({ opacity: 0, transform: 'scale(0.9)' }))
])
]),
],
})
export class ExampleComponent {}

Use it in the template with *ngIf:

<MenuItems *ngIf="disclosure.isOpen()" @menuAppear>
Panel content goes here.
</MenuItems>

✅ You now have full control over the entrance and exit behavior of the panel.
You can combine this with Tailwind transitions or any styling framework as needed.

Component API

InputTypeDescription
classstringClass applied to the wrapper
Host BindingDescription
role="menu"ARIA role
aria-orientation="vertical"Indicates vertical layout
aria-expandedReflects open state
BehaviorDescription
Auto closeCloses menu when clicking outside
InputTypeDescription
classstringClass applied to the button
Host BindingDescription
role="button"ARIA role
aria-haspopup="menu"Indicates menu relationship
aria-expandedReflects open state
tabindex="0"Makes the element focusable
BehaviorDescription
(click) toggles menuToggles menu open state
InputTypeDescription
classstringClass applied to the menu items container
Host BindingDescription
role="menu"ARIA role
hiddenHidden when Menu is closed
InputTypeDescription
classstringClass applied to the menu item
disabledbooleanPrevents interaction if true
OutputTypeDescription
selectedEventEmitter<void>Emits on item click (if enabled)
Host BindingDescription
role="menuitem"ARIA role
tabindex="-1"Makes item keyboard-accessible
aria-disabledReflects disabled state

See Also