Skip to content

Navigation

Displays a Navigation component that visualizes the page routing structure and allows users to navigate between different pages.

Basic Usage

To implement the Navigation component, you need to import it first:

import { Navigation } from 'rades_react';

And use it:

React.createElement(() => {
  const [path, setPath] = React.useState("/");
  return (
    <Navigation
      aria-label="Main"
      currentPath={path}
      items={[
        {
          label: 'Dashboard',
          path: '/',
        },
        {
          label: 'Settings',
          path: '/settings',
        },
        {
          label: 'Diagnostics',
          path: '/diagnostics',
        },
        {
          label: 'Users',
          path: '/users',
        },
      ]}
      navigate={(path) => setPath(path)}
      scrollToTopOnNavigate={false}
    />
  );
})

See API for all available options.

General Guidelines

  • Keep the navigation structure clear and logical. Group related items together and use descriptive labels that clearly indicate the destination.

  • Consider the depth of nesting. While Navigation supports up to 5 levels of nesting, try to keep your navigation structure as shallow as possible. Deep nesting can make navigation more difficult for users.

  • Use consistent labeling. Keep labels short and use consistent terminology throughout your navigation to help users understand where they are and where they can go.

  • Plan for mobile. Configure the responsive breakpoint via the mobileBreakpoint prop or the --rcm-navigation-breakpoint CSS custom property to ensure your navigation adapts appropriately for smaller screens. The component automatically switches to a bottom bar layout below the breakpoint, where first-level items open dropdowns to display the second level.

Responsive Behavior

The Navigation component automatically switches between desktop and mobile layouts based on the breakpoint configuration.

  • Desktop layout: Vertical collapsible stack
  • Mobile layout: Horizontal bottom bar (first level items open dropdowns to display the second level)

Configuring the Breakpoint

The component determines the mobile breakpoint in the following order:

  1. mobileBreakpoint prop — If explicitly provided with a valid unit, this value is used.
  2. CSS custom property — Reads --rcm-navigation-breakpoint from current context (must have explicit unit).
  3. Default fallback — Uses 768 px if neither of the above is set.

⚠️ The mobileBreakpoint prop and CSS custom property must include an explicit unit (px, em, or rem). Unitless values are rejected.

Supported Units

  • px - Pixels (e.g., "768px", "1024px")
  • em - Relative to root font size (e.g., "48em" = 768 px with 16 px root font size)
  • rem - Relative to root font size (e.g., "48rem" = 768 px with 16 px root font size)

Examples

/* Set breakpoint globally via CSS (various unit formats supported) */
:root {
  --rcm-navigation-breakpoint: 1024px;  /* Explicit pixels */
  --rcm-navigation-breakpoint: 64em;    /* Relative to root font size */
  --rcm-navigation-breakpoint: 64rem;   /* Root-relative unit */
}
/* Override per component instance with different units */
<Navigation
  aria-label="Main"
  currentPath={path}
  items={items}
  navigate={setPath}
  mobileBreakpoint="1024px" // Must be string with unit
/>

/* Using em units (48 em = 768 px with 16 px root font size) */
<Navigation
  aria-label="Main"
  currentPath={path}
  items={items}
  navigate={setPath}
  mobileBreakpoint="48em"
/>

/* Using rem units */
<Navigation
  aria-label="Main"
  currentPath={path}
  items={items}
  navigate={setPath}
  mobileBreakpoint="48rem"
/>

Try toggling the buttons below to see how the navigation adapts to different screen sizes:

