Animated Tabs

PreviousNext

A custom tabs component with smooth CSS animations, three visual variants, and flexible styling options. Built without external dependencies

Manage your account settings.

Installation

pnpm dlx shadcn@latest add https://deltacomponents.dev/r/tabs.json

Usage

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.

Manage your account settings.

Ghost Variant

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

Manage your account settings.

Size Variants

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

Small

Manage your account settings.

Default

Manage your account settings.

Large

Manage your account settings.

With Icons

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

Manage your account settings and preferences.

Entrance Animations

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

Manage your account settings.

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.

Content for Tab 1. Click different tabs to see the active tab scroll into view automatically.

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.

<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.

<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.

PropTypeDefaultDescription
defaultValuestring-The value of the tab that should be active when initially rendered
valuestring-The controlled value of the tab to activate
onValueChangefunction-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)
indicatorThicknessstring-Override indicator thickness (e.g., "2px", "4px")
indicatorClassNamestring-Override active indicator className (e.g., "bg-primary")
concentricbooleanfalseUse concentric border radius (outer = inner + padding)
classNamestring-Additional CSS classes

TabsList

Container for the tab triggers with animated indicator support.

PropTypeDefaultDescription
classNamestring-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.

PropTypeDefaultDescription
valuestring-A unique value that associates the trigger with a content
iconReactNode-Icon to display alongside the label
disabledbooleanfalseWhen true, prevents the user from interacting with the tab
classNamestring-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.

PropTypeDefaultDescription
valuestring-A unique value that associates the content with a trigger
forceMountbooleanfalseForces the content to always be mounted in the DOM (hidden when inactive)
animatebooleanfalseEnable default opacity animation with ease-in-out
animateYnumber-Set Y translation offset in pixels for entrance animation
animateOpacityboolean-Set opacity animation - overrides animate when explicitly set
animationDurationnumber250Animation duration in milliseconds
animationEasing"ease" | "ease-in" | "ease-out" | "ease-in-out" | "linear" | stringvariesCSS timing function (default: "ease-out" for animateY, "ease-in-out" for animate)
optimizedbooleanfalseEnable content-visibility CSS optimization for better performance
intrinsicSizestring"auto 500px"Set contain-intrinsic-size for content-visibility optimization
classNamestring-Additional CSS classes

TabsFromArray

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

PropTypeDefaultDescription
tabsTabItem[]-Array of tab items to render
defaultValuestringtabs[0]Initial active tab value
valuestring-Controlled active tab value
onValueChangefunction-Event handler called when the value changes
classNamestring-Additional CSS classes for Tabs
listClassNamestring-Additional CSS classes for TabsList
triggerClassNamestring-Additional CSS classes for each TabsTrigger
contentClassNamestring-Additional CSS classes for each TabsContent
children(tab) => ReactNode-Render function for tab content

TabItem Type:

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