Skip to main content
react-md

Navigation

The Navigation component can be used to create a list of links to different pages within the app and generally used within a Sheet or LayoutNav.

Most of the demos on this page are for presentational purpose only and will use a FakeLink with a hard-coded pathname. A "real-world" example is available as the navigation for this website and some example layouts.

Simple Example

The Navigation component accepts a list of items defining all the routes to render and a data prop defining the current pathname and linkComponent. The linkComponent must accept and forward a ref and accept an href. When an item's href matches the current pathname, it will gain the active styles.

The Navigation component must be wrapped in a <nav> with an accessible label when not using the LayoutNav component.

import { card } from "@react-md/core/card/styles";
import { Navigation } from "@react-md/core/navigation/Navigation";
import { type NavigationItem } from "@react-md/core/navigation/types";
import { useNavigationExpansion } from "@react-md/core/navigation/useNavigationExpansion";
import {
  type ReactElement,
  type AnchorHTMLAttributes,
  forwardRef,
} from "react";
import { Tooltip } from "@react-md/core/tooltip/Tooltip";
import { useTooltip } from "@react-md/core/tooltip/useTooltip";

const items: readonly NavigationItem[] = [
  {
    type: "route",
    href: "/",
    children: "Home",
  },
  {
    type: "route",
    href: "/route-1",
    children: "Route 1",
  },
  {
    type: "route",
    href: "/route-2",
    children: "Route 2",
  },
];

export default function SimpleExample(): ReactElement {
  const { data } = useNavigationExpansion({
    pathname: "/",
    linkComponent: FakeLink,
  });
  return (
    <nav aria-label="Fake Navigation" className={card()}>
      <Navigation data={data} items={items} />
    </nav>
  );
}

export interface FakeLinkProps extends AnchorHTMLAttributes<HTMLAnchorElement> {
  href: string;
}

export const FakeLink = forwardRef<HTMLAnchorElement, FakeLinkProps>(
  function HashLink(props, _ref) {
    const { href, ...remaining } = props;
    const { elementProps, tooltipProps } = useTooltip({
      ...props,
      hoverTimeout: 0,
      defaultPosition: "right",
    });

    // do not forward the ref so the link is not scrolled into view for demos
    // since it scrolls the entire page instead of a navigation panel
    return (
      <>
        <span {...remaining} {...elementProps} />
        <Tooltip {...tooltipProps}>href: {href}</Tooltip>
      </>
    );
  },
);

Press Enter to start editing.

Adding Icons

Icons can be added before or after the children by using the beforeAddon and afterAddon props respectively.

import { card } from "@react-md/core/card/styles";
import { Navigation } from "@react-md/core/navigation/Navigation";
import { type NavigationItem } from "@react-md/core/navigation/types";
import { useNavigationExpansion } from "@react-md/core/navigation/useNavigationExpansion";
import FavoriteIcon from "@react-md/material-icons/FavoriteIcon";
import HomeIcon from "@react-md/material-icons/HomeIcon";
import StarIcon from "@react-md/material-icons/StarIcon";
import {
  type ReactElement,
  type AnchorHTMLAttributes,
  forwardRef,
} from "react";
import { Tooltip } from "@react-md/core/tooltip/Tooltip";
import { useTooltip } from "@react-md/core/tooltip/useTooltip";

const items: readonly NavigationItem[] = [
  {
    type: "route",
    href: "/",
    children: "Home",
    beforeAddon: <HomeIcon />,
  },
  {
    type: "route",
    href: "/route-1",
    children: "Route 1",
    beforeAddon: <StarIcon />,
  },
  {
    type: "route",
    href: "/route-2",
    children: "Route 2",
    beforeAddon: <FavoriteIcon />,
    afterAddon: <FavoriteIcon />,
  },
];

export default function AddingIconsExample(): ReactElement {
  const { data } = useNavigationExpansion({
    pathname: "/",
    linkComponent: FakeLink,
  });
  return (
    <nav aria-label="Fake Navigation" className={card()}>
      <Navigation data={data} items={items} />
    </nav>
  );
}

export interface FakeLinkProps extends AnchorHTMLAttributes<HTMLAnchorElement> {
  href: string;
}

