Installation
Terminal
TSXReactTailwind
npx @uiblox/cli add tabsVisual 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
| Prop | Type | Default | Description |
|---|---|---|---|
| defaultValue* | string | - | The initial active tab value |
| value | string | - | Controlled active tab value |
| onValueChange | (value: string) => void | - | Callback when the active tab changes |
TabsTrigger
| Prop | Type | Default | Description |
|---|---|---|---|
| value* | string | - | The unique value for this tab |
TabsContent
| Prop | Type | Default | Description |
|---|---|---|---|
| value* | string | - | The value matching the TabsTrigger to show this content |
Source Code
Copy this code into src/components/ui/tabs.tsx:
tabs.tsx
TSXReactTailwind
"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 };