React.createElement(() => {
  const [path, setPath] = React.useState("/");
  const [breakpointMode, setBreakpointMode] = React.useState('responsive');

  const mobileBreakpointMap = {
    responsive: undefined,
    mobile: '7680px', // 8k, maximum allowed width
    desktop: '0px',
  };

  return (
    <>
      <ButtonGroup>
        <Button
          color={breakpointMode === 'responsive' ? 'selected' : 'secondary'}
          label="Responsive"
          onClick={() => setBreakpointMode('responsive')}
        />
        <Button
          color={breakpointMode === 'mobile' ? 'selected' : 'secondary'}
          label="Force mobile"
          onClick={() => setBreakpointMode('mobile')}
        />
        <Button
          color={breakpointMode === 'desktop' ? 'selected' : 'secondary'}
          label="Force desktop"
          onClick={() => setBreakpointMode('desktop')}
        />
      </ButtonGroup>
      <div
        style={{
          display: 'grid',
          minHeight: '250px',
          marginTop: '1rem',
          overflow: 'hidden',
          position: 'relative',
          zIndex: 2,
        }}
      >
        <Navigation
          aria-label="Main"
          currentPath={path}
          items={[
            {
              label: 'Dashboard',
              path: '/',
            },
            {
              label: 'Settings',
              path: '/settings',
            },
            {
              label: 'Diagnostics',
              path: '/diagnostics',
            },
            {
              label: 'Users',
              path: '/users',
            },
          ]}
          mobileBreakpoint={mobileBreakpointMap[breakpointMode]}
          navigate={(path) => setPath(path)}
          scrollToTopOnNavigate={false}
        />
      </div>
    </>
  );
})

In your application, simply set the mobileBreakpoint prop to match your responsive design breakpoints, and the component will handle the rest.

Icons can be added to navigation items to improve visual clarity and help users quickly identify different sections. Icons are supported in the first and second level of navigation.

When hasIcons is enabled, items without an icon property will display a fallback icon automatically.

React.createElement(() => {
  const [path, setPath] = React.useState("/");
  const [breakpointMode, setBreakpointMode] = React.useState('responsive');

  const mobileBreakpointMap = {
    responsive: undefined,
    mobile: '7680px', // 8k, maximum allowed width
    desktop: '0px',
  };

  return (
    <>
      <ButtonGroup>
        <Button
          color={breakpointMode === 'responsive' ? 'selected' : 'secondary'}
          label="Responsive"
          onClick={() => setBreakpointMode('responsive')}
        />
        <Button
          color={breakpointMode === 'mobile' ? 'selected' : 'secondary'}
          label="Force mobile"
          onClick={() => setBreakpointMode('mobile')}
        />
        <Button
          color={breakpointMode === 'desktop' ? 'selected' : 'secondary'}
          label="Force desktop"
          onClick={() => setBreakpointMode('desktop')}
        />
      </ButtonGroup>
      <div
        style={{
          display: 'grid',
          minHeight: '250px',
          marginTop: '1rem',
          overflow: 'hidden',
          position: 'relative',
          zIndex: 2,
        }}
      >
        <Navigation
          aria-label="Main"
          currentPath={path}
          hasIcons
          items={[
            {
              icon: 'dashboard',
              label: 'Dashboard',
              path: '/',
            },
            {
              icon: 'cogwheels',
              label: 'Settings',
              path: '/settings',
            },
            {
              icon: 'stethoscope',
              label: 'Diagnostics',
              path: '/diagnostics',
            },
            {
              label: 'Users',
              path: '/users',
            },
          ]}
          mobileBreakpoint={mobileBreakpointMap[breakpointMode]}
          navigate={(path) => setPath(path)}
          scrollToTopOnNavigate={false}
        />
      </div>
    </>
  );
})

👉 Notice how the "Users" item displays a fallback icon since no icon property was specified.

Nested Navigation

Navigation supports hierarchical navigation structures. Parent items can contain nested children to organize related pages together. Items with children expand inline within the collapsible stack. On mobile, first-level items open a dropdown to display the second level, and from there the same collapsible stack is used.

