Nested Navbar
Collapsible sidebar navigation with nested link groups and a user footer.
Install
One command copies the item's source into your project, where you own and edit it.
npx manteen add @house/navbar-nestedDependencies
@mantine/core@^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 { IconCalendarStats, IconGauge, IconLock, IconNotes } from "@tabler/icons-react";
import { NavbarNested } from "@/components/ui/navbar-nested";
export function AppSidebar() { // NavbarNested fills its parent's height (see the `height: 100%` note in // navbar-nested.module.css), so it needs a height-constrained wrapper // here; in an app shell that's usually AppShell.Navbar instead. return ( <div style={{ height: "100vh" }}> <NavbarNested versionLabel="v1.4.0" user={{ name: "Jordan Vance", email: "jordan@example.com", }} links={[ { label: "Dashboard", icon: IconGauge, link: "/dashboard" }, { label: "Market news", icon: IconNotes, initiallyOpened: true, links: [ { label: "Overview", link: "/news/overview" }, { label: "Forecasts", link: "/news/forecasts" }, { label: "Outlook", link: "/news/outlook" }, ], }, { label: "Security", icon: IconLock, links: [ { label: "Enable 2FA", link: "/security/2fa" }, { label: "Change password", link: "/security/password" }, ], }, { label: "Releases", icon: IconCalendarStats, link: "/releases" }, ]} /> </div> );}Props
Author-documented props, carried verbatim from the registry item.
| Prop | Type | Default | Description |
|---|---|---|---|
links* | NavbarNestedLinkItem[] | — | Ordered top-level nav entries; each entry sets either `link` to render a leaf that navigates directly, or `links` to render a group whose secondary links collapse beneath it (and may set `initiallyOpened`). |
user* | UserButtonProps | — | Name, email and optional avatar rendered in the footer user row. |
versionLabel | string | — | Text for the version badge next to the logo; no badge renders when omitted. |
* 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
7 installable files ship with this item.
registry/mantine-ui/navbar-nested/navbar-nested.tsxcomponent · 1.5 kB · tsx
/** * Adapted from Mantine UI's NavbarNested at * ffbf61c559f374a7ea28fcf00355e84dcbe9a908. MIT licensed; see * LICENSES/MANTINE-UI.txt after installation. */
import { Code, Group, ScrollArea } from "@mantine/core";import classes from "./navbar-nested.module.css";import { LinksGroup, type NavbarNestedLinkItem } from "./navbar-nested-links-group";import { Logo } from "./navbar-nested-logo";import { UserButton, type UserButtonProps } from "./navbar-nested-user-button";
export type { NavbarNestedLink, NavbarNestedLinkGroupItem, NavbarNestedLinkItem, NavbarNestedLinkLeafItem,} from "./navbar-nested-links-group";export type { UserButtonProps } from "./navbar-nested-user-button";
export interface NavbarNestedProps { links: NavbarNestedLinkItem[]; user: UserButtonProps; versionLabel?: string;}
export function NavbarNested({ links, user, versionLabel }: NavbarNestedProps) { const items = links.map((item) => <LinksGroup {...item} key={item.label} />);
return ( <nav className={classes.navbar}> <div className={classes.header}> <Group justify="space-between"> <Logo style={{ width: 120 }} /> {versionLabel && <Code fw={700}>{versionLabel}</Code>} </Group> </div>
<ScrollArea className={classes.links}> <div className={classes.linksInner}>{items}</div> </ScrollArea>
<div className={classes.footer}> <UserButton {...user} /> </div> </nav> );}registry/mantine-ui/navbar-nested/navbar-nested.module.cssstyle · 1.7 kB · css
/* * Adapted from Mantine UI's NavbarNested at * ffbf61c559f374a7ea28fcf00355e84dcbe9a908. MIT licensed; see * LICENSES/MANTINE-UI.txt after installation. */.navbar { background-color: light-dark(var(--mantine-color-white), var(--mantine-color-dark-6)); /* Upstream's 800px is a fixed demo height. A sidebar fills its container * instead, so mount it inside a height-constrained parent (a 100vh/100dvh * wrapper, or an AppShell.Navbar) — see navbar-nested.usage.tsx. Against * an unconstrained (auto-height) parent this resolves to auto and the * navbar sizes to its content instead of collapsing. */ height: 100%; width: 300px; padding: var(--mantine-spacing-md); padding-bottom: 0; display: flex; flex-direction: column; border-right: 1px solid light-dark(var(--mantine-color-gray-3), var(--mantine-color-dark-4));}
.header { padding: var(--mantine-spacing-md); padding-top: 0; margin-left: calc(var(--mantine-spacing-md) * -1); margin-right: calc(var(--mantine-spacing-md) * -1); color: light-dark(var(--mantine-color-black), var(--mantine-color-white)); border-bottom: 1px solid light-dark(var(--mantine-color-gray-3), var(--mantine-color-dark-4));}
.links { flex: 1; margin-left: calc(var(--mantine-spacing-md) * -1); margin-right: calc(var(--mantine-spacing-md) * -1);}
.linksInner { padding-top: var(--mantine-spacing-xl); padding-bottom: var(--mantine-spacing-xl);}
.footer { margin-left: calc(var(--mantine-spacing-md) * -1); margin-right: calc(var(--mantine-spacing-md) * -1); border-top: 1px solid light-dark(var(--mantine-color-gray-3), var(--mantine-color-dark-4));}registry/mantine-ui/navbar-nested/navbar-nested-links-group.tsxcomponent · 2.5 kB · tsx
/** * Adapted from Mantine UI's NavbarLinksGroup at * ffbf61c559f374a7ea28fcf00355e84dcbe9a908. MIT licensed; see * LICENSES/MANTINE-UI.txt after installation. */
import { Box, Collapse, Group, Text, ThemeIcon, UnstyledButton } from "@mantine/core";import { IconChevronRight, type TablerIcon } from "@tabler/icons-react";import { useState } from "react";
import classes from "./navbar-nested-links-group.module.css";
export interface NavbarNestedLink { label: string; link: string;}
interface NavbarNestedLinkItemBase { icon: TablerIcon; label: string;}
/** An entry that expands a nested list of secondary links. */export interface NavbarNestedLinkGroupItem extends NavbarNestedLinkItemBase { initiallyOpened?: boolean; links: NavbarNestedLink[];}
/** An entry that navigates directly; it has no nested list. */export interface NavbarNestedLinkLeafItem extends NavbarNestedLinkItemBase { link: string;}
export type NavbarNestedLinkItem = NavbarNestedLinkGroupItem | NavbarNestedLinkLeafItem;
export function LinksGroup(props: NavbarNestedLinkItem) { const { icon: Icon, label } = props; const [opened, setOpened] = useState("links" in props ? (props.initiallyOpened ?? false) : false);
const content = ( <Group justify="space-between" gap={0}> <Box style={{ display: "flex", alignItems: "center" }}> <ThemeIcon variant="light" size={30}> <Icon size={18} aria-hidden="true" /> </ThemeIcon> <Box ml="md">{label}</Box> </Box> {"links" in props && ( <IconChevronRight className={classes.chevron} stroke={1.5} size={16} aria-hidden="true" style={{ transform: opened ? "rotate(-90deg)" : "none" }} /> )} </Group> );
return ( <> {"links" in props ? ( <UnstyledButton onClick={() => setOpened((o) => !o)} className={classes.control} aria-expanded={opened} > {content} </UnstyledButton> ) : ( <UnstyledButton component="a" href={props.link} className={classes.control}> {content} </UnstyledButton> )} {"links" in props && ( <Collapse expanded={opened}> {props.links.map((link) => ( <Text<"a"> component="a" className={classes.link} href={link.link} key={link.label}> {link.label} </Text> ))} </Collapse> )} </> );}registry/mantine-ui/navbar-nested/navbar-nested-links-group.module.cssstyle · 2.0 kB · css
/* * Adapted from Mantine UI's NavbarLinksGroup at * ffbf61c559f374a7ea28fcf00355e84dcbe9a908. MIT licensed; see * LICENSES/MANTINE-UI.txt after installation. * * Upstream's `@mixin hover` (postcss-preset-mantine) is expanded here to * plain `@media (hover: …)` rules so the module compiles without requiring * every consumer's PostCSS pipeline to include that preset. */.control { font-weight: 500; display: block; width: 100%; padding: var(--mantine-spacing-xs) var(--mantine-spacing-md); color: var(--mantine-color-text); font-size: var(--mantine-font-size-sm);}
@media (hover: hover) { .control:hover { background-color: light-dark(var(--mantine-color-gray-0), var(--mantine-color-dark-7)); color: light-dark(var(--mantine-color-black), var(--mantine-color-dark-0)); }}
@media (hover: none) { .control:active { background-color: light-dark(var(--mantine-color-gray-0), var(--mantine-color-dark-7)); color: light-dark(var(--mantine-color-black), var(--mantine-color-dark-0)); }}
.link { font-weight: 500; display: block; text-decoration: none; padding: var(--mantine-spacing-xs) var(--mantine-spacing-md); padding-left: var(--mantine-spacing-md); margin-left: var(--mantine-spacing-xl); font-size: var(--mantine-font-size-sm); color: light-dark(var(--mantine-color-gray-7), var(--mantine-color-dark-0)); border-left: 1px solid light-dark(var(--mantine-color-gray-3), var(--mantine-color-dark-4));}
@media (hover: hover) { .link:hover { background-color: light-dark(var(--mantine-color-gray-0), var(--mantine-color-dark-7)); color: light-dark(var(--mantine-color-black), var(--mantine-color-dark-0)); }}
@media (hover: none) { .link:active { background-color: light-dark(var(--mantine-color-gray-0), var(--mantine-color-dark-7)); color: light-dark(var(--mantine-color-black), var(--mantine-color-dark-0)); }}
.chevron { transition: transform 200ms ease;}registry/mantine-ui/navbar-nested/navbar-nested-user-button.tsxcomponent · 950 B · tsx
/** * Adapted from Mantine UI's UserButton at * ffbf61c559f374a7ea28fcf00355e84dcbe9a908. MIT licensed; see * LICENSES/MANTINE-UI.txt after installation. */
import { Avatar, Group, Text, UnstyledButton } from "@mantine/core";import { IconChevronRight } from "@tabler/icons-react";
import classes from "./navbar-nested-user-button.module.css";
export interface UserButtonProps { name: string; email: string; avatar?: string;}
export function UserButton({ name, email, avatar }: UserButtonProps) { return ( <UnstyledButton className={classes.user}> <Group> <Avatar src={avatar} radius="xl" alt="" />
<div style={{ flex: 1 }}> <Text size="sm" fw={500}> {name} </Text>
<Text c="dimmed" size="xs"> {email} </Text> </div>
<IconChevronRight size={14} stroke={1.5} aria-hidden="true" /> </Group> </UnstyledButton> );}registry/mantine-ui/navbar-nested/navbar-nested-user-button.module.cssstyle · 813 B · css
/* * Adapted from Mantine UI's UserButton at * ffbf61c559f374a7ea28fcf00355e84dcbe9a908. MIT licensed; see * LICENSES/MANTINE-UI.txt after installation. * * Upstream's `@mixin hover` (postcss-preset-mantine) is expanded here to * plain `@media (hover: …)` rules so the module compiles without requiring * every consumer's PostCSS pipeline to include that preset. */.user { display: block; width: 100%; padding: var(--mantine-spacing-md); color: light-dark(var(--mantine-color-black), var(--mantine-color-dark-0));}
@media (hover: hover) { .user:hover { background-color: light-dark(var(--mantine-color-gray-0), var(--mantine-color-dark-8)); }}
@media (hover: none) { .user:active { background-color: light-dark(var(--mantine-color-gray-0), var(--mantine-color-dark-8)); }}registry/mantine-ui/navbar-nested/navbar-nested-logo.tsxcomponent · 7.5 kB · tsx
/** * Adapted from Mantine UI's NavbarNested Logo at * ffbf61c559f374a7ea28fcf00355e84dcbe9a908. MIT licensed; see * LICENSES/MANTINE-UI.txt after installation. */
import type { ComponentPropsWithoutRef } from "react";
export function Logo(props: ComponentPropsWithoutRef<"svg">) { return ( <svg aria-hidden="true" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 623 163" {...props}> <g fill="none" fillRule="evenodd"> <path fill="#339AF0" fillRule="nonzero" d="M162.162 81.5c0-45.011-36.301-81.5-81.08-81.5C36.301 0 0 36.489 0 81.5 0 126.51 36.301 163 81.081 163s81.081-36.49 81.081-81.5z" /> <g fill="#FFF"> <path fillRule="nonzero" d="M65.983 43.049a6.234 6.234 0 00-.336 6.884 6.14 6.14 0 001.618 1.786c9.444 7.036 14.866 17.794 14.866 29.52 0 11.726-5.422 22.484-14.866 29.52a6.142 6.142 0 00-1.616 1.786 6.211 6.211 0 00-.694 4.693c.197.79.546 1.533 1.028 2.186a6.154 6.154 0 008.634 1.284 50.112 50.112 0 007.947-7.39h17.493c3.406 0 6.174-2.772 6.174-6.194s-2.762-6.194-6.174-6.194h-9.655a49.166 49.166 0 004.071-19.69 49.166 49.166 0 00-4.07-19.692h9.66c3.406 0 6.173-2.771 6.173-6.194 0-3.422-2.762-6.193-6.173-6.193H82.574a50.11 50.11 0 00-7.952-7.397 6.149 6.149 0 00-4.578-1.153 6.189 6.189 0 00-4.055 2.438h-.006z" /> <path d="M56.236 79.391a9.342 9.342 0 01.632-3.608 9.261 9.261 0 011.967-3.077 9.143 9.143 0 012.994-2.063 9.06 9.06 0 017.103 0 9.144 9.144 0 012.995 2.063 9.261 9.261 0 011.967 3.077 9.34 9.34 0 01.63 3.608 9.299 9.299 0 01-2.755 6.395 9.094 9.094 0 01-6.388 2.63 9.094 9.094 0 01-6.39-2.63 9.299 9.299 0 01-2.755-6.395z" /> </g> <path fill="currentColor" fillRule="nonzero" d="M291.736 126.644c1.984 0 3.823-.434 5.518-1.302 1.695-.868 2.542-2.129 2.542-3.782v-77.5c0-2.976-.827-5.063-2.48-6.262-1.653-1.199-3.513-1.798-5.58-1.798-1.901 0-3.555.207-4.96.62-1.405.413-2.666 1.24-3.782 2.48s-2.418 3.059-3.906 5.456l-15.252 27.776-15.128-27.776c-1.323-2.397-2.583-4.216-3.782-5.456-1.199-1.24-2.48-2.067-3.844-2.48-1.364-.413-3.038-.62-5.022-.62-1.984 0-3.823.6-5.518 1.798-1.695 1.199-2.542 3.286-2.542 6.262v77.5c0 1.653.847 2.914 2.542 3.782 1.695.868 3.534 1.302 5.518 1.302 2.067 0 3.927-.434 5.58-1.302 1.653-.868 2.48-2.129 2.48-3.782V67.248l14.26 26.784c.744 1.24 1.591 2.087 2.542 2.542.95.455 1.88.682 2.79.682.992 0 1.984-.248 2.976-.744s1.86-1.323 2.604-2.48l14.384-25.792v53.32c0 1.653.847 2.914 2.542 3.782 1.695.868 3.534 1.302 5.518 1.302zm34.375 1.116c4.298 0 7.956-.992 10.974-2.976 3.017-1.984 5.642-4.257 7.874-6.82v3.596c0 1.405.682 2.604 2.046 3.596 1.364.992 3.08 1.488 5.146 1.488 2.232 0 4.092-.496 5.58-1.488 1.488-.992 2.232-2.19 2.232-3.596V91.18c0-4.216-.889-8.143-2.666-11.78-1.778-3.637-4.609-6.613-8.494-8.928-3.886-2.315-9.052-3.472-15.5-3.472-2.894 0-5.87.372-8.928 1.116-3.059.744-5.642 1.798-7.75 3.162-2.108 1.364-3.162 2.914-3.162 4.65 0 1.819.475 3.596 1.426 5.332.95 1.736 2.294 2.604 4.03 2.604 1.074 0 2.066-.33 2.976-.992.91-.661 2.211-1.302 3.906-1.922 1.694-.62 4.112-.93 7.254-.93 2.81 0 4.98.579 6.51 1.736 1.53 1.157 2.645 2.604 3.348 4.34a14.092 14.092 0 011.054 5.332v1.612h-5.084c-5.704 0-10.726.537-15.066 1.612-4.34 1.075-7.73 2.935-10.168 5.58-2.439 2.645-3.658 6.324-3.658 11.036 0 5.621 1.591 9.775 4.774 12.462 3.182 2.687 6.964 4.03 11.346 4.03zm6.448-11.904c-1.819 0-3.369-.537-4.65-1.612-1.282-1.075-1.922-2.77-1.922-5.084 0-2.315.764-4.03 2.294-5.146 1.53-1.116 3.534-1.84 6.014-2.17 2.48-.33 5.084-.496 7.812-.496h1.86v2.604c0 1.984-.6 3.885-1.798 5.704-1.199 1.819-2.666 3.307-4.402 4.464-1.736 1.157-3.472 1.736-5.208 1.736zm84.169 10.788c2.067 0 3.927-.434 5.58-1.302 1.653-.868 2.48-2.129 2.48-3.782V92.172c0-4.63-.971-8.845-2.914-12.648-1.943-3.803-4.526-6.84-7.75-9.114C410.9 68.137 407.345 67 403.46 67c-4.133 0-7.626.971-10.478 2.914-2.852 1.943-4.898 4.113-6.138 6.51v-3.72c0-1.488-.682-2.687-2.046-3.596-1.364-.91-3.038-1.364-5.022-1.364-2.315 0-4.216.455-5.704 1.364-1.488.91-2.232 2.108-2.232 3.596v48.856c0 1.24.744 2.397 2.232 3.472 1.488 1.075 3.39 1.612 5.704 1.612 2.232 0 4.133-.537 5.704-1.612 1.57-1.075 2.356-2.232 2.356-3.472V92.172c0-2.315.496-4.299 1.488-5.952.992-1.653 2.273-2.935 3.844-3.844 1.57-.91 3.183-1.364 4.836-1.364 1.984 0 3.803.558 5.456 1.674 1.653 1.116 2.955 2.5 3.906 4.154a10.52 10.52 0 011.426 5.332v29.388c0 1.653.868 2.914 2.604 3.782 1.736.868 3.513 1.302 5.332 1.302zm47.432 0c2.315 0 4.03-.703 5.146-2.108 1.116-1.405 1.674-2.976 1.674-4.712 0-1.653-.558-3.183-1.674-4.588-1.116-1.405-2.831-2.108-5.146-2.108h-4.836c-2.563 0-4.36-.496-5.394-1.488-1.033-.992-1.55-2.687-1.55-5.084V79.4h14.632c1.323 0 2.335-.6 3.038-1.798.703-1.199 1.054-2.542 1.054-4.03 0-1.488-.351-2.831-1.054-4.03-.703-1.199-1.715-1.798-3.038-1.798H452.38V46.416c0-1.488-.847-2.687-2.542-3.596-1.695-.91-3.534-1.364-5.518-1.364-1.819 0-3.596.455-5.332 1.364-1.736.91-2.604 2.108-2.604 3.596v60.14c0 6.944 1.963 12.028 5.89 15.252 3.927 3.224 9.61 4.836 17.05 4.836h4.836zM487.232 54.6c2.397 0 4.443-.806 6.138-2.418 1.695-1.612 2.542-3.41 2.542-5.394 0-2.15-.847-3.989-2.542-5.518-1.695-1.53-3.74-2.294-6.138-2.294-2.397 0-4.464.765-6.2 2.294-1.736 1.53-2.604 3.369-2.604 5.518 0 1.984.868 3.782 2.604 5.394 1.736 1.612 3.803 2.418 6.2 2.418zm0 72.044c2.232 0 4.133-.537 5.704-1.612 1.57-1.075 2.356-2.232 2.356-3.472V72.704c0-1.488-.785-2.687-2.356-3.596-1.57-.91-3.472-1.364-5.704-1.364-2.315 0-4.216.455-5.704 1.364-1.488.91-2.232 2.108-2.232 3.596v48.856c0 1.24.744 2.397 2.232 3.472 1.488 1.075 3.39 1.612 5.704 1.612zm65.247 0c2.066 0 3.926-.434 5.58-1.302 1.653-.868 2.48-2.129 2.48-3.782V92.172c0-4.63-.972-8.845-2.914-12.648-1.943-3.803-4.526-6.84-7.75-9.114-3.224-2.273-6.779-3.41-10.664-3.41-4.134 0-7.626.971-10.478 2.914-2.852 1.943-4.898 4.113-6.138 6.51v-3.72c0-1.488-.682-2.687-2.046-3.596-1.364-.91-3.038-1.364-5.022-1.364-2.315 0-4.216.455-5.704 1.364-1.488.91-2.232 2.108-2.232 3.596v48.856c0 1.24.744 2.397 2.232 3.472 1.488 1.075 3.39 1.612 5.704 1.612 2.232 0 4.133-.537 5.704-1.612 1.57-1.075 2.356-2.232 2.356-3.472V92.172c0-2.315.496-4.299 1.488-5.952.992-1.653 2.273-2.935 3.844-3.844 1.57-.91 3.182-1.364 4.836-1.364 1.984 0 3.802.558 5.456 1.674 1.653 1.116 2.955 2.5 3.906 4.154a10.52 10.52 0 011.426 5.332v29.388c0 1.653.868 2.914 2.604 3.782 1.736.868 3.513 1.302 5.332 1.302zm47.68 1.116c4.464 0 8.328-.558 11.594-1.674 3.265-1.116 5.786-2.48 7.564-4.092 1.777-1.612 2.666-3.12 2.666-4.526 0-.827-.248-1.798-.744-2.914a8.641 8.641 0 00-2.108-2.914c-.91-.827-1.984-1.24-3.224-1.24-1.158 0-2.398.372-3.72 1.116-1.323.744-2.894 1.53-4.712 2.356-1.819.827-4.092 1.24-6.82 1.24-4.299 0-7.792-1.095-10.478-3.286-2.687-2.19-4.03-5.063-4.03-8.618v-1.86h25.172c1.901 0 3.74-.186 5.518-.558 1.777-.372 3.244-1.323 4.402-2.852 1.157-1.53 1.736-4.071 1.736-7.626 0-4.63-1.199-8.68-3.596-12.152-2.398-3.472-5.518-6.2-9.362-8.184-3.844-1.984-8.08-2.976-12.71-2.976-5.043 0-9.61 1.137-13.702 3.41-4.092 2.273-7.358 5.29-9.796 9.052-2.439 3.761-3.658 7.874-3.658 12.338v10.54c0 5.043 1.281 9.486 3.844 13.33 2.562 3.844 6.096 6.82 10.602 8.928 4.505 2.108 9.692 3.162 15.562 3.162zm4.092-35.836h-18.104v-3.472c0-1.984.516-3.7 1.55-5.146 1.033-1.447 2.376-2.563 4.03-3.348 1.653-.785 3.43-1.178 5.332-1.178 1.984 0 3.802.413 5.456 1.24 1.653.827 2.976 1.984 3.968 3.472s1.488 3.183 1.488 5.084c0 1.323-.269 2.211-.806 2.666-.538.455-1.509.682-2.914.682z" /> </g> </svg> );}Author notes
Adapted from Mantine UI NavbarNested at ffbf61c559…, merged with its NavbarLinksGroup and UserButton siblings; the mock nav tree, avatar and version badge became typed props, dead leaf-link buttons became real anchors, and the sidebar now fills a height-constrained parent (see the usage example) instead of a fixed 800px demo height.