Skip to content

Mega Menu

Group navigation links into columns with captions and a featured card.

import { MegaMenuContent, MegaMenuFeatured, MegaMenuGroup, MegaMenuLink } from '@youj/react'

Installation & usage

npm i @youj/react

For an app already set up with npx @youj/cli init, which installs the peers (@youj/core, @youj/motion, motion and Base UI) and wires the provider. New app? Start at Installation.

Use a mega menu when a top-level section has more destinations than a plain dropdown holds well: a product with a dozen features, a resources hub, a developer portal. Each panel groups links into captioned columns, and a featured card at the end can carry a launch or a case study. For a handful of links, a Navigation Menu with plain panels is enough. Actions (sign out, delete, switch theme) belong in a Dropdown Menu, not in navigation.

The mega menu parts go inside a Navigation Menu: MegaMenuContent takes the place of NavigationMenuContent.

import {
  MegaMenuContent,
  MegaMenuFeatured,
  MegaMenuGroup,
  MegaMenuLink,
  NavigationMenu,
  NavigationMenuItem,
  NavigationMenuList,
  NavigationMenuTrigger,
} from '@youj/react'
import { RocketIcon, TerminalIcon } from '@youj/icons'

export function ProductMenu() {
  return (
    <NavigationMenu aria-label="Main">
      <NavigationMenuList>
        <NavigationMenuItem value="product">
          <NavigationMenuTrigger>Product</NavigationMenuTrigger>
          <MegaMenuContent featured={<MegaMenuFeatured href="/blog/2-0" eyebrow="New" title="Northwind 2.0" />}>
            <MegaMenuGroup title="Build">
              <MegaMenuLink href="/deployments" icon={<RocketIcon size={16} />} title="Deployments" description="Git-push to preview, promote to prod." />
              <MegaMenuLink href="/functions" icon={<TerminalIcon size={16} />} title="Functions" description="Server code that scales to zero." />
            </MegaMenuGroup>
          </MegaMenuContent>
        </NavigationMenuItem>
      </NavigationMenuList>
    </NavigationMenu>
  )
}

Each MegaMenuGroup is a column, 15.5rem wide, under its caption. MegaMenuLink is a destination: an icon tile, a title and one line of description. It closes the menu when it's followed. A Youj icon in the tile plays while the link is hovered or focused, with no wiring.

In a header

A mega menu usually lives in an App Header, inside AppHeaderNav, which hides it below md. Phones get the same destinations as a flat list in AppHeaderMenu.

Reference

Propsfeatured, title, title… (9)

Props

MegaMenuContent

MegaMenuContent props
PropType
featuredReactNode

A card at the inline end: a launch, a case study, a changelog entry.

–
Other props pass through: extends ComponentProps<typeof NavigationMenuContent>.

It takes NavigationMenuContent's props as well.

MegaMenuGroup

MegaMenuGroup props
PropType
titleReactNode

The column's caption. It labels the group for assistive tech.

–
Other props pass through: extends ComponentProps<'div'>.
MegaMenuLink props
PropType
iconReactNode

A 16px glyph for the tile; a youj icon plays while the link is hovered or focused.

–
titleRequiredReactNode–
descriptionReactNode–
Other props pass through: extends ComponentProps<typeof NavigationMenuLink>.

MegaMenuFeatured

MegaMenuFeatured props
PropType
mediaReactNode

Art for the top of the card: an image, a motion background piece. Decorative.

–
eyebrowReactNode

A small line above the title, e.g. "New".

–
titleRequiredReactNode–
descriptionReactNode–
Other props pass through: extends ComponentProps<typeof NavigationMenuLink>.

MegaMenuLink and MegaMenuFeatured are NavigationMenuLinks, so href, render (for a router link), active and closeOnClick pass through.

MotionHover pill, popup in, popup out… · reduced motion

Motion

The motion is the Navigation Menu's: the panels only add layout.

Motion tokens
MomentTokens
Hover pillglides between triggers with the pointer and keyboard focus
spring.smooth

0.36s, bounce 0

0.36s, bounce 0
Popup ingrows out of the trigger: scale from 0.96 and fade
duration.fastease.enter

140ms · cubic-bezier(0.16, 1, 0.3, 1)

140mscubic-bezier(0.16, 1, 0.3, 1)
Popup out
duration.instantease.exit

80ms · cubic-bezier(0.4, 0, 1, 1)

80mscubic-bezier(0.4, 0, 1, 1)
Popup between triggersslides to the next trigger and morphs to its panel size
duration.slowease.standard

340ms · cubic-bezier(0.2, 0, 0, 1)

340mscubic-bezier(0.2, 0, 0, 1)
Panels trade places24px drift and fade, away from the way you moved
duration.slowease.standard

340ms · cubic-bezier(0.2, 0, 0, 1)

340mscubic-bezier(0.2, 0, 0, 1)
Link tilethe icon turns to ink, and a Youj icon plays its own motion
duration.fastease.standard

140ms · cubic-bezier(0.2, 0, 0, 1)

140mscubic-bezier(0.2, 0, 0, 1)
Featured card borderstrengthens on hover
duration.fastease.standard

140ms · cubic-bezier(0.2, 0, 0, 1)

140mscubic-bezier(0.2, 0, 0, 1)

Reduced motion

The popup and its panels appear, resize and swap at once, the pill jumps, and link icons don't play.

Reduced motion

The panel appears, resizes and swaps with the next one at once, and the hover pill jumps between triggers. Link icons stay still. The menu opens, closes and moves focus exactly as it does with motion.

AccessibilityKeyboard, ARIA, focus

Accessibility

  • Each column is a group labeled by its caption.
  • A link is named by its title, and its description is read after it (aria-describedby). The featured card is named by its eyebrow and title. Icons and art are hidden from assistive technology.
  • Triggers are buttons with aria-expanded. The whole menu is a nav landmark, named by the NavigationMenu's aria-label.
KeyAction
Enter / Space / ↓Open the focused trigger's panel.
TabMove through the panel's links, then on to the next trigger.
EscapeClose the panel and return focus to its trigger.