Tabs

Our Tabs components allow you to easily create accessible tabs where the content remains in the DOM when the tab is not currently visible. In order to achieve the mobile design, a conditional select field must be hidden and shown depending on screen size. Features include:

  • Makes use of the react-aria-components library to handle the tabs logic.
  • Content for all tabs remains in the DOM at all times (shouldForceMount={true} and className="data-[inert=true]:hidden" on the <TabPanel>)
  • Keyboard navigation and focus management
  • Supports vertical or horizontal orientation

Example

This is the content for tab 1 Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed a justo sapien. Nunc tempor, sem non feugiat vehicula, augue arcu feugiat ligula, quis efficitur nulla dui non magna. Mauris leo erat, suscipit id elit quis, sodales varius lacus. Ut tempus dignissim elit ut blandit. Aenean et dui ut metus lacinia ullamcorper quis non nisi. Etiam tincidunt sagittis scelerisque. Fusce consectetur accumsan sem non consequat. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Aenean ac sapien pharetra, aliquam leo sit amet, suscipit felis. Vivamus id malesuada justo. Quisque ultricies tortor sed massa sodales sodales.
This is the content for tab 2 Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed a justo sapien. Nunc tempor, sem non feugiat vehicula, augue arcu feugiat ligula, quis efficitur nulla dui non magna. Mauris leo erat, suscipit id elit quis, sodales varius lacus. Ut tempus dignissim elit ut blandit. Aenean et dui ut metus lacinia ullamcorper quis non nisi. Etiam tincidunt sagittis scelerisque. Fusce consectetur accumsan sem non consequat. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Aenean ac sapien pharetra, aliquam leo sit amet, suscipit felis. Vivamus id malesuada justo. Quisque ultricies tortor sed massa sodales sodales.
This is the content for tab 3 Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed a justo sapien. Nunc tempor, sem non feugiat vehicula, augue arcu feugiat ligula, quis efficitur nulla dui non magna. Mauris leo erat, suscipit id elit quis, sodales varius lacus. Ut tempus dignissim elit ut blandit. Aenean et dui ut metus lacinia ullamcorper quis non nisi. Etiam tincidunt sagittis scelerisque. Fusce consectetur accumsan sem non consequat. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Aenean ac sapien pharetra, aliquam leo sit amet, suscipit felis. Vivamus id malesuada justo. Quisque ultricies tortor sed massa sodales sodales.
 <Tabs
        selectedKey={selectedKey}
        onSelectionChange={(key) => setSelectedKey(String(key))}
      >
        <div className="relative mb-4 w-full md:hidden">
          <select
            value={selectedKey}
            onChange={(e) => setSelectedKey(e.target.value)}
            className="text-primary-500 w-full appearance-none rounded-xl border border-gray-200 bg-white px-4 py-3 pr-12 shadow-sm focus:ring-2 focus:ring-blue-500 focus:outline-none"
          >
            {items.map((item, index) => (
              <option key={index} value={String(index)}>
                {item.title}
              </option>
            ))}
          </select>
 
          <div className="pointer-events-none absolute inset-y-0 right-3 flex items-center">
            <RiArrowDownSLine className="text-gray-400" />
          </div>
        </div>
 
        <TabList
          aria-label="Tabs"
          className="hidden w-fit flex-row gap-2 pt-2 pl-1 md:flex"
        >
          {items.map((item, index) => (
            <Tab
              key={index}
              id={String(index)}
              className="data-[selected=true]:text-primary-600 data-[selected=true]:after:bg-primary-600 data-[selected=true]:hover:bg-primary-100 focus-ring-inverse relative flex cursor-pointer gap-2.5 rounded-md px-2 py-1 font-semibold text-gray-600 after:absolute after:right-0 after:-bottom-1.5 after:left-0 after:h-[2px] after:rounded after:bg-transparent hover:bg-gray-100 hover:after:bg-gray-600 focus-visible:ring-2"
            >
              <RiFoldersLine aria-hidden />
              {item.title}
            </Tab>
          ))}
        </TabList>
 
        <TabPanels className="mt-1 border-t-2 border-gray-100 pt-8">
          {items.map((item, index) => (
            <TabPanel
              key={index}
              id={String(index)}
              shouldForceMount
              className="focus-ring data-[inert=true]:hidden"
            >
              {item.content}
            </TabPanel>
          ))}
        </TabPanels>
      </Tabs>