export const FakeLink = forwardRef<HTMLAnchorElement, FakeLinkProps>(
  function HashLink(props, _ref) {
    const { href, ...remaining } = props;
    const { elementProps, tooltipProps } = useTooltip({
      ...props,
      hoverTimeout: 0,
      defaultPosition: "right",
    });

    // do not forward the ref so the link is not scrolled into view for demos
    // since it scrolls the entire page instead of a navigation panel
    return (
      <>
        <span {...remaining} {...elementProps} />
        <Tooltip {...tooltipProps}>href: {href}</Tooltip>
      </>
    );
  },
);

Press Enter to start editing.

Adding Dividers and Subheaders

Dividers and subheaders can be rendered by setting the type to "divider" or "subheader" respectively.

import { card } from "@react-md/core/card/styles";
import { Navigation } from "@react-md/core/navigation/Navigation";
import { type NavigationItem } from "@react-md/core/navigation/types";
import { useNavigationExpansion } from "@react-md/core/navigation/useNavigationExpansion";
import {
  type ReactElement,
  type AnchorHTMLAttributes,
  forwardRef,
} from "react";
import { Tooltip } from "@react-md/core/tooltip/Tooltip";
import { useTooltip } from "@react-md/core/tooltip/useTooltip";

const items: readonly NavigationItem[] = [
  {
    type: "route",
    href: "/",
    children: "Home",
  },
  {
    type: "route",
    href: "/route-1",
    children: "Route 1",
  },
  {
    type: "route",
    href: "/route-2",
    children: "Route 2",
  },
  { type: "divider" },
  { type: "subheader", children: "Subheader 1" },
  {
    type: "route",
    href: "/route-3",
    children: "Route 3",
  },
  {
    type: "route",
    href: "/route-4",
    children: "Route 4",
  },
];

export default function AddingDividersAndSubheadersExample(): ReactElement {
  const { data } = useNavigationExpansion({
    pathname: "/",
    linkComponent: FakeLink,
  });
  return (
    <nav aria-label="Fake Navigation" className={card()}>
      <Navigation data={data} items={items} />
    </nav>
  );
}

export interface FakeLinkProps extends AnchorHTMLAttributes<HTMLAnchorElement> {
  href: string;
}

export const FakeLink = forwardRef<HTMLAnchorElement, FakeLinkProps>(
  function HashLink(props, _ref) {
    const { href, ...remaining } = props;
    const { elementProps, tooltipProps } = useTooltip({
      ...props,
      hoverTimeout: 0,
      defaultPosition: "right",
    });

    // do not forward the ref so the link is not scrolled into view for demos
    // since it scrolls the entire page instead of a navigation panel
    return (
      <>
        <span {...remaining} {...elementProps} />
        <Tooltip {...tooltipProps}>href: {href}</Tooltip>
      </>
    );
  },
);

Press Enter to start editing.

Collapsible Groups

A group of routes can be created by creating an item with type: "group". If an href is also included in this item, all child routes' href will be prefixed with the group href.

import { card } from "@react-md/core/card/styles";
import { Navigation } from "@react-md/core/navigation/Navigation";
import { type NavigationItem } from "@react-md/core/navigation/types";
import { useNavigationExpansion } from "@react-md/core/navigation/useNavigationExpansion";
import {
  type ReactElement,
  type AnchorHTMLAttributes,
  forwardRef,
} from "react";
import { Tooltip } from "@react-md/core/tooltip/Tooltip";
import { useTooltip } from "@react-md/core/tooltip/useTooltip";

const items: readonly NavigationItem[] = [
  {
    type: "route",
    href: "/",
    children: "Home",
  },
  {
    type: "group",
    href: "/route-1",
    children: "Prefixed Href",
    items: [
      {
        type: "route",
        href: "/page-1",
        children: "Page 1",
      },
      {
        type: "route",
        href: "/page-2",
        children: "Page 2",
      },
      {
        type: "route",
        href: "/page-3",
        children: "Page 3",
      },
    ],
  },
  {
    type: "group",
    children: "No Prexed Href",
    items: [
      {
        type: "route",
        href: "/page-1",
        children: "Page 1",
      },
      {
        type: "route",
        href: "/page-2",
        children: "Page 2",
      },
      {
        type: "route",
        href: "/page-3",
        children: "Page 3",
      },
    ],
  },
  {
    type: "group",
    href: "/multi",
    children: "Multiple Levels",
    items: [
      {
        type: "route",
        href: "/page-1",
        children: "Page 1",
      },
      {
        type: "group",
        href: "/level-2",
        children: "Level 2",
        items: [
          {
            type: "route",
            href: "/page-1",
            children: "Page 1",
          },
        ],
      },
    ],
  },
];

