navigation menu

A collection of links for navigating websites.

PreviousNext
  • Docs
import { ChangeDetectionStrategy, Component } from '@angular/core';

import { NgIcon, provideIcons } from '@ng-icons/core';
import { lucideCircleAlert, lucideCircleCheck, lucideCircleDashed } from '@ng-icons/lucide';

import { navigationMenuTriggerVariants, ZardNavigationMenuImports } from '@/shared/components/navigation-menu';

interface ComponentLink {
  title: string;
  href: string;
  description: string;
}

@Component({
  selector: 'z-demo-navigation-menu-preview',
  imports: [ZardNavigationMenuImports, NgIcon],
  template: `
    <z-navigation-menu>
      <ul z-navigation-menu-list>
        <li z-navigation-menu-item>
          <button type="button" z-navigation-menu-trigger [zNavigationMenuTriggerFor]="gettingStarted">
            Getting started
          </button>

          <ng-template #gettingStarted>
            <div z-navigation-menu-content>
              <ul class="w-96">
                <li>
                  <a z-navigation-menu-link href="#">
                    <div class="flex flex-col gap-1 text-sm">
                      <div class="leading-none font-medium">Introduction</div>
                      <div class="text-muted-foreground line-clamp-2">
                        Re-usable components built with Tailwind CSS.
                      </div>
                    </div>
                  </a>
                </li>
                <li>
                  <a z-navigation-menu-link href="#">
                    <div class="flex flex-col gap-1 text-sm">
                      <div class="leading-none font-medium">Installation</div>
                      <div class="text-muted-foreground line-clamp-2">
                        How to install dependencies and structure your app.
                      </div>
                    </div>
                  </a>
                </li>
                <li>
                  <a z-navigation-menu-link href="#">
                    <div class="flex flex-col gap-1 text-sm">
                      <div class="leading-none font-medium">Typography</div>
                      <div class="text-muted-foreground line-clamp-2">Styles for headings, paragraphs, lists...etc</div>
                    </div>
                  </a>
                </li>
              </ul>
            </div>
          </ng-template>
        </li>

        <li z-navigation-menu-item class="hidden md:flex">
          <button type="button" z-navigation-menu-trigger [zNavigationMenuTriggerFor]="componentsMenu">
            Components
          </button>

          <ng-template #componentsMenu>
            <div z-navigation-menu-content>
              <ul class="grid w-[400px] gap-2 md:w-[500px] md:grid-cols-2 lg:w-[600px]">
                @for (component of components; track component.title) {
                  <li>
                    <a z-navigation-menu-link href="#">
                      <div class="flex flex-col gap-1 text-sm">
                        <div class="leading-none font-medium">{{ component.title }}</div>
                        <div class="text-muted-foreground line-clamp-2">{{ component.description }}</div>
                      </div>
                    </a>
                  </li>
                }
              </ul>
            </div>
          </ng-template>
        </li>

        <li z-navigation-menu-item>
          <button type="button" z-navigation-menu-trigger [zNavigationMenuTriggerFor]="withIcon">With Icon</button>

          <ng-template #withIcon>
            <div z-navigation-menu-content>
              <ul class="grid w-[200px]">
                <li>
                  <a z-navigation-menu-link href="#" class="flex-row items-center gap-2">
                    <ng-icon name="lucideCircleAlert" />
                    Backlog
                  </a>
                  <a z-navigation-menu-link href="#" class="flex-row items-center gap-2">
                    <ng-icon name="lucideCircleDashed" />
                    To Do
                  </a>
                  <a z-navigation-menu-link href="#" class="flex-row items-center gap-2">
                    <ng-icon name="lucideCircleCheck" />
                    Done
                  </a>
                </li>
              </ul>
            </div>
          </ng-template>
        </li>

        <li z-navigation-menu-item>
          <a z-navigation-menu-link href="#" [class]="triggerClass">Docs</a>
        </li>
      </ul>
    </z-navigation-menu>
  `,
  changeDetection: ChangeDetectionStrategy.OnPush,
  viewProviders: [provideIcons({ lucideCircleAlert, lucideCircleCheck, lucideCircleDashed })],
})
export class ZardDemoNavigationMenuPreviewComponent {
  /** A plain link styled like a trigger, so it lines up with the rest of the bar. */
  protected readonly triggerClass = navigationMenuTriggerVariants();

  protected readonly components: ComponentLink[] = [
    {
      title: 'Alert Dialog',
      href: '/docs/primitives/alert-dialog',
      description: 'A modal dialog that interrupts the user with important content and expects a response.',
    },
    {
      title: 'Hover Card',
      href: '/docs/primitives/hover-card',
      description: 'For sighted users to preview content available behind a link.',
    },
    {
      title: 'Progress',
      href: '/docs/primitives/progress',
      description:
        'Displays an indicator showing the completion progress of a task, typically displayed as a progress bar.',
    },
    {
      title: 'Scroll-area',
      href: '/docs/primitives/scroll-area',
      description: 'Visually or semantically separates content.',
    },
    {
      title: 'Tabs',
      href: '/docs/primitives/tabs',
      description: 'A set of layered sections of content—known as tab panels—that are displayed one at a time.',
    },
    {
      title: 'Tooltip',
      href: '/docs/primitives/tooltip',
      description:
        'A popup that displays information related to an element when the element receives keyboard focus or the mouse hovers over it.',
    },
  ];
}

