Tabs

A set of layered sections of content, known as tab panels, displayed one at a time.

Installation

Terminal
ReactTailwind
TSX
npx @uiblox/cli add tabs

Visual Variations

Toggle between three different visual styles: pills (background), underline (border-bottom), and boxed (bordered).

Content for Tab 1

Simple navigation

Dashboard overview content

Tab with icons

All items

Stretched tabs

Basic Usage

Account settings content here.

Copy & paste ready
import { Tabs, TabsList, TabsTrigger, TabsContent } from "@/components/ui";

<Tabs defaultValue="account">
  <TabsList>
    <TabsTrigger value="account">Account</TabsTrigger>
    <TabsTrigger value="password">Password</TabsTrigger>
    <TabsTrigger value="settings">Settings</TabsTrigger>
  </TabsList>
  <TabsContent value="account">
    <p>Account settings content here.</p>
  </TabsContent>
  <TabsContent value="password">
    <p>Password settings content here.</p>
  </TabsContent>
  <TabsContent value="settings">
    <p>General settings content here.</p>
  </TabsContent>
</Tabs>

Full Width Tabs

Overview

View your dashboard overview here.

ReactTailwind CSSCopy & paste ready
<Tabs defaultValue="overview">
  <TabsList className="w-full">
    <TabsTrigger value="overview" className="flex-1">Overview</TabsTrigger>
    <TabsTrigger value="analytics" className="flex-1">Analytics</TabsTrigger>
    <TabsTrigger value="reports" className="flex-1">Reports</TabsTrigger>
  </TabsList>
  <TabsContent value="overview">...</TabsContent>
  <TabsContent value="analytics">...</TabsContent>
  <TabsContent value="reports">...</TabsContent>
</Tabs>

Props

Tabs

PropTypeDefaultDescription
defaultValue*string-The initial active tab value
valuestring-Controlled active tab value
onValueChange(value: string) => void-Callback when the active tab changes

TabsTrigger

PropTypeDefaultDescription
value*string-The unique value for this tab

TabsContent

PropTypeDefaultDescription
value*string-The value matching the TabsTrigger to show this content

Source Code

Copy this code into src/components/ui/tabs.tsx:

tabs.tsx
ReactTailwind
TSX
"use client";

import * as React from "react";
import { cn } from "@/lib/utils";

interface TabsContextValue {
  activeTab: string;
  setActiveTab: (value: string) => void;
}

const TabsContext = React.createContext<TabsContextValue | undefined>(undefined);

function useTabsContext() {
  const context = React.useContext(TabsContext);
  if (!context) {
    throw new Error("Tabs components must be used within a Tabs provider");
  }
  return context;
}

interface TabsProps extends React.HTMLAttributes<HTMLDivElement> {
  defaultValue: string;
  value?: string;
  onValueChange?: (value: string) => void;
}

const Tabs = React.forwardRef<HTMLDivElement, TabsProps>(
  ({ className, defaultValue, value, onValueChange, children, ...props }, ref) => {
    const [internalValue, setInternalValue] = React.useState(defaultValue);
    const activeTab = value ?? internalValue;

    const setActiveTab = React.useCallback(
      (newValue: string) => {
        if (!value) setInternalValue(newValue);
        onValueChange?.(newValue);
      },
      [value, onValueChange]
    );

    return (
      <TabsContext.Provider value={{ activeTab, setActiveTab }}>
        <div ref={ref} className={cn("w-full", className)} {...props}>
          {children}
        </div>
      </TabsContext.Provider>
    );
  }
);
Tabs.displayName = "Tabs";

const TabsList = React.forwardRef<HTMLDivElement, React.HTMLAttributes<HTMLDivElement>>(
  ({ className, ...props }, ref) => (
    <div
      ref={ref}
      role="tablist"
      className={cn(
        "inline-flex h-10 items-center justify-center rounded-lg bg-gray-100 dark:bg-gray-800 p-1 text-gray-500",
        className
      )}
      {...props}
    />
  )
);
TabsList.displayName = "TabsList";

interface TabsTriggerProps extends React.ButtonHTMLAttributes<HTMLButtonElement> {
  value: string;
}

const TabsTrigger = React.forwardRef<HTMLButtonElement, TabsTriggerProps>(
  ({ className, value, ...props }, ref) => {
    const { activeTab, setActiveTab } = useTabsContext();
    const isActive = activeTab === value;

    return (
      <button
        ref={ref}
        role="tab"
        aria-selected={isActive}
        onClick={() => setActiveTab(value)}
        className={cn(
          "inline-flex items-center justify-center whitespace-nowrap rounded-md px-3 py-1.5 text-sm font-medium transition-all",
          isActive
            ? "bg-white dark:bg-gray-900 text-gray-900 dark:text-white shadow-sm"
            : "hover:bg-gray-50 dark:hover:bg-gray-700",
          className
        )}
        {...props}
      />
    );
  }
);
TabsTrigger.displayName = "TabsTrigger";

interface TabsContentProps extends React.HTMLAttributes<HTMLDivElement> {
  value: string;
}

const TabsContent = React.forwardRef<HTMLDivElement, TabsContentProps>(
  ({ className, value, ...props }, ref) => {
    const { activeTab } = useTabsContext();
    if (activeTab !== value) return null;

    return (
      <div ref={ref} role="tabpanel" className={cn("mt-2", className)} {...props} />
    );
  }
);
TabsContent.displayName = "TabsContent";

export { Tabs, TabsList, TabsTrigger, TabsContent };