export default function CollapsibleGroupsExample(): ReactElement {
  const { data } = useNavigationExpansion({
    pathname: "/route-1/page-2",
    linkComponent: FakeLink,
  });
  return (
    <nav aria-label="Fake Navigation" className={card()}>
      <Navigation data={data} items={items} />
    </nav>
  );
}

export interface FakeLinkProps extends AnchorHTMLAttributes<HTMLAnchorElement> {
  href: string;
}

export const FakeLink = forwardRef<HTMLAnchorElement, FakeLinkProps>(
  function HashLink(props, _ref) {
    const { href, ...remaining } = props;
    const { elementProps, tooltipProps } = useTooltip({
      ...props,
      hoverTimeout: 0,
      defaultPosition: "right",
    });

    // do not forward the ref so the link is not scrolled into view for demos
    // since it scrolls the entire page instead of a navigation panel
    return (
      <>
        <span {...remaining} {...elementProps} />
        <Tooltip {...tooltipProps}>href: {href}</Tooltip>
      </>
    );
  },
);

Press Enter to start editing.

Routing Libraries and Frameworks

Nextjs Example

It is recommended to create a simple LinkUnstyled component which can be customized and used throughout the app:

src/components/LinkUnstyled.tsx
import Link, { type LinkProps } from "next/link.js";
import { type AnchorHTMLAttributes, forwardRef } from "react";

export interface LinkUnstyledProps
  extends LinkProps,
    AnchorHTMLAttributes<HTMLAnchorElement> {
  href: string;
}

export const LinkUnstyled = forwardRef<HTMLAnchorElement, LinkUnstyledProps>(
  function LinkUnstyled(props, ref) {
    if (props.href.endsWith(".html")) {
      return <a {...props} ref={ref} />;
    }

    return <Link {...props} ref={ref} />;
  }
);

Then use the LinkUnstyled as the linkComponent and pass the current pathname with the usePathname hook:

src/components/Navigation.tsx
"use client";

import { Navigation } from "@react-md/core/navigation/Navigation";
import { type NavigationItem } from "@react-md/core/navigation/types";
import { useNavigationExpansion } from "@react-md/core/navigation/useNavigationExpansion";
import { usePathname } from "next/navigation.js";
import { type ReactElement } from "react";

import { LinkUnstyled } from "@/components/LinkUnstyled.jsx";

const items: readonly NavigationItem[] = [
  {
    type: "route",
    href: "/",
    children: "Home",
  },
  {
    type: "route",
    href: "/page-1",
    children: "Page 1",
  },
];

export function MainNavigation(): ReactElement {
  const pathname = usePathname();
  const { data } = useNavigationExpansion({
    pathname,
    linkComponent: LinkUnstyled,
  });
  return (
    <nav aria-label="Navigation">
      <Navigation data={data} items={items} />
    </nav>
  );
}

React Router Example

It is recommended to create a simple LinkUnstyled component which can be customized and used throughout the app:

import { Link, type LinkProps } from "react-router-dom";

export interface LinkUnstyledProps extends Omit<LinkProps, "to"> {
  href: string;
}

export const LinkUnstyled = forwardRef<HTMLAnchorElement, LinkUnstyledProps>(
  function LinkUnstyled(props, ref) {
    const { href, ...remaining } = props;

    return <Link {...remaining} to={href} ref={ref} />;
  }
);

Then use the LinkUnstyled as the linkComponent and pass the current pathname with the useHref hook:

import { Navigation } from "@react-md/core/navigation/Navigation";
import { type NavigationItem } from "@react-md/core/navigation/types";
import { type ReactElement } from "react";
import { useHref } from "react-router-dom";

import { LinkUnstyled } from "@/components/LinkUnstyled.jsx";

const items: readonly NavigationItem[] = [
  {
    type: "route",
    href: "/",
    children: "Home",
  },
  {
    type: "route",
    href: "/page-1",
    children: "Page 1",
  },
];

export function MainNavigation(): ReactElement {
  const pathname = useHref();
  return (
    <nav aria-label="Navigation">
      <Navigation
        data={{ pathname, linkComponent: LinkUnstyled }}
        items={items}
      />
    </nav>
  );
}

Disclaimer: I haven't actually tried upgrading to react-router-dom@v6.x.x yet.