Tabs
Switches between related sections without leaving the current view.
Preview
Example code
Overview
Activity
Settings
Track the milestones, owners, and next steps for this project.
Playground
schemeApplies a semantic color to the selected tab indicator.
sizeSets tab height and horizontal padding.
isDisabledDisables interaction for the full tab set.
Usage
tsx
import { Tab, TabList, TabPanel, Tabs } from "earthling-ui/tabs";
export function Example() {
return (
<>
<Tabs defaultSelectedKey="overview">
<TabList aria-label="Project details">
<Tab id="overview">Overview</Tab>
<Tab id="activity">Activity</Tab>
</TabList>
<TabPanel id="overview">Project overview</TabPanel>
<TabPanel id="activity">Recent activity</TabPanel>
</Tabs>
</>
);
}
- Each Tab id must match the id of its TabPanel.
- Give TabList an accessible label when surrounding context is not enough.
- Tabs and panels preserve React Aria render-function className props.
Installation
Start with the project setup for React and Tailwind CSS 4. Both paths use the same component and theme.
Import package
Own the source
bash
npm install earthling-uiUse the component import shown above. Package updates keep the implementation current.
API reference
Declarations from the current library build, including inherited primitive props. Playground controls above show selected options; they are not the complete API.
tsx
// Generated by dts-bundle-generator v9.5.1
import { VariantProps } from 'class-variance-authority';
import { ComponentPropsWithoutRef, ContextType } from 'react';
import { Tab as TabPrimitive, TabList as TabListPrimitive, TabPanel as TabPanelPrimitive, Tabs as TabsPrimitive } from 'react-aria-components';
declare const TabsContext: import("react").Context<Pick<TabProps, "scheme" | "size">>;
export declare const tabsVariants: (props?: ({} & import("class-variance-authority/types").ClassProp) | undefined) => string;
export interface TabsProps extends ComponentPropsWithoutRef<typeof TabsPrimitive>, VariantProps<typeof tabsVariants>, ContextType<typeof TabsContext> {
}
export declare const Tabs: import("react").ForwardRefExoticComponent<TabsProps & import("react").RefAttributes<HTMLDivElement>>;
declare const tabListVariants: (props?: ({} & import("class-variance-authority/types").ClassProp) | undefined) => string;
export interface TabListProps extends ComponentPropsWithoutRef<typeof TabListPrimitive>, VariantProps<typeof tabListVariants> {
}
export declare const TabList: import("react").ForwardRefExoticComponent<TabListProps & import("react").RefAttributes<HTMLDivElement>>;
declare const tabVariants: (props?: ({
size?: "sm" | "md" | "lg" | null | undefined;
scheme?: "default" | "primary" | "secondary" | "tertiary" | "neutral" | "muted" | "good" | "caution" | "bad" | null | undefined;
} & import("class-variance-authority/types").ClassProp) | undefined) => string;
export interface TabProps extends ComponentPropsWithoutRef<typeof TabPrimitive>, VariantProps<typeof tabVariants> {
}
export declare const Tab: import("react").ForwardRefExoticComponent<TabProps & import("react").RefAttributes<HTMLDivElement>>;
declare const tabPanelVariants: (props?: ({} & import("class-variance-authority/types").ClassProp) | undefined) => string;
export interface TabPanelProps extends ComponentPropsWithoutRef<typeof TabPanelPrimitive>, VariantProps<typeof tabPanelVariants> {
}
export declare const TabPanel: import("react").ForwardRefExoticComponent<TabPanelProps & import("react").RefAttributes<HTMLDivElement>>;
export {};Runtime dependencies: class-variance-authority, cnfast, react-aria-components.
Source
Use the eject command to copy this implementation with its required helpers and project-specific imports.
View Tabs implementation
tsx
"use client";
import {
createContext,
forwardRef,
useContext,
type ComponentPropsWithoutRef,
type ComponentRef,
type ContextType,
} from "react";
import {
Tab as TabPrimitive,
TabList as TabListPrimitive,
TabPanel as TabPanelPrimitive,
Tabs as TabsPrimitive,
composeRenderProps,
} from "react-aria-components";
import { cn } from "@/utils/cn";
import { schemes } from "@/utils/variants";
import { cva, type VariantProps } from "class-variance-authority";
const TabsContext = createContext<Pick<TabProps, "size" | "scheme">>({
size: "md",
scheme: "primary",
});
//Tabs
export const tabsVariants = cva("", { variants: {}, defaultVariants: {} });
interface TabsProps
extends
ComponentPropsWithoutRef<typeof TabsPrimitive>,
VariantProps<typeof tabsVariants>,
ContextType<typeof TabsContext> {}
const Tabs = forwardRef<ComponentRef<typeof TabsPrimitive>, TabsProps>(
({ className, size = "md", scheme = "primary", ...props }, ref) => {
return (
<TabsContext.Provider value={{ size, scheme }}>
<TabsPrimitive
ref={ref}
{...props}
className={composeRenderProps(className, (value) =>
cn(tabsVariants({}), value),
)}
/>
</TabsContext.Provider>
);
},
);
Tabs.displayName = "Tabs";
//TabList
const tabListVariants = cva("flex flex-row border-b border-current/15", {
variants: {},
defaultVariants: {},
});
export interface TabListProps
extends
ComponentPropsWithoutRef<typeof TabListPrimitive>,
VariantProps<typeof tabListVariants> {}
const TabList = forwardRef<ComponentRef<typeof TabListPrimitive>, TabListProps>(
({ className, ...props }, ref) => {
return (
<TabListPrimitive
ref={ref}
{...props}
className={composeRenderProps(className, (value) =>
cn(tabListVariants({}), value),
)}
/>
);
},
);
TabList.displayName = "TabList";
//Tab
const tabVariants = cva(
"-mb-px inline-flex cursor-pointer items-center justify-center border-b-2 border-transparent ring-offset-background outline-none ring-outline hover:bg-(--scheme-tint)/5 focus-visible:relative focus-visible:z-10 focus-visible:ring-2 focus-visible:ring-outline focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50 data-disabled:cursor-not-allowed data-disabled:opacity-50 aria-selected:border-b-(--scheme-tint)",
{
variants: {
size: { sm: "h-9 px-3 text-sm", md: "h-10 px-4", lg: "h-11 px-8" },
scheme: schemes,
},
defaultVariants: { size: "md", scheme: "primary" },
},
);
export interface TabProps
extends
ComponentPropsWithoutRef<typeof TabPrimitive>,
VariantProps<typeof tabVariants> {}
const Tab = forwardRef<ComponentRef<typeof TabPrimitive>, TabProps>(
({ className, size, scheme, ...props }, ref) => {
const context = useContext(TabsContext);
return (
<TabPrimitive
ref={ref}
{...props}
className={composeRenderProps(className, (value) =>
cn(
tabVariants({
size: size ?? context.size,
scheme: scheme ?? context.scheme,
}),
value,
),
)}
/>
);
},
);
Tab.displayName = "Tab";
//TabPanel
const tabPanelVariants = cva(
"ring-offset-background focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-outline focus-visible:ring-offset-2",
{ variants: {}, defaultVariants: {} },
);
export interface TabPanelProps
extends
ComponentPropsWithoutRef<typeof TabPanelPrimitive>,
VariantProps<typeof tabPanelVariants> {}
const TabPanel = forwardRef<
ComponentRef<typeof TabPanelPrimitive>,
TabPanelProps
>(({ className, ...props }, ref) => {
return (
<TabPanelPrimitive
ref={ref}
{...props}
className={composeRenderProps(className, (value) =>
cn(tabPanelVariants({}), value),
)}
/>
);
});
TabPanel.displayName = "TabPanel";
export { Tabs, TabList, TabPanel, Tab };