React.createElement(() => {
  const [path, setPath] = React.useState("/settings/account/password");
  const [breakpointMode, setBreakpointMode] = React.useState('responsive');

  const mobileBreakpointMap = {
    responsive: undefined,
    mobile: '7680px', // 8k, maximum allowed width
    desktop: '0px',
  };

  return (
    <>
      <ButtonGroup>
        <Button
          color={breakpointMode === 'responsive' ? 'selected' : 'secondary'}
          label="Responsive"
          onClick={() => setBreakpointMode('responsive')}
        />
        <Button
          color={breakpointMode === 'mobile' ? 'selected' : 'secondary'}
          label="Force mobile"
          onClick={() => setBreakpointMode('mobile')}
        />
        <Button
          color={breakpointMode === 'desktop' ? 'selected' : 'secondary'}
          label="Force desktop"
          onClick={() => setBreakpointMode('desktop')}
        />
      </ButtonGroup>
      <div
        style={{
          display: 'grid',
          minHeight: '450px',
          marginTop: '1rem',
          overflow: 'hidden',
          position: 'relative',
          zIndex: 2,
        }}
      >
        <Navigation
          aria-label="Main"
          currentPath={path}
          hasIcons
          items={[
            {
              icon: 'dashboard',
              label: 'Dashboard',
              path: '/',
            },
            {
              icon: 'cogwheels',
              label: 'Settings',
              items: [
                {
                  icon: 'adjust',
                  label: 'Appearance',
                  path: '/settings/appearance',
                },
                {
                  icon: 'globe',
                  label: 'Networks',
                  path: '/settings/networks',
                },
                {
                  icon: 'key',
                  label: 'Account',
                  items: [
                    {
                      label: 'Profile',
                      path: '/settings/account/profile',
                    },
                    {
                      label: 'Password',
                      path: '/settings/account/password',
                    },
                    {
                      label: 'Security',
                      path: '/settings/account/security',
                    },
                  ],
                },
              ],
            },
            {
              icon: 'stethoscope',
              label: 'Diagnostics',
              path: '/diagnostics',
            },
          ]}
          mobileBreakpoint={mobileBreakpointMap[breakpointMode]}
          navigate={(path) => setPath(path)}
          scrollToTopOnNavigate={false}
        />
      </div>
    </>
  );
})

Deep Nesting

Navigation supports up to 5 levels of nesting to accommodate complex application structures. This example demonstrates the maximum nesting depth.

React.createElement(() => {
  const [path, setPath] = React.useState("/settings/account/security/backup/codes");
  const [breakpointMode, setBreakpointMode] = React.useState('responsive');

  const mobileBreakpointMap = {
    responsive: undefined,
    mobile: '7680px', // 8k, maximum allowed width
    desktop: '0px',
  };

  return (
    <>
      <ButtonGroup>
        <Button
          color={breakpointMode === 'responsive' ? 'selected' : 'secondary'}
          label="Responsive"
          onClick={() => setBreakpointMode('responsive')}
        />
        <Button
          color={breakpointMode === 'mobile' ? 'selected' : 'secondary'}
          label="Force mobile"
          onClick={() => setBreakpointMode('mobile')}
        />
        <Button
          color={breakpointMode === 'desktop' ? 'selected' : 'secondary'}
          label="Force desktop"
          onClick={() => setBreakpointMode('desktop')}
        />
      </ButtonGroup>
      <div
        style={{
          display: 'grid',
          minHeight: '650px',
          marginTop: '1rem',
          overflow: 'hidden',
          position: 'relative',
          zIndex: 2,
        }}
      >
        <Navigation
          aria-label="Main"
          currentPath={path}
          hasIcons
          items={[
            {
              icon: 'dashboard',
              label: 'Dashboard',
              path: '/',
            },
            {
              icon: 'cogwheels',
              label: 'Settings',
              items: [
                {
                  icon: 'adjust',
                  label: 'Appearance',
                  path: '/settings/appearance',
                },
                {
                  icon: 'globe',
                  label: 'Networks',
                  path: '/settings/networks',
                },
                {
                  icon: 'key',
                  label: 'Account',
                  items: [
                    {
                      label: 'Password',
                      path: '/settings/account/password',
                    },
                    {
                      label: 'Profile',
                      path: '/settings/account/profile',
                    },
                    {
                      label: 'Security',
                      items: [
                        {
                          label: '2FA Setup',
                          path: '/settings/account/security/2fa',
                        },
                        {
                          label: 'Backup Options',
                          items: [
                            {
                              label: 'Download Codes',
                              path: '/settings/account/security/backup/codes',
                            },
                            {
                              label: 'Email Recovery',
                              path: '/settings/account/security/backup/email',
                            },
                          ],
                        },
                      ],
                    },
                  ],
                },
              ],
            },
            {
              icon: 'stethoscope',
              label: 'Diagnostics',
              path: '/diagnostics',
            },
          ]}
          mobileBreakpoint={mobileBreakpointMap[breakpointMode]}
          navigate={(path) => setPath(path)}
          scrollToTopOnNavigate={false}
        />
      </div>
    </>
  );
})

Item States