Vertical Tabs

Tabs can be arranged vertically by:

  1. Setting the orientation prop to "vertical" on the <Tabs>
  2. Styling the tabs so they appear vertical.
This is the content for tab 1 Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed a justo sapien. Nunc tempor, sem non feugiat vehicula, augue arcu feugiat ligula, quis efficitur nulla dui non magna. Mauris leo erat, suscipit id elit quis, sodales varius lacus. Ut tempus dignissim elit ut blandit. Aenean et dui ut metus lacinia ullamcorper quis non nisi. Etiam tincidunt sagittis scelerisque. Fusce consectetur accumsan sem non consequat. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Aenean ac sapien pharetra, aliquam leo sit amet, suscipit felis. Vivamus id malesuada justo. Quisque ultricies tortor sed massa sodales sodales.
This is the content for tab 2 Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed a justo sapien. Nunc tempor, sem non feugiat vehicula, augue arcu feugiat ligula, quis efficitur nulla dui non magna. Mauris leo erat, suscipit id elit quis, sodales varius lacus. Ut tempus dignissim elit ut blandit. Aenean et dui ut metus lacinia ullamcorper quis non nisi. Etiam tincidunt sagittis scelerisque. Fusce consectetur accumsan sem non consequat. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Aenean ac sapien pharetra, aliquam leo sit amet, suscipit felis. Vivamus id malesuada justo. Quisque ultricies tortor sed massa sodales sodales.
This is the content for tab 3 Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed a justo sapien. Nunc tempor, sem non feugiat vehicula, augue arcu feugiat ligula, quis efficitur nulla dui non magna. Mauris leo erat, suscipit id elit quis, sodales varius lacus. Ut tempus dignissim elit ut blandit. Aenean et dui ut metus lacinia ullamcorper quis non nisi. Etiam tincidunt sagittis scelerisque. Fusce consectetur accumsan sem non consequat. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Aenean ac sapien pharetra, aliquam leo sit amet, suscipit felis. Vivamus id malesuada justo. Quisque ultricies tortor sed massa sodales sodales.
<div className="mx-auto w-full max-w-5xl px-4">
      <Tabs
        selectedKey={selectedKey}
        onSelectionChange={(key) => setSelectedKey(String(key))}
        orientation="vertical"
        className="flex flex-col gap-6 md:flex-row md:items-start"
      >
        <div className="relative w-full md:hidden">
          <select
            value={selectedKey}
            onChange={(e) => setSelectedKey(e.target.value)}
            className="text-primary-500 w-full appearance-none rounded-xl border border-gray-200 bg-white px-4 py-3 pr-10 shadow-sm focus:ring-2 focus:ring-blue-500 focus:outline-none"
          >
            {items.map(({ title }, index) => (
              <option key={index} value={String(index)}>
                {title}
              </option>
            ))}
          </select>
          <div className="pointer-events-none absolute inset-y-0 right-3 flex items-center text-gray-400">
            <RiArrowDownSLine />
          </div>
        </div>
 
        <TabList
          aria-label="Tabs"
          className="ml-2 hidden w-fit flex-col gap-2 border-l-2 border-gray-100 pl-1 md:flex"
        >
          {items.map(({ title }, index) => (
            <Tab
              key={index}
              id={String(index)}
              className="data-[selected=true]:text-primary-600 data-[selected=true]:before:bg-primary-600 data-[selected=true]:hover:bg-primary-100 relative flex w-max cursor-pointer gap-2.5 rounded-md px-2 py-1 font-semibold text-gray-600 before:absolute before:inset-y-0 before:-left-1.5 before:w-[2px] before:bg-transparent hover:bg-gray-100 hover:before:bg-gray-600 focus-visible:ring-2"
            >
              <RiFoldersLine aria-hidden />
              {title}
            </Tab>
          ))}
        </TabList>
 
        <TabPanels className="flex-1">
          {items.map(({ content }, index) => (
            <TabPanel
              key={index}
              id={String(index)}
              shouldForceMount
              className="focus-ring data-[inert=true]:hidden"
            >
              {content}
            </TabPanel>
          ))}
        </TabPanels>
      </Tabs>
    </div>