MegaMenu
The site's main navigation on desktop: disclosure buttons that open panels of
links, never a menu, so it is announced and operated as links. Closed panels
keep their links in the DOM for crawlers. Compose it with MegaMenuBar and a
panel shape, MegaMenuFlyout or MegaMenuFullWidth.
import { MegaMenu } from "@evolve-storefront/layout/mega-menu";Design notes
One panel is open at a time. Following a link, Escape, a click outside and focus leaving the navigation all close it; Escape returns focus to its button.
Preview
Usage
The sites' composition, hover and a full-width panel: choosing a category closes the panel, and it stays closed once the hover timers have run out.
<MegaMenu label={t("main")} renderLink={renderLink} openOnHover> <MegaMenuBar items={[{ id: "all", label: t("categories"), children: items }]} renderPanel={(item) => <MegaMenuFullWidth item={item} />} /></MegaMenu>;Props
| Prop | Type | Description |
|---|---|---|
label | string | Accessible name of the navigation, such as "Main". |
children | ReactNode | A |
renderLink? | MegaMenuLinkRenderer | |
renderPromo? | MegaMenuPromoRenderer<unknown> | |
icons? | Readonly<Record<string, ComponentType<SVGProps<SVGSVGElement>>>> | The site's icons by name; without it, no item shows an icon. |
openOnHover? | boolean | Open a panel on hover as well as on click and keys. |
hoverIntent? | number | How long the pointer must rest on an item before hover opens it, in ms. |
hoverDelay? | number | How long the pointer may be away, or crossing toward a panel, in ms. |
Any other props go to the root element, like on a plain HTML element.
Change the layout
Pass children to MegaMenu to arrange its parts yourself. Every part is exported from @evolve-storefront/layout/mega-menu.
<MegaMenu label={t("main")} renderLink={renderLink} openOnHover> <MegaMenuBar items={[{ id: "all", label: t("categories"), children: items }]} renderPanel={(item) => <MegaMenuFullWidth item={item} />} /></MegaMenu>;MegaMenuBarThe row of top-level entries.
| Prop | Type | Description |
|---|---|---|
items | readonly MegaMenuItem[] | |
secondaryItems? | readonly MegaMenuItem[] | The secondary menu, such as service or business, after |
renderPanel? | ((item: MegaMenuItem) => ReactNode) | The panel of an item with children: |
Any other props go to the root element, like on a plain HTML element.
MegaMenuBrandsA brands panel across the navigation: "All brands (n)", the popular brands as tiles, and every brand A–Z.
| Prop | Type | Description |
|---|---|---|
item | MegaMenuBrandsItem | |
total? | number | How many brands the all-brands page has, when more than the menu lists. |
className? | string |
MegaMenuFlyoutA panel under its own button: the item's link, its children as columns, and its promo beside them.
| Prop | Type | Description |
|---|---|---|
item | MegaMenuItem | |
className? | string |
MegaMenuFullWidthA panel across the whole navigation for one button, such as "Categories": its children listed on the left, the chosen one's children as links on the right, with that child's promo or else the button's.
| Prop | Type | Description |
|---|---|---|
item | MegaMenuItem | The button's item; its children are the menu's top level. |
className? | string |
Examples
Open On HoverHover opens a panel once the pointer rests for hoverIntent, and leaving closes it after hoverDelay.
<MegaMenu label={t("main")} openOnHover>{/* … */}</MegaMenu>;Full WidthOne button opening a panel across the whole navigation: its children on the left, the chosen one's children as links on the right.
<MegaMenu label={t("main")} renderLink={renderLink} renderPromo={renderPromo}> <MegaMenuBar items={siteMegaMenuItems(site?.headerNavigation, resolveHref)} // [Categories, Brands, …] renderPanel={(item) => <MegaMenuFullWidth item={item} />} /></MegaMenu>;Childless ItemA top-level item without children is a link, styled as the buttons beside it: on a dark header "Sale" takes the bar's colour, not the page's.
/* Style both kinds of top-level entry together. */[data-component="mega-menu-trigger"],[data-component="mega-menu-bar-link"] { color: var(--color-primary-foreground);}BrandsBrands as their own panel: "All brands (n)", the popular brands as tiles, then every brand A–Z.
<MegaMenu label={t("main")} renderLink={renderLink}> <MegaMenuBar items={[...items, brands]} // brands: { id, label, href: allBrandsPage, children: [{ id, label, href, logo?, popular? }] } renderPanel={(item) => item.id === brands.id ? <MegaMenuBrands item={brands} total={brandCount} /> : <MegaMenuFlyout item={item} /> } /></MegaMenu>;With IconsAn item's icon names a glyph in the site's icons, shown before its label in the bar and the panel.
// items: [{ id, label, icon: "tools", children }, …]<MegaMenu label={t("main")} renderLink={renderLink} icons={icons}> <MegaMenuBar items={items} /></MegaMenu>;From SiteThe header menu and its secondary menu of the site query, through siteMegaMenuItems: a heading without a link opens its panel all the same, and a product entry links to its page.
const { site } = await fetcher(LayoutGetSite, { version });const resolveHref = (link) => siteLinkHref(link, ({ slug }) => productHref(slug));const items = siteMegaMenuItems(site?.headerNavigation, resolveHref);const secondaryItems = siteMegaMenuItems(site?.secondaryNavigation, resolveHref);
<MegaMenu label={t("main")} renderLink={renderLink} renderPromo={(promo) => <MenuPromo promo={promo} />}> <MegaMenuBar items={items} secondaryItems={secondaryItems} renderPanel={(item) => <MegaMenuFlyout item={item} />} /></MegaMenu>;
