UI
Tabs
A collection of content layers called tab panels, shown individually.
You have 60 days from the time we've shipped your order to return any part of it to us for a refund, provided it is still in its original, unused condition: we do not accept returns of used items.
No return authorization (RMA) is required. If you are within the United States, a pre-paid shipping label will be generated. For direct returns, a flat fee of $10 is deducted from your return for shipping and processing costs.
import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/Tabs"
export const TabsHero = () => ( <Tabs defaultValue="tab1"> <TabsList> <TabsTrigger value="tab1">Returns</TabsTrigger> <TabsTrigger value="tab2">Shipping</TabsTrigger> </TabsList> <div className="ml-2 mt-4"> <TabsContent value="tab1" className="space-y-2 text-sm leading-7 text-gray-600 dark:text-gray-500" > <p> You have 60 days from the time we've shipped your order to return any part of it to us for a refund, provided it is still in its original, unused condition: we do not accept returns of used items. </p> <p> No return authorization (RMA) is required. If you are within the United States, a pre-paid shipping label will be generated. For direct returns, a flat fee of $10 is deducted from your return for shipping and processing costs. </p> </TabsContent> <TabsContent value="tab2" className="space-y-2 text-sm leading-7 text-gray-600 dark:text-gray-500" > <p> We ship worldwide via UPS Expedited. We offer flat rate shipping to customers in Canada ($30), the EU, Japan, and Singapore ($45–$65+), and Australia ($65). Note that most brokerage fees are included in the price of UPS Expedited shipping, with the exception of a possible $10 fee assessed in Canada only if prior arrangements to pay for duties and taxes are not made (see next question and answer). </p> <p> Outside of the United States, tariffs, duties, and taxes are the responsibility of the customer and are usually paid at time of delivery. </p> </TabsContent> </div> </Tabs>);
Installation
- 1
Install dependencies:
npm install @radix-ui/react-tabs
- 2
Add component:
Copy and paste the code into your project’s component directory. Do not forget to update the import paths.import * as TabsPrimitives from '@radix-ui/react-tabs';import React from 'react'; import { cx, focusRing } from './utils'; const Tabs = ( props: Omit< React.ComponentPropsWithoutRef<typeof TabsPrimitives.Root>, 'orientation' >) => { return <TabsPrimitives.Root {...props} />;}; Tabs.displayName = 'Tabs'; type TabsListVariant = 'line' | 'solid'; const TabsListVariantContext = React.createContext<TabsListVariant>('line'); interface TabsListProps extends React.ComponentPropsWithoutRef<typeof TabsPrimitives.List> { variant?: TabsListVariant;} const variantStyles: Record<TabsListVariant, string> = { line: cx( // base 'flex items-center justify-start border-b', // border color 'border-gray-200 dark:border-gray-800' ), solid: cx( // base 'inline-flex items-center justify-center rounded-md p-1', // border color // "border-gray-200 dark:border-gray-800", // background color 'bg-gray-100 dark:bg-gray-800' ),}; const TabsList = React.forwardRef< React.ElementRef<typeof TabsPrimitives.List>, TabsListProps>(({ className, variant = 'line', children, ...props }, forwardedRef) => ( <TabsPrimitives.List ref={forwardedRef} className={cx(variantStyles[variant], className)} {...props} > <TabsListVariantContext.Provider value={variant}> {children} </TabsListVariantContext.Provider> </TabsPrimitives.List>)); TabsList.displayName = 'TabsList'; function getVariantStyles(tabVariant: TabsListVariant) { switch (tabVariant) { case 'line': return cx( // base '-mb-px items-center justify-center whitespace-nowrap border-b-2 border-transparent px-3 pb-3 text-sm font-medium transition-all', // text color 'text-gray-500 dark:text-gray-500', // hover 'hover:text-gray-700 hover:dark:text-gray-400', // border hover 'hover:border-gray-300 hover:dark:border-gray-400', // selected 'data-[state=active]:border-gray-900 data-[state=active]:text-gray-900', 'data-[state=active]:dark:border-gray-50 data-[state=active]:dark:text-gray-50', // disabled 'disabled:pointer-events-none', 'disabled:text-gray-300 disabled:dark:text-gray-700' ); case 'solid': return cx( // base 'inline-flex items-center justify-center whitespace-nowrap rounded px-3 py-1 transition-all text-sm font-medium', // text color 'text-gray-500 dark:text-gray-400', // hover 'hover:text-gray-700 hover:dark:text-gray-200', // selected ' data-[state=active]:bg-white data-[state=active]:text-gray-900 data-[state=active]:shadow', 'data-[state=active]:dark:bg-gray-900 data-[state=active]:dark:text-gray-50', // disabled 'disabled:pointer-events-none disabled:text-gray-400 disabled:dark:text-gray-600 disabled:opacity-50' ); }} const TabsTrigger = React.forwardRef< React.ElementRef<typeof TabsPrimitives.Trigger>, React.ComponentPropsWithoutRef<typeof TabsPrimitives.Trigger>>(({ className, children, ...props }, forwardedRef) => { const variant = React.useContext(TabsListVariantContext); return ( <TabsPrimitives.Trigger ref={forwardedRef} className={cx(getVariantStyles(variant), focusRing, className)} {...props} > {children} </TabsPrimitives.Trigger> );}); TabsTrigger.displayName = 'TabsTrigger'; const TabsContent = React.forwardRef< React.ElementRef<typeof TabsPrimitives.Content>, React.ComponentPropsWithoutRef<typeof TabsPrimitives.Content>>(({ className, ...props }, forwardedRef) => ( <TabsPrimitives.Content ref={forwardedRef} className={cx('outline-none', focusRing, className)} {...props} />)); TabsContent.displayName = 'TabsContent'; export { Tabs, TabsContent, TabsList, TabsTrigger };
Example: Tabs solid variant
You have 60 days from the time we've shipped your order to return any part of it to us for a refund, provided it is still in its original, unused condition: we do not accept returns of used items.
No return authorization (RMA) is required. If you are within the United States, a pre-paid shipping label will be generated. For direct returns, a flat fee of $10 is deducted from your return for shipping and processing costs.
import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/Tabs"
export const TabsExample = () => ( <Tabs defaultValue="tab1"> <TabsList variant="solid"> <TabsTrigger value="tab1">Returns</TabsTrigger> <TabsTrigger value="tab2">Shipping</TabsTrigger> </TabsList> <div className="ml-2 mt-4"> <TabsContent value="tab1" className="space-y-2 text-sm leading-7 text-gray-600 dark:text-gray-500" > <p> You have 60 days from the time we've shipped your order to return any part of it to us for a refund, provided it is still in its original, unused condition: we do not accept returns of used items. </p> <p> No return authorization (RMA) is required. If you are within the United States, a pre-paid shipping label will be generated. For direct returns, a flat fee of $10 is deducted from your return for shipping and processing costs. </p> </TabsContent> <TabsContent value="tab2" className="space-y-2 text-sm leading-7 text-gray-600 dark:text-gray-500" > <p> We ship worldwide via UPS Expedited. We offer flat rate shipping to customers in Canada ($30), the EU, Japan, and Singapore ($45–$65+), and Australia ($65). Note that most brokerage fees are included in the price of UPS Expedited shipping, with the exception of a possible $10 fee assessed in Canada only if prior arrangements to pay for duties and taxes are not made (see next question and answer). </p> <p> Outside of the United States, tariffs, duties, and taxes are the responsibility of the customer and are usually paid at time of delivery. </p> </TabsContent> </div> </Tabs>);
Example: Tabs with disabled trigger
Tab 1 content
import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/Tabs"
export const TabsDisabledExample = () => ( <Tabs defaultValue="tab1"> <TabsList variant="line"> <TabsTrigger value="tab1">Tab 1</TabsTrigger> <TabsTrigger value="tab2">Tab 2</TabsTrigger> <TabsTrigger value="tab3" disabled> Tab 3 </TabsTrigger> </TabsList> <div className="mt-4"> <TabsContent value="tab1"> <p className="text-sm text-gray-500 sm:text-gray-500">Tab 1 content</p> </TabsContent> <TabsContent value="tab2"> <p className="text-sm text-gray-500 sm:text-gray-500">Tab 2 content</p> </TabsContent> <TabsContent value="tab3"> <p className="text-sm text-gray-500 sm:text-gray-500">Tab 3 content</p> </TabsContent> </div> </Tabs>);
Example: Tabs with icons
Tab 1 content
Tab 1 content
import { RiCalculatorLine, RiMapPin2Line } from "@remixicon/react"import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/Tabs"import { Tabs, TabsContent, TabsList, TabsTrigger,} from "@/components/Tabs"
export const TabsWithIconsExample = () => ( <div className="flex flex-col gap-12"> <Tabs defaultValue="tab1"> <TabsList variant="line"> <TabsTrigger value="tab1" className="inline-flex gap-2"> <RiMapPin2Line className="-ml-1 size-4" aria-hidden="true" /> Pic location </TabsTrigger> <TabsTrigger value="tab2" className="inline-flex gap-2"> <RiCalculatorLine className="-ml-1 size-4" aria-hidden="true" /> Calculate location </TabsTrigger> </TabsList> <div className="mt-4"> <TabsContent value="tab1"> <p className="text-sm text-gray-500 sm:text-gray-500"> Tab 1 content </p> </TabsContent> <TabsContent value="tab2"> <p className="text-sm text-gray-500 sm:text-gray-500"> Tab 2 content </p> </TabsContent> </div> </Tabs> <Tabs defaultValue="tab1"> <TabsList variant="solid"> <TabsTrigger value="tab1" className="gap-1.5"> <RiMapPin2Line className="-ml-1 size-4" aria-hidden="true" /> Pic location </TabsTrigger> <TabsTrigger value="tab2" className="gap-1.5"> <RiCalculatorLine className="-ml-1 size-4" aria-hidden="true" /> Calculate location </TabsTrigger> </TabsList> <div className="mt-4"> <TabsContent value="tab1"> <p className="text-sm text-gray-500 sm:text-gray-500"> Tab 1 content </p> </TabsContent> <TabsContent value="tab2"> <p className="text-sm text-gray-500 sm:text-gray-500"> Tab 2 content </p> </TabsContent> </div> </Tabs> </div>);
Example: Streched tabs
Tab 1 content
Tab 1 content
import { Tabs, TabsContent, TabsList, TabsTrigger,} from "@/components/Tabs"
export const TabsStretchWidthExample = () => ( <div className="flex flex-col gap-6"> <Tabs defaultValue="tab1"> <TabsList className="grid w-full grid-cols-3" variant="line"> <TabsTrigger value="tab1">Tab 1</TabsTrigger> <TabsTrigger value="tab2">Tab 2</TabsTrigger> <TabsTrigger value="tab3">Tab 3</TabsTrigger> </TabsList> <div className="mt-4"> <TabsContent value="tab1"> <p className="text-sm text-gray-500 sm:text-gray-500"> Tab 1 content </p> </TabsContent> <TabsContent value="tab2"> <p className="text-sm text-gray-500 sm:text-gray-500"> Tab 2 content </p> </TabsContent> <TabsContent value="tab3"> <p className="text-sm text-gray-500 sm:text-gray-500"> Tab 3 content </p> </TabsContent> </div> </Tabs> <Tabs defaultValue="tab1"> <TabsList className="grid w-full grid-cols-3" variant="solid"> <TabsTrigger value="tab1">Tab 1</TabsTrigger> <TabsTrigger value="tab2">Tab 2</TabsTrigger> <TabsTrigger value="tab3">Tab 3</TabsTrigger> </TabsList> <div className="mt-4"> <TabsContent value="tab1"> <p className="text-sm text-gray-500 sm:text-gray-500"> Tab 1 content </p> </TabsContent> <TabsContent value="tab2"> <p className="text-sm text-gray-500 sm:text-gray-500"> Tab 2 content </p> </TabsContent> <TabsContent value="tab3"> <p className="text-sm text-gray-500 sm:text-gray-500"> Tab 3 content </p> </TabsContent> </div> </Tabs> </div>);
API Reference: Tabs
This component uses the Radix UI API.
Note: The orientation prop has been removed.
API Reference: TabsList
This component uses the Radix UI API.
- variant
- Set a predefined look.
Default: "line"
API Reference: TabsTrigger
This component uses the Radix UI API.
API Reference: TabsContent
This component uses the Radix UI API.