---
title: Animated Tabs
description: A custom tabs component with smooth CSS animations, three visual variants, and flexible styling options. Built without external dependencies
status: beta
---

<ComponentPreview name="tabs-background-demo" />

## Installation

<Installation name="tabs" />

## Usage

```tsx
import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs"

export default function Example() {
  return (
    <Tabs defaultValue="account" className="w-[400px]">
      <TabsList>
        <TabsTrigger value="account">Account</TabsTrigger>
        <TabsTrigger value="password">Password</TabsTrigger>
      </TabsList>
      <TabsContent value="account">
        Make changes to your account here.
      </TabsContent>
      <TabsContent value="password">Change your password here.</TabsContent>
    </Tabs>
  )
}
```

## Examples

### Underline Variant

The underline variant displays tabs with an animated underline indicator, perfect for navigation and section switching.

<ComponentPreview name="tabs-demo" />

### Ghost Variant

Remove the background from the default variant tabs for a cleaner, more minimal appearance.

<ComponentPreview name="tabs-no-background-demo" />

### Size Variants

Tabs come in three sizes: `sm`, `default`, and `lg`. Choose the size that best fits your layout.

<ComponentPreview name="tabs-sizes-demo" height="600px" />

### With Icons

Enhance your tabs with icons for better visual communication and user experience.

<ComponentPreview name="tabs-with-icons-demo" />

### Entrance Animations

Control how tab content appears with customizable entrance animations. Set `animate={true}` on `TabsContent` to enable smooth transitions using CSS.

<ComponentPreview name="tabs-entrance-animation-demo" />

**Note:** Animations are disabled by default for optimal performance. Enable them by setting `animate={true}` on individual `TabsContent` components when you need smooth transitions for lightweight content.

### Tabs in Scroll Area

When you have many tabs that don't fit in the available space, wrap the `TabsList` in a `ScrollArea` to enable horizontal scrolling. This demo automatically scrolls the active tab into view when switching tabs.

<ComponentPreview name="tabs-in-scroll-area-demo" />

### Performance Optimization

For tabs containing heavy content (large code blocks, images, complex components), use these optimization strategies to prevent animation stagger and improve performance:

#### Pre-render with forceMount

Set `forceMount={true}` to keep all tab content mounted in the DOM. This eliminates mounting/unmounting delays and ensures content is accessible to search engines and LLMs.

```tsx
<Tabs defaultValue="tab1">
  <TabsList>
    <TabsTrigger value="tab1">Documentation</TabsTrigger>
    <TabsTrigger value="tab2">Examples</TabsTrigger>
  </TabsList>

  <TabsContent value="tab1" forceMount={true}>
    {/* Heavy content - always in DOM for crawlers */}
    <ComplexCodeBlock />
  </TabsContent>

  <TabsContent value="tab2" forceMount={true}>
    {/* Heavy content - always in DOM for crawlers */}
    <LargeImageGallery />
  </TabsContent>
</Tabs>
```

#### CSS Content-Visibility Optimization

Combine `forceMount` with `optimized={true}` to enable CSS `content-visibility` optimization. This allows the browser to skip rendering off-screen content while keeping it in the DOM.

```tsx
<TabsContent
  value="tab1"
  forceMount={true}
  optimized={true}
  intrinsicSize="auto 300px"
>
  {/* Optimized heavy content */}
  <HeavyComponent />
</TabsContent>
```

**When to use:**

- **forceMount only**: Documentation sites with 2-5 tabs, SEO requirements
- **forceMount + optimized**: Modern browsers, heavy content, progressive enhancement
- **Default behavior**: Applications with many tabs (5+), memory-conscious scenarios

## API Reference

### Tabs

The root tabs component that provides context for all child components.

| Prop               | Type                                            | Default   | Description                                                        |
| ------------------ | ----------------------------------------------- | --------- | ------------------------------------------------------------------ |
| defaultValue       | string                                          | -         | The value of the tab that should be active when initially rendered |
| value              | string                                          | -         | The controlled value of the tab to activate                        |
| onValueChange      | function                                        | -         | Event handler called when the value changes                        |
| variant            | "default" \| "underline" \| "ghost"             | "default" | Visual style variant of the tabs                                   |
| size               | "sm" \| "small" \| "default" \| "lg" \| "large" | "default" | Size of the tabs (both short and long forms accepted)              |
| indicatorThickness | string                                          | -         | Override indicator thickness (e.g., "2px", "4px")                  |
| indicatorClassName | string                                          | -         | Override active indicator className (e.g., "bg-primary")           |
| concentric         | boolean                                         | false     | Use concentric border radius (outer = inner + padding)             |
| className          | string                                          | -         | Additional CSS classes                                             |