Navigation items can have different states to communicate their status to users.

Active Items

The active state is automatically applied to navigation items based on the currentPath prop. When the current path matches an item's path property, that item is visually highlighted as active. This helps users understand their current location within the application.

👉 If a nested item is active, the navigation tree is automatically uncollapsed to show that item. This only applies to the stack mode (i.e., on desktop and inside dropdowns on mobile).

React.createElement(() => {
  const [path, setPath] = React.useState("/settings/account/profile");
  const [breakpointMode, setBreakpointMode] = React.useState('responsive');

  const mobileBreakpointMap = {
    responsive: undefined,
    mobile: '7680px', // 8k, maximum allowed width
    desktop: '0px',
  };

  return (
    <>
      <ButtonGroup>
        <Button
          color={breakpointMode === 'responsive' ? 'selected' : 'secondary'}
          label="Responsive"
          onClick={() => setBreakpointMode('responsive')}
        />
        <Button
          color={breakpointMode === 'mobile' ? 'selected' : 'secondary'}
          label="Force mobile"
          onClick={() => setBreakpointMode('mobile')}
        />
        <Button
          color={breakpointMode === 'desktop' ? 'selected' : 'secondary'}
          label="Force desktop"
          onClick={() => setBreakpointMode('desktop')}
        />
      </ButtonGroup>
      <div
        style={{
          display: 'grid',
          minHeight: '450px',
          marginTop: '1rem',
          overflow: 'hidden',
          position: 'relative',
          zIndex: 2,
        }}
      >
        <Navigation
          aria-label="Main"
          currentPath={path}
          hasIcons
          items={[
            {
              icon: 'dashboard',
              label: 'Dashboard',
              path: '/',
            },
            {
              icon: 'cogwheels',
              label: 'Settings',
              items: [
                {
                  icon: 'adjust',
                  label: 'Appearance',
                  path: '/settings/appearance',
                },
                {
                  icon: 'globe',
                  label: 'Networks',
                  path: '/settings/networks',
                },
                {
                  icon: 'key',
                  label: 'Account',
                  items: [
                    {
                      label: 'Profile',
                      path: '/settings/account/profile',
                    },
                    {
                      label: 'Password',
                      path: '/settings/account/password',
                    },
                    {
                      label: 'Security',
                      path: '/settings/account/security',
                    },
                  ],
                },
              ],
            },
            {
              icon: 'stethoscope',
              label: 'Diagnostics',
              path: '/diagnostics',
            },
          ]}
          mobileBreakpoint={mobileBreakpointMap[breakpointMode]}
          navigate={(path) => setPath(path)}
          scrollToTopOnNavigate={false}
        />
      </div>
    </>
  );
})

Disabled Items

Disabled items are visible but not interactive. Use this state for navigation items that are temporarily unavailable or not applicable in the current context.

React.createElement(() => {
  const [path, setPath] = React.useState("/");
  const [breakpointMode, setBreakpointMode] = React.useState('responsive');

  const mobileBreakpointMap = {
    responsive: undefined,
    mobile: '7680px', // 8k, maximum allowed width
    desktop: '0px',
  };

  return (
    <>
      <ButtonGroup>
        <Button
          color={breakpointMode === 'responsive' ? 'selected' : 'secondary'}
          label="Responsive"
          onClick={() => setBreakpointMode('responsive')}
        />
        <Button
          color={breakpointMode === 'mobile' ? 'selected' : 'secondary'}
          label="Force mobile"
          onClick={() => setBreakpointMode('mobile')}
        />
        <Button
          color={breakpointMode === 'desktop' ? 'selected' : 'secondary'}
          label="Force desktop"
          onClick={() => setBreakpointMode('desktop')}
        />
      </ButtonGroup>
      <div
        style={{
          display: 'grid',
          minHeight: '250px',
          marginTop: '1rem',
          overflow: 'hidden',
          position: 'relative',
          zIndex: 2,
        }}
      >
        <Navigation
          aria-label="Main"
          currentPath={path}
          hasIcons
          items={[
            {
              icon: 'dashboard',
              label: 'Dashboard',
              path: '/',
            },
            {
              disabled: true,
              icon: 'cogwheels',
              label: 'Settings (Coming Soon)',
              path: '/settings',
            },
            {
              icon: 'stethoscope',
              label: 'Diagnostics',
              path: '/diagnostics',
            },
            {
              icon: 'old-man',
              label: 'Users',
              path: '/users',
            },
          ]}
          mobileBreakpoint={mobileBreakpointMap[breakpointMode]}
          navigate={(path) => setPath(path)}
          scrollToTopOnNavigate={false}
        />
      </div>
    </>
  );
})

