Tabs Compound Component

Overview

The Tabs compound component creates a tabbed interface for organizing content into separate views that share the same space. It uses React Context to manage the active tab state and coordinate between tab triggers and their corresponding content panels.

Example

Account Information

This panel contains user account information settings like name, email, and profile picture.

Styled Example

React

React is a JavaScript library for building user interfaces. It is maintained by Facebook and a community of individual developers and companies.

Code Implementation

// Tabs.tsx
import React, { createContext, useContext, useState, ReactNode } from 'react';

// Create context for the tabs
type TabsContextType = {
  activeTab: string;
  setActiveTab: (id: string) => void;
};

const TabsContext = createContext<TabsContextType | undefined>(undefined);

// Hook to use tabs context
const useTabs = () => {
  const context = useContext(TabsContext);
  if (!context) {
    throw new Error('Tabs components must be used within a Tabs component');
  }
  return context;
};

// Main Tabs component
const Tabs = ({ children, defaultTab = '', className = '' }) => {
  const [activeTab, setActiveTab] = useState(defaultTab);

  return (
    <TabsContext.Provider value={{ activeTab, setActiveTab }}>
      <div className={`${className}`}>
        {children}
      </div>
    </TabsContext.Provider>
  );
};

// TabList component
const TabList = ({ children, className = '' }) => {
  return (
    <div 
      className={`flex border-b border-gray-200 ${className}`}
      role="tablist"
    >
      {children}
    </div>
  );
};

// Tab component
const Tab = ({ children, tabId, className = '' }) => {
  const { activeTab, setActiveTab } = useTabs();
  const isActive = activeTab === tabId;

  return (
    <div
      className={`px-4 py-2 cursor-pointer ${isActive 
        ? 'border-b-2 border-blue-500 text-blue-600' 
        : 'text-gray-500 hover:text-gray-700'} ${className}`}
      onClick={() => setActiveTab(tabId)}
      role="tab"
      aria-selected={isActive}
    >
      {children}
    </div>
  );
};

// TabPanel component
const TabPanel = ({ children, tabId, className = '' }) => {
  const { activeTab } = useTabs();
  
  if (activeTab !== tabId) return null;

  return (
    <div 
      className={`p-4 ${className}`}
      role="tabpanel"
    >
      {children}
    </div>
  );
};

// Attach sub-components to Tabs
Tabs.TabList = TabList;
Tabs.Tab = Tab;
Tabs.TabPanel = TabPanel;

export default Tabs;

Key Features

  • Shared state using Context API to track the active tab
  • Automatic showing/hiding of content based on the active tab
  • Support for a default active tab
  • Customizable styling for tabs, tab list, and tab panels
  • Proper ARIA roles for accessibility
  • Clean and declarative API that makes composition intuitive