About

The navigation menu is built on top of the Angular CDK Menu. Angular CDK Menu

Installation

Copy
npx zard-cli@latest add navigation-menu

Usage

import { ZardNavigationMenuImports } from '@/shared/components/navigation-menu/navigation-menu.imports';
Copy
<z-navigation-menu>
  <ul z-navigation-menu-list>
    <li z-navigation-menu-item>
      <button type="button" z-navigation-menu-trigger [zNavigationMenuTriggerFor]="gettingStarted">
        Getting started
      </button>

      <ng-template #gettingStarted>
        <div z-navigation-menu-content>
          <ul class="w-64">
            <li><a z-navigation-menu-link href="#">Introduction</a></li>
            <li><a z-navigation-menu-link href="#">Installation</a></li>
          </ul>
        </div>
      </ng-template>
    </li>
  </ul>
</z-navigation-menu>
Copy

Composition

Use the following composition to build a navigation-menu:

z-navigation-menu
├── ul[z-navigation-menu-list]
│   └── li[z-navigation-menu-item]
│       ├── button[z-navigation-menu-trigger]
│       └── ng-template
│           └── div[z-navigation-menu-content]
│               ├── a[z-navigation-menu-link]
│               └── a[z-navigation-menu-link]
└── z-navigation-menu-indicator
Copy

Examples

link

Use routerLink on the [z-navigation-menu-link] element to compose with the Angular Router.
import { ChangeDetectionStrategy, Component } from '@angular/core';
import { RouterLink, RouterLinkActive } from '@angular/router';

import { navigationMenuTriggerVariants, ZardNavigationMenuImports } from '@/shared/components/navigation-menu';

@Component({
  selector: 'z-demo-navigation-menu-link',
  imports: [ZardNavigationMenuImports, RouterLink, RouterLinkActive],
  template: `
    <z-navigation-menu>
      <ul z-navigation-menu-list>
        <li z-navigation-menu-item>
          <a
            z-navigation-menu-link
            routerLink="/docs/components/navigation-menu"
            routerLinkActive
            #navigationMenuLink="routerLinkActive"
            [zActive]="navigationMenuLink.isActive"
            [class]="triggerClass"
          >
            Navigation Menu
          </a>
        </li>
        <li z-navigation-menu-item>
          <a
            z-navigation-menu-link
            routerLink="/docs/components/dropdown"
            routerLinkActive
            #dropdownLink="routerLinkActive"
            [zActive]="dropdownLink.isActive"
            [class]="triggerClass"
          >
            Dropdown
          </a>
        </li>
        <li z-navigation-menu-item>
          <a
            z-navigation-menu-link
            routerLink="/docs/components/tabs"
            routerLinkActive
            #tabsLink="routerLinkActive"
            [zActive]="tabsLink.isActive"
            [class]="triggerClass"
          >
            Tabs
          </a>
        </li>
      </ul>
    </z-navigation-menu>
  `,
  changeDetection: ChangeDetectionStrategy.OnPush,
})
export class ZardDemoNavigationMenuLinkComponent {
  protected readonly triggerClass = navigationMenuTriggerVariants();
}

simple

A bar of plain links, with no dropdown. Reuse navigationMenuTriggerVariants() to keep the height and spacing of a trigger.
import { ChangeDetectionStrategy, Component } from '@angular/core';

import { navigationMenuTriggerVariants, ZardNavigationMenuImports } from '@/shared/components/navigation-menu';

@Component({
  selector: 'z-demo-navigation-menu-simple',
  imports: [ZardNavigationMenuImports],
  template: `
    <z-navigation-menu>
      <ul z-navigation-menu-list>
        <li z-navigation-menu-item>
          <a z-navigation-menu-link href="#" zActive [class]="triggerClass">Overview</a>
        </li>
        <li z-navigation-menu-item>
          <a z-navigation-menu-link href="#" [class]="triggerClass">Documentation</a>
        </li>
        <li z-navigation-menu-item>
          <a z-navigation-menu-link href="#" [class]="triggerClass">Blocks</a>
        </li>
        <li z-navigation-menu-item>
          <a z-navigation-menu-link href="#" [class]="triggerClass">Changelog</a>
        </li>
      </ul>
    </z-navigation-menu>
  `,
  changeDetection: ChangeDetectionStrategy.OnPush,
})
export class ZardDemoNavigationMenuSimpleComponent {
  /** Reuses the trigger CVA so plain links keep the height and spacing of the bar. */
  protected readonly triggerClass = navigationMenuTriggerVariants();
}

no viewport

Set [zViewport]="false" so each item opens its own popup, with its own background and ring, instead of sharing the animated viewport. The markup is the same in both modes — only the input changes.
import { ChangeDetectionStrategy, Component } from '@angular/core';

