Header Mega Menu
Responsive site header with a hover-card feature mega-menu that collapses into a mobile drawer.
Install
One command copies the item's source into your project, where you own and edit it.
npx manteen add @house/header-mega-menuDependencies
@mantine/core@^9npm@mantine/hooks@^9npm@tabler/icons-react@^3npm
Installing this item also installs @house/mantine-ui-license from the registry.
Preview
Usage
A copy-ready example published by the registry author. Imports use the aliases Manteen configures at init.
import { Text } from "@mantine/core";import { IconBook, IconChartPie3, IconCode, IconCoin, IconFingerprint, IconNotification,} from "@tabler/icons-react";import { HeaderMegaMenu, type HeaderMegaMenuLink } from "@/components/ui/header-mega-menu";
const links: HeaderMegaMenuLink[] = [ { label: "Home", href: "/" }, { label: "Features", href: "/features", features: [ { icon: <IconCode size={22} color="var(--mantine-color-blue-6)" />, title: "Open source", description: "The full source is on GitHub, under the MIT license", }, { icon: <IconCoin size={22} color="var(--mantine-color-blue-6)" />, title: "Free for everyone", description: "No paid tier gates any core feature", }, { icon: <IconBook size={22} color="var(--mantine-color-blue-6)" />, title: "Documentation", description: "Guides and API references for every component", }, { icon: <IconFingerprint size={22} color="var(--mantine-color-blue-6)" />, title: "Security", description: "SOC 2 Type II audited, with SSO on every plan", }, { icon: <IconChartPie3 size={22} color="var(--mantine-color-blue-6)" />, title: "Analytics", description: "Usage dashboards and exportable reports", }, { icon: <IconNotification size={22} color="var(--mantine-color-blue-6)" />, title: "Notifications", description: "Email and webhook alerts for every event", }, ], }, { label: "Learn", href: "/learn" }, { label: "Academy", href: "/academy" },];
export function SiteHeader() { return ( <HeaderMegaMenu logo={ <Text fw={700} size="lg"> Acme </Text> } links={links} featuresViewAllHref="/features" onLogin={() => console.log("log in")} onSignUp={() => console.log("sign up")} /> );}Props
Author-documented props, carried verbatim from the registry item.
| Prop | Type | Default | Description |
|---|---|---|---|
logo | ReactNode | — | Rendered at the start of the header bar; nothing renders when omitted. |
links* | readonly HeaderMegaMenuLink[] | — | Ordered nav links rendered in the header bar and the mobile drawer. At most one entry should set `features`: only the first entry that does renders as the mega-menu (a hover-card dropdown on desktop, a collapsible section on mobile); `features` on any later entry is ignored. |
featuresViewAllHref | string | — | Href for the mega-menu's "View all" link; the link is omitted when unset. |
featuresFooterHeading | string | "Get started" | Heading shown in the mega-menu's footer callout. |
featuresFooterDescription | string | "Everything you need to explore what's possible and start building." | Body copy shown in the mega-menu's footer callout. |
featuresFooterActionLabel | string | "Get started" | Label for the mega-menu footer's action button; clicking it calls `onSignUp`. |
loginLabel | string | "Log in" | Label for the log in button, shown in the header and the mobile drawer. |
signUpLabel | string | "Sign up" | Label for the sign up button, shown in the header and the mobile drawer. |
onLogin | () => void | — | Called when a log in button (header or drawer) is clicked. |
onSignUp | () => void | — | Called when a sign up button (header or drawer) or the mega-menu footer's action button is clicked. |
* required
Styling
No public Styles API is declared. Installed CSS remains editable because your project owns the source; internal class names are implementation details, not a customization contract.
Source
2 installable files ship with this item.
registry/mantine-ui/header-mega-menu/header-mega-menu.tsxcomponent · 7.1 kB · tsx
/** * Adapted from Mantine UI's HeaderMegaMenu at * ffbf61c559f374a7ea28fcf00355e84dcbe9a908. MIT licensed; see * LICENSES/MANTINE-UI.txt after installation. */
import { Anchor, Box, Burger, Button, Center, Collapse, Divider, Drawer, Group, HoverCard, ScrollArea, SimpleGrid, Text, ThemeIcon, UnstyledButton, useMantineTheme,} from "@mantine/core";import { useDisclosure } from "@mantine/hooks";import { IconChevronDown } from "@tabler/icons-react";import type { ReactNode } from "react";
import classes from "./header-mega-menu.module.css";
export interface HeaderMegaMenuFeature { icon?: ReactNode; title: string; description: string;}
export interface HeaderMegaMenuLink { label: string; href: string; /** * Renders this link as a hover/tap mega-menu instead of a plain link. Only * the first entry in `links` that sets `features` is treated as the * mega-menu; `features` on any later entry is ignored. */ features?: readonly HeaderMegaMenuFeature[];}
export interface HeaderMegaMenuProps { /** Rendered at the start of the header bar; nothing renders when omitted. */ logo?: ReactNode; /** Ordered nav links; at most one entry should set `features`. */ links: readonly HeaderMegaMenuLink[]; /** "View all" link inside the mega-menu; the link is omitted when unset. */ featuresViewAllHref?: string; featuresFooterHeading?: string; featuresFooterDescription?: string; featuresFooterActionLabel?: string; loginLabel?: string; signUpLabel?: string; onLogin?: () => void; onSignUp?: () => void;}
export function HeaderMegaMenu({ logo, links, featuresViewAllHref, featuresFooterHeading = "Get started", featuresFooterDescription = "Everything you need to explore what's possible and start building.", featuresFooterActionLabel = "Get started", loginLabel = "Log in", signUpLabel = "Sign up", onLogin, onSignUp,}: HeaderMegaMenuProps) { const [drawerOpened, { toggle: toggleDrawer, close: closeDrawer }] = useDisclosure(false); const [linksOpened, { toggle: toggleLinks }] = useDisclosure(false); const theme = useMantineTheme();
const featuresLink = links.find((link) => link.features); const featureItems = featuresLink?.features?.map((item) => ( <UnstyledButton className={classes.subLink} key={item.title}> <Group wrap="nowrap" align="flex-start"> <ThemeIcon size={34} variant="default" radius="md"> {item.icon} </ThemeIcon> <div> <Text size="sm" fw={500}> {item.title} </Text> <Text size="xs" c="dimmed"> {item.description} </Text> </div> </Group> </UnstyledButton> ));
return ( <> <header className={classes.header}> <Group justify="space-between" h="100%"> {logo}
<Group h="100%" gap={0} visibleFrom="sm"> {links.map((link) => link === featuresLink ? ( <HoverCard key={link.label} width={600} position="bottom" radius="md" shadow="md" withinPortal > <HoverCard.Target> <UnstyledButton className={classes.link}> <Center inline> <Box component="span" mr={5}> {link.label} </Box> <IconChevronDown size={16} color={theme.colors.blue[6]} /> </Center> </UnstyledButton> </HoverCard.Target>
<HoverCard.Dropdown style={{ overflow: "hidden" }}> <Group justify="space-between" px="md"> <Text fw={500}>{link.label}</Text> {featuresViewAllHref && ( <Anchor href={featuresViewAllHref} fz="xs"> View all </Anchor> )} </Group>
<Divider my="sm" />
<SimpleGrid cols={2} spacing={0}> {featureItems} </SimpleGrid>
<div className={classes.dropdownFooter}> <Group justify="space-between"> <div> <Text fw={500} fz="sm"> {featuresFooterHeading} </Text> <Text size="xs" c="dimmed"> {featuresFooterDescription} </Text> </div> <Button variant="default" onClick={onSignUp}> {featuresFooterActionLabel} </Button> </Group> </div> </HoverCard.Dropdown> </HoverCard> ) : ( <a href={link.href} className={classes.link} key={link.label}> {link.label} </a> ), )} </Group>
<Group visibleFrom="sm"> <Button variant="default" onClick={onLogin}> {loginLabel} </Button> <Button onClick={onSignUp}>{signUpLabel}</Button> </Group>
<Burger opened={drawerOpened} onClick={toggleDrawer} hiddenFrom="sm" aria-label="Toggle navigation" /> </Group> </header>
<Drawer opened={drawerOpened} onClose={closeDrawer} size="100%" padding="md" title="Navigation" hiddenFrom="sm" zIndex={1000000} > <ScrollArea h="calc(100vh - 80px)" mx="-md"> <Divider my="sm" />
{links.map((link) => link === featuresLink ? ( <div key={link.label}> <UnstyledButton className={classes.link} onClick={toggleLinks} aria-expanded={linksOpened} > <Center inline> <Box component="span" mr={5}> {link.label} </Box> <IconChevronDown size={16} color={theme.colors.blue[6]} /> </Center> </UnstyledButton> <Collapse expanded={linksOpened}>{featureItems}</Collapse> </div> ) : ( <a href={link.href} className={classes.link} key={link.label}> {link.label} </a> ), )}
<Divider my="sm" />
<Group justify="center" grow pb="xl" px="md"> <Button variant="default" onClick={onLogin}> {loginLabel} </Button> <Button onClick={onSignUp}>{signUpLabel}</Button> </Group> </ScrollArea> </Drawer> </> );}registry/mantine-ui/header-mega-menu/header-mega-menu.module.cssstyle · 1.9 kB · css
/* * Adapted from Mantine UI's HeaderMegaMenu at * ffbf61c559f374a7ea28fcf00355e84dcbe9a908. MIT licensed; see * LICENSES/MANTINE-UI.txt after installation. */.header { height: 60px; padding-left: var(--mantine-spacing-md); padding-right: var(--mantine-spacing-md); border-bottom: 1px solid light-dark(var(--mantine-color-gray-3), var(--mantine-color-dark-4));}
/* Upstream used postcss-preset-mantine's `@mixin hover` and * `$mantine-breakpoint-sm`; this registry ships plain CSS modules with no * PostCSS preset, so both are inlined here (`:hover`, and the literal `48em` * default `sm` breakpoint value — `var()` is invalid inside a media * condition). See cards-carousel.module.css for the same convention. */.link { display: flex; align-items: center; height: 100%; padding-left: var(--mantine-spacing-md); padding-right: var(--mantine-spacing-md); text-decoration: none; color: light-dark(var(--mantine-color-black), var(--mantine-color-white)); font-weight: 500; font-size: var(--mantine-font-size-sm);}
.link:hover { background-color: light-dark(var(--mantine-color-gray-0), var(--mantine-color-dark-6));}
@media (max-width: 48em) { .link { height: 42px; width: 100%; }}
.subLink { width: 100%; padding: var(--mantine-spacing-xs) var(--mantine-spacing-md); border-radius: var(--mantine-radius-md);}
.subLink:hover { background-color: light-dark(var(--mantine-color-gray-0), var(--mantine-color-dark-7));}
.dropdownFooter { background-color: light-dark(var(--mantine-color-gray-0), var(--mantine-color-dark-7)); margin: calc(var(--mantine-spacing-md) * -1); margin-top: var(--mantine-spacing-sm); padding: var(--mantine-spacing-md) calc(var(--mantine-spacing-md) * 2); padding-bottom: var(--mantine-spacing-xl); border-top: 1px solid light-dark(var(--mantine-color-gray-1), var(--mantine-color-dark-5));}Author notes
Adapted from Mantine UI HeaderMegaMenu at ffbf61c559…; feature-grid mock data and the two CTA labels became typed props, the fixed nav links became a consumer-supplied array, and the upstream @mantinex/mantine-logo import (not part of this workspace) became a logo ReactNode prop. Kept as an opinionated block with internal CSS classes rather than a public Styles API, consistent with this catalog's other page-section blocks (authentication-form, stats-grid, table-sort).