Highlighted Items

Highlighted items draw attention to important actions or new features. Use the isHighlighted prop to emphasize specific navigation items.

👉 When an item is highlighted, all its parent items are automatically highlighted as well.

React.createElement(() => {
  const [path, setPath] = React.useState("/settings/account/security");
  const [breakpointMode, setBreakpointMode] = React.useState('responsive');

  const mobileBreakpointMap = {
    responsive: undefined,
    mobile: '7680px', // 8k, maximum allowed width
    desktop: '0px',
  };

  return (
    <>
      <ButtonGroup>
        <Button
          color={breakpointMode === 'responsive' ? 'selected' : 'secondary'}
          label="Responsive"
          onClick={() => setBreakpointMode('responsive')}
        />
        <Button
          color={breakpointMode === 'mobile' ? 'selected' : 'secondary'}
          label="Force mobile"
          onClick={() => setBreakpointMode('mobile')}
        />
        <Button
          color={breakpointMode === 'desktop' ? 'selected' : 'secondary'}
          label="Force desktop"
          onClick={() => setBreakpointMode('desktop')}
        />
      </ButtonGroup>
      <div
        style={{
          display: 'grid',
          minHeight: '400px',
          marginTop: '1rem',
          overflow: 'hidden',
          position: 'relative',
          zIndex: 2,
        }}
      >
        <Navigation
          aria-label="Main"
          currentPath={path}
          hasIcons
          items={[
            {
              icon: 'dashboard',
              label: 'Dashboard',
              path: '/',
            },
            {
              icon: 'cogwheels',
              label: 'Settings',
              items: [
                {
                  icon: 'adjust',
                  label: 'Appearance',
                  path: '/settings/appearance',
                },
                {
                  disabled: true,
                  icon: 'globe',
                  label: 'Networks (Coming Soon)',
                  path: '/settings/networks',
                },
                {
                  icon: 'key',
                  label: 'Account',
                  items: [
                    {
                      label: 'Profile',
                      path: '/settings/account/profile',
                    },
                    {
                      isHighlighted: true,
                      label: 'Setup 2FA',
                      path: '/settings/account/security',
                    },
                  ],
                },
              ],
            },
            {
              icon: 'stethoscope',
              label: 'Diagnostics',
              path: '/diagnostics',
            },
          ]}
          mobileBreakpoint={mobileBreakpointMap[breakpointMode]}
          navigate={(path) => setPath(path)}
          scrollToTopOnNavigate={false}
        />
      </div>
    </>
  );
})

Each navigation item can optionally include an id property, which will be rendered as the HTML id attribute on the corresponding <li> element. This is useful for easier element selection in test frameworks.

Usage Example

<Navigation
  aria-label="Main"
  currentPath={path}
  items={[
    {
      id: 'nav_dashboard',
      icon: 'dashboard',
      label: 'Dashboard',
      path: '/',
    },
    {
      id: 'nav_settings',
      icon: 'cogwheels',
      label: 'Settings',
      items: [
        {
          id: 'nav_settings_appearance',
          icon: 'adjust',
          label: 'Appearance',
          path: '/settings/appearance',
        },
        {
          id: 'nav_settings_account',
          icon: 'key',
          label: 'Account',
          path: '/settings/account',
        },
      ],
    },
  ]}
  navigate={setPath}
/>

Important Notes

  • The id property is optional. If not provided, no id attribute will be rendered on the <li> element.
  • Ensure all IDs are unique across your entire navigation structure to maintain valid HTML.
  • IDs are used as-is (no automatic prefixing), giving you full control over naming conventions.

Complete Example

Here's a comprehensive example combining multiple features: icons, nested navigation, disabled items, highlighted items, and state management.

