Skip to main navigationSkip to main content
Return to Sky UI homepage

Navigation Bar - React

import { NavBar } from "@sky-uk/ui-core";

Props

PropTypeDefaultDescription
$appearancestringdefault

The $appearance prop adjusts the NavBar styles for various backgrounds, including a light version for dark backgrounds. It's responsive, allowing breakpoint-specific adjustments.

'default' | 'light'

$fillbooleanfalse
Dictates whether the NavBar proportionately fill the available space. This prop is responsive and can be used to set the $fill value at different breakpoints.
asstringnav
The as prop determines the semantic element used for the component. nav is a standard navigation landmark, tabs is used for tabbed interfaces, and anchor is for anchor link navigation.
activeIndexnumberControls which item is active using a zero-based index. Use this for a controlled NavBar.
onItemChange(index: number) => void
The onItemChange callback triggers when an item is changed, providing the index of the changed item as a number. This enables custom actions based on the specific item interaction.
onItemTrigger(nextIndex: number, currentIndex: number) => boolean | Promise<boolean>
The onItemTrigger callback is triggered when an item is triggered. It receives the index of the item that is being triggered and the index of the currently active item. This callback can return a boolean or a Promise that resolves to a boolean to allow or prevent the activation from proceeding.
PropTypeDefaultDescription
$activebooleanSets the initially active item when the NavBar is uncontrolled.
$hoverbooleanDisplays the item in its hover state.
$focusedbooleanDisplays the item in its focused state.

Basic NavBar

<NavBar>
  <NavBar.Item href="#">
    One
  </NavBar.Item>
  <NavBar.Item href="#">
    Two
  </NavBar.Item>
  <NavBar.Item href="#">
    Three
  </NavBar.Item>
  <NavBar.Item href="#">
    Four
  </NavBar.Item>
</NavBar>

NavBar can be used for page navigation by passing href props to NavBar.Item. In this example, the component is rendered as a nav element containing links to different pages.

For accessibility, set aria-current="page" on the active item. This indicates to users that they are on the page of the current nav item.

() => {
  const [activeIndex, setActiveIndex] = React.useState(0);
  const navItems = [
    { label: "Home", href: "https://www.sky.com" },
    { label: "Watch", href: "https://www.sky.com/watch" },
    { label: "TV", href: "https://www.sky.com/tv" },
    { label: "Glass", href: "https://www.sky.com/glass" },
    { label: "Broadband", href: "https://www.sky.com/broadband" },
    { label: "Mobile", href: "https://www.sky.com/shop/mobile" },
  ];

  return (
    <NavBar activeIndex={activeIndex} onItemChange={setActiveIndex}>
      {navItems.map((item, idx) => (
        <NavBar.Item
          key={item.label}
          href={item.href}
          target="_blank"
          rel="noopener"
          aria-current={activeIndex === idx ? "page" : undefined}
        >
          {item.label}
        </NavBar.Item>
      ))}
    </NavBar>
  );
}

Router Components

Use the component prop when you want NavBar.Item to render a framework-agnostic routing component such as React Router Link or Next.js Link.

This keeps the NavBar behavior the same while letting the routing component control navigation.

import { Link } from "react-router-dom";
import NextLink from "next/link";
<NavBar>
<NavBar.Item component={Link} to="/dashboard">
Dashboard
</NavBar.Item>
<NavBar.Item component={NextLink} href="/account">
Account
</NavBar.Item>
<NavBar.Item href="/support">Support</NavBar.Item>
</NavBar>;

In-Page Navigation

NavBar can be used for in-page anchor navigation by setting the as prop to "anchor" and providing href props that correspond to section IDs on the page. Clicking an item will smoothly scroll to the associated section. The active item updates based on the currently visible section as you scroll.

For accessibility, set aria-current="location" on the active item. This indicates that the active nav item is in view.

Please view this example in fullscreen mode. Click the full screen button above.

Tab Style Navigation

NavBar can be used as a tabs bar by setting the as prop to "tabs". In this mode, the component's semantics change: it renders as a div with role="tablist", and each NavBar.Item receives role=tab. In this example, clicking on a tab updates the active state and displays corresponding content below the NavBar.

For accessibility:

  • Set aria-selected="true" on the active tab item to indicate it is selected.
  • Each tab (NavBar.Item) should have an aria-controls attribute pointing to the ID of its associated tab panel.
  • Each tab panel (the content area with role="tabpanel") should have an aria-labelledby attribute pointing to the ID of its corresponding tab.