import { ZardNavigationMenuImports } from '@/shared/components/navigation-menu';

@Component({
  selector: 'z-demo-navigation-menu-no-viewport',
  imports: [ZardNavigationMenuImports],
  template: `
    <z-navigation-menu [zViewport]="false">
      <ul z-navigation-menu-list>
        <li z-navigation-menu-item>
          <button type="button" z-navigation-menu-trigger [zNavigationMenuTriggerFor]="overview">Overview</button>

          <ng-template #overview>
            <div z-navigation-menu-content>
              <ul class="w-56">
                <li><a z-navigation-menu-link href="#">Introduction</a></li>
                <li><a z-navigation-menu-link href="#">Installation</a></li>
                <li><a z-navigation-menu-link href="#">Theming</a></li>
              </ul>
            </div>
          </ng-template>
        </li>

        <li z-navigation-menu-item>
          <button type="button" z-navigation-menu-trigger [zNavigationMenuTriggerFor]="resources">Resources</button>

          <ng-template #resources>
            <div z-navigation-menu-content>
              <ul class="w-56">
                <li><a z-navigation-menu-link href="#">Blocks</a></li>
                <li><a z-navigation-menu-link href="#">Changelog</a></li>
                <li><a z-navigation-menu-link href="#">Contributing</a></li>
              </ul>
            </div>
          </ng-template>
        </li>
      </ul>
    </z-navigation-menu>
  `,
  changeDetection: ChangeDetectionStrategy.OnPush,
})
export class ZardDemoNavigationMenuNoViewportComponent {}

API Reference

z-navigation-menuComponent

Root of the navigation bar. Scopes the shared viewport to the triggers inside it.

PropertyDescriptionTypeDefault
[class] Additional CSS classes ClassValue ''
[zViewport] Share a single animated popup between every trigger. When false each trigger opens its own boolean true
[zAlign] Which edge of the active trigger the shared viewport lines up with 'start' | 'center' | 'end' 'start'
[zHoverDelay] Delay in ms before closing once the pointer leaves the bar number 100

z-navigation-menu-listComponent

The `<ul>` holding the items of the bar.

PropertyDescriptionTypeDefault
[class] Additional CSS classes ClassValue ''

z-navigation-menu-itemComponent

The `<li>` wrapping a trigger and its content, or a standalone link.

PropertyDescriptionTypeDefault
[class] Additional CSS classes ClassValue ''

z-navigation-menu-triggerComponent

Opens the content it points to. Inside a root it opens on hover; standalone it opens on click.

PropertyDescriptionTypeDefault
[zNavigationMenuTriggerFor] Reference to the content template TemplateRef<void> required
[zDisabled] Whether the trigger is disabled boolean false
[zTrigger] How the content is opened 'click' | 'hover' 'hover' inside a root, 'click' standalone
[zHoverDelay] Delay in ms before closing on hover exit number 100
[zPlacement] Popup position relative to the trigger. Overlay mode only ZardNavigationMenuPlacement 'bottomLeft'
[zShowChevron] Render the built-in chevron boolean true inside a root, false standalone
[class] Additional CSS classes ClassValue ''

z-navigation-menu-contentComponent

Container for the links of one trigger. Inside the shared viewport it is plain content; in overlay mode it is the popup itself.

PropertyDescriptionTypeDefault
[class] Additional CSS classes ClassValue ''

z-navigation-menu-linkComponent

A single entry. Pair `[zActive]` with `routerLinkActive` to mark the current route.

PropertyDescriptionTypeDefault
[zActive] Marks the link as the current one boolean false
[zDisabled] Whether the link is disabled boolean false
[zInset] Add left padding for alignment boolean false
[zType] Visual variant of the link 'default' | 'destructive' 'default'
[class] Additional CSS classes ClassValue ''
[menuItemTriggered] Emits when the link is activated EventEmitter

z-navigation-menu-indicatorComponent

The arrow that follows the trigger currently owning the viewport.

PropertyDescriptionTypeDefault
[class] Additional CSS classes ClassValue ''

z-navigation-menu-viewportComponent

The shared popup. Rendered automatically by the root while `zViewport` is on — declare it manually only to place it yourself.

PropertyDescriptionTypeDefault

z-navigation-menu-labelComponent

Label for grouping links inside a content block.

PropertyDescriptionTypeDefault
[class] Additional CSS classes ClassValue ''
[inset] Adds left padding for alignment boolean false

z-navigation-menu-shortcutComponent

Displays a keyboard shortcut aligned to the end of a link.

PropertyDescriptionTypeDefault
[class] Additional CSS classes ClassValue ''

z-context-menuComponent

Opens a content template on right click, anchored to the pointer.

PropertyDescriptionTypeDefault
[zContextMenuTriggerFor] Reference to the context menu content TemplateRef<void> required

navigationMenuTriggerVariants()Component

Helper that returns the trigger classes, for links that must line up with the triggers of the bar.

PropertyDescriptionTypeDefault
github iconwhatsapp icondiscord iconX icon

Made with in Brazil. Open source and available on GitHub .