React.createElement(() => {
  const [path, setPath] = React.useState("/");
  const [breakpointMode, setBreakpointMode] = React.useState('responsive');

  const mobileBreakpointMap = {
    responsive: undefined,
    mobile: '7680px', // 8k, maximum allowed width
    desktop: '0px',
  };

  return (
    <>
      <ButtonGroup>
        <Button
          color={breakpointMode === 'responsive' ? 'selected' : 'secondary'}
          label="Responsive"
          onClick={() => setBreakpointMode('responsive')}
        />
        <Button
          color={breakpointMode === 'mobile' ? 'selected' : 'secondary'}
          label="Force mobile"
          onClick={() => setBreakpointMode('mobile')}
        />
        <Button
          color={breakpointMode === 'desktop' ? 'selected' : 'secondary'}
          label="Force desktop"
          onClick={() => setBreakpointMode('desktop')}
        />
      </ButtonGroup>
      <div
        style={{
          display: 'grid',
          minHeight: '500px',
          marginTop: '1rem',
          overflow: 'hidden',
          position: 'relative',
          zIndex: 2,
        }}
      >
        <Navigation
          aria-label="Main"
          currentPath={path}
          hasIcons
          items={[
            {
              icon: 'dashboard',
              label: 'Dashboard',
              path: '/',
            },
            {
              icon: 'cogwheels',
              label: 'Settings',
              items: [
                {
                  icon: 'adjust',
                  label: 'Appearance',
                  path: '/settings/appearance',
                },
                {
                  disabled: true,
                  icon: 'remote-connection',
                  label: 'Interfaces',
                  path: '/settings/interfaces',
                },
                {
                  icon: 'globe',
                  label: 'Networks',
                  path: '/settings/networks',
                },
                {
                  icon: 'key',
                  label: 'Account',
                  items: [
                    {
                      label: 'Password',
                      path: '/settings/account/password',
                    },
                    {
                      label: 'Profile',
                      path: '/settings/account/profile',
                    },
                    {
                      isHighlighted: true,
                      label: 'Setup 2FA',
                      path: '/settings/account/security',
                    },
                  ],
                },
              ],
            },
            {
              icon: 'stethoscope',
              label: 'Diagnostics',
              path: '/diagnostics',
            },
            {
              icon: 'old-man',
              label: 'Users',
              path: '/users',
            },
          ]}
          mobileBreakpoint={mobileBreakpointMap[breakpointMode]}
          navigate={(path) => setPath(path)}
          scrollToTopOnNavigate={false}
        />
      </div>
    </>
  );
})

Scroll Behavior

By default, the Navigation component automatically scrolls the page to the top when navigating to a new item. This behavior can be disabled using the scrollToTopOnNavigate prop:

<Navigation
  currentPath={path}
  items={items}
  navigate={setPath}
  scrollToTopOnNavigate={false}
/>

When to disable: Useful in documentation pages or demos where multiple Navigation components are displayed together and automatic scrolling would disrupt the user experience.

Click-Outside Behavior

Mobile navigation dropdowns automatically close when clicking anywhere outside the dropdown boundaries. This includes clicks on elements with higher z-index than the dropdown backdrop, such as app headers or toolbars positioned above the navigation.

This behavior is automatic and works regardless of the z-index stacking context of your application layout. When clicking on interactive elements outside the dropdown (like buttons or links in your app header), both actions occur: the dropdown closes, and the clicked element's normal behavior executes (navigation, opening menus, etc.).

Accessibility

The Navigation component renders as a semantic <nav> element, which serves as a navigation landmark for assistive technologies. To help users understand the purpose of the navigation, you must provide an accessible label using the aria-label prop.

The mobile variant uses the native HTML <dialog> element to display the second-level navigation when first-level items are clicked. This provides built-in accessibility features including focus management and keyboard navigation.

<Navigation
  aria-label="Main"
  currentPath={path}
  items={items}
  navigate={setPath}
/>

Forwarding HTML Attributes

In addition to the options below in the component's API section, you can specify React synthetic events or any HTML attribute you like. All attributes that don't interfere with the API are forwarded to the root <nav> HTML element. This enables making the component interactive and helps to improve its accessibility.

👉 Refer to the MDN reference for the full list of supported attributes of the nav element.

API

Theming

Custom Property Description
--rcm-navigation-breakpoint Breakpoint for switching to mobile layout (in px, em or rem)