### TabsList

Container for the tab triggers with animated indicator support.

| Prop      | Type   | Default | Description            |
| --------- | ------ | ------- | ---------------------- |
| className | string | -       | Additional CSS classes |

The `TabsList` component automatically inherits `variant`, `size`, `indicatorThickness`, `indicatorClassName`, and `concentric` from the parent `Tabs` component. For the underline variant, tabs align to the start; for default and ghost variants, they're centered.

### TabsTrigger

Individual tab trigger button that activates a tab panel when clicked.

| Prop      | Type      | Default | Description                                                |
| --------- | --------- | ------- | ---------------------------------------------------------- |
| value     | string    | -       | A unique value that associates the trigger with a content  |
| icon      | ReactNode | -       | Icon to display alongside the label                        |
| disabled  | boolean   | false   | When true, prevents the user from interacting with the tab |
| className | string    | -       | Additional CSS classes                                     |

The trigger automatically displays the animated indicator when active using CSS transitions with `requestAnimationFrame` for smooth positioning.

### TabsContent

Content panel associated with a tab trigger.

| Prop              | Type                                                                     | Default      | Description                                                                       |
| ----------------- | ------------------------------------------------------------------------ | ------------ | --------------------------------------------------------------------------------- |
| value             | string                                                                   | -            | A unique value that associates the content with a trigger                         |
| forceMount        | boolean                                                                  | false        | Forces the content to always be mounted in the DOM (hidden when inactive)         |
| animate           | boolean                                                                  | false        | Enable default opacity animation with ease-in-out                                 |
| animateY          | number                                                                   | -            | Set Y translation offset in pixels for entrance animation                         |
| animateOpacity    | boolean                                                                  | -            | Set opacity animation - overrides animate when explicitly set                     |
| animationDuration | number                                                                   | 250          | Animation duration in milliseconds                                                |
| animationEasing   | "ease" \| "ease-in" \| "ease-out" \| "ease-in-out" \| "linear" \| string | varies       | CSS timing function (default: "ease-out" for animateY, "ease-in-out" for animate) |
| optimized         | boolean                                                                  | false        | Enable content-visibility CSS optimization for better performance                 |
| intrinsicSize     | string                                                                   | "auto 500px" | Set contain-intrinsic-size for content-visibility optimization                    |
| className         | string                                                                   | -            | Additional CSS classes                                                            |

### TabsFromArray

Utility component for rendering tabs from an array of tab items, useful when working with dynamic data.

| Prop             | Type               | Default | Description                                 |
| ---------------- | ------------------ | ------- | ------------------------------------------- |
| tabs             | TabItem[]          | -       | Array of tab items to render                |
| defaultValue     | string             | tabs[0] | Initial active tab value                    |
| value            | string             | -       | Controlled active tab value                 |
| onValueChange    | function           | -       | Event handler called when the value changes |
| className        | string             | -       | Additional CSS classes for Tabs             |
| listClassName    | string             | -       | Additional CSS classes for TabsList         |
| triggerClassName | string             | -       | Additional CSS classes for each TabsTrigger |
| contentClassName | string             | -       | Additional CSS classes for each TabsContent |
| children         | (tab) => ReactNode | -       | Render function for tab content             |

**TabItem Type:**

```ts
interface TabItem {
  id: string
  label: React.ReactNode
  icon?: React.ReactNode
  disabled?: boolean
}
```

## Features

- **Animated Indicators**: Smooth spring animations powered by Framer Motion
- **Two Variants**: Choose between default (background) and underline styles
- **Three Sizes**: Small, default, and large options for different layouts
- **Icon Support**: Add icons to your tabs for enhanced visual communication
- **Keyboard Navigation**: Full keyboard support for accessibility
- **Disabled State**: Disable individual tabs when needed
- **Responsive**: Works seamlessly across all screen sizes
- **Dark Mode**: Automatically adapts to your theme

## Accessibility

- Uses Radix UI Tabs primitive for full accessibility compliance
- Supports keyboard navigation (Arrow keys, Home, End)
- Proper ARIA attributes for screen readers
- Focus management and visual indicators

## Notes

- The animated indicator uses Framer Motion's `layoutId` feature for smooth transitions
- Tab content supports controlled and uncontrolled state management
- The underline variant tabs align to the start by default for better UX
- The default variant centers tabs within their container