() => {
  const sections = [
    { id: "overview", label: "Overview", color: "grey10" },
    { id: "features", label: "Features", color: "grey20" },
    { id: "pricing", label: "Pricing", color: "grey40" },
    { id: "testimonials", label: "Testimonials", color: "grey50" },
    { id: "faq", label: "FAQ", color: "grey70" },
    { id: "contact", label: "Contact", color: "grey80" },
  ];
  const [activeItem, setActiveItem] = React.useState(sections[0].label);

  return (
    <>
      <NavBar as="tabs">
        {sections.map((section) => (
          <NavBar.Item
            key={section.id}
            onClick={() => setActiveItem(section.label)}
            aria-selected={activeItem === section.label ? "true" : undefined}
            id={`tab-${section.id}`}
            aria-controls={`section-${section.id}`}
          >
            {section.label}
          </NavBar.Item>
        ))}
      </NavBar>
      {activeItem && (
        <Box
          $alignItems="center"
          $bgColor={sections.find((s) => s.label === activeItem)?.color || "transparent"}
          $display="flex"
          $height="400px"
          $justifyContent="center"
          role="tabpanel"
          id={`section-${sections.find((s) => s.label === activeItem)?.id}`}
          aria-labelledby={`tab-${sections.find((s) => s.label === activeItem)?.id}`}
        >
          <Text $fontSize="display-2" $color="white">
            {activeItem}
          </Text>
        </Box>
      )}
    </>
  );
}

Callbacks

onItemChange

The onItemChange callback is triggered when a item is changed. It receives the index of the changed item as a number. This allows you to determine which item was activated and perform additional actions or updates based on the selected item.

function Example(){
  const [itemState, setItemState] = React.useState(0);

  const handleItemChange = index => {
    setItemState(index);
  };

  return (
    <Flex $flexDirection="column" $gap={5}>
      <Text>Current Tab index: {itemState}</Text>
      <NavBar onItemChange={handleItemChange}>
        <NavBar.Item $active>Video Doorbell</NavBar.Item>
        <NavBar.Item>Indoor Camera</NavBar.Item>
        <NavBar.Item>Motion & Contact Sensors</NavBar.Item>
      </NavBar>
    </Flex>
  );
}

onItemTrigger

The onItemTrigger callback requires a promise to be resolved before proceeding with navigation. In this example, when an item is clicked, a modal appears asking if the item change should proceed. Clicking Allow continues, Block cancels.

function Example(){
  const [modalOpen, setModalOpen] = React.useState(false);

    const sections = [
      { id: "broadband", label: "Broadband", icon: wifiHouseLinear, color: "grey50" },
      { id: "tv", label: "TV", icon: tvLinear, color: "grey70" },
      { id: "mobile", label: "Mobile", icon: mobileLinear, color: "grey80"},
    ];

    const resolveRef = React.useRef();

    const onItemTrigger = () => {
      setModalOpen(true);
      return new Promise((resolve) => {
        resolveRef.current = resolve;
      });
    };

    const handleModalResult = (result) => {
      if (resolveRef.current) {
        resolveRef.current(result);
        resolveRef.current = undefined;
      }
      setModalOpen(false);
    };

    const [activeItem, setActiveItem] = React.useState(sections[0].label);

    return (
      <>
        <NavBar as='tabs' onItemTrigger={onItemTrigger}>
          {sections.map((section) => (
            <NavBar.Item
              key={section.id}
              onClick={() => setActiveItem(section.label)}
              aria-selected={activeItem === section.label ? 'true' : undefined}
            >
              <Icon src={section.icon} $size={{ xs: 'medium', xl: 'large' }} />
              {section.label}
            </NavBar.Item>
          ))}
        </NavBar>
        {activeItem && (
          <Box
            $alignItems="center"
            $bgColor={sections.find((s) => s.label === activeItem)?.color || "transparent"}
            $display="flex"
            $height="400px"
            $justifyContent="center"
            role="tabpanel"
            id={`section-${sections.find((s) => s.label === activeItem)?.id}`}
            aria-labelledby={`tab-${sections.find((s) => s.label === activeItem)?.id}`}
          >
            <Text $fontSize='display-2' $color='white'>
              {activeItem}
            </Text>
          </Box>
        )}
        {modalOpen && (
          <Modal onClose={() => handleModalResult(false)}>
            <Modal.Content>
              <Flex
                $flexDirection='column'
                $gap={4}
                $justifyContent='center'
                $alignItems='center'
                $width='400px'
                $height='250px'
              >
                <Text $fontSize='display-6'>Allow nav?</Text>
                <Flex $gap={2} $justifyContent='center'>
                  <Button $variant='primary' onClick={() => handleModalResult(true)}>
                    Allow
                  </Button>
                  <Button $variant='secondary' onClick={() => handleModalResult(false)}>
                    Block
                  </Button>
                </Flex>
              </Flex>
            </Modal.Content>
          </Modal>
        )}
      </>
  );
}

System Modifiers

The NavBar component supports the props applied using the following system modifiers:

Navigation Bar - React | Sky UI Design System