SlidingTabs

Animated tab component with smooth sliding background transition. Perfect for filtering, view switching, and navigation within a section.

Tab Sizes

Small
28px height
size="small"
Default
32px height
(no size prop)
Large
40px height
size="large"

Display Modes

Text Mode
Default
mode="text"
Icon Mode
Icon only
mode="icon"
Both Mode
Icon + Text
mode="both"

Equal Width Distribution

Auto Width
Default
equalWidth={false}
Equal Width
Evenly distributed
equalWidth={true}
Two Options
50% each
equalWidth={true}
Four Options
25% each
equalWidth={true}

Full Width Mode

Auto Width
Default
fullWidth={false}
Full Width
Container fills parent
fullWidth={true}

Interactive Demo

Draft Status
3 options
value: "drafts"
Time Period
4 options
value: "week"
Toggle
2 options
value: "grid"

Content Filter

Filter content by status

Props:
{ options: "3 items" }

Priority Selector

Select priority level

Props:
{ options: "3 items" }

Binary Toggle

Two-option switching

Props:
{ options: "2 items" }

Multiple Options

Four or more options

Props:
{ options: "4+ items" }

With Custom Width

Override width with className

Props:
{ className: "w-full" }

Compact Labels

Short labels for tight spaces

Props:
{ compact: true }

Icon Mode - Text Formatting

Icon-only tabs for toolbars

Props:
{ mode: ""icon"" }

Icon Mode - Large Size

Larger icon-only tabs

Props:
{ mode: ""icon"", size: ""large"" }

Both Mode - Schedule

Icon and text combined

Props:
{ mode: ""both"" }

Usage Example

import { useState } from "react";
import { SlidingTabs } from "@/components/ui/sliding-tabs";
import { LayoutGrid, List, AlignLeft, AlignCenter, AlignRight } from "lucide-react";

// Basic usage (text mode - default)
const [value, setValue] = useState("drafts");

<SlidingTabs
  value={value}
  onValueChange={setValue}
  options={[
    { value: "drafts", label: "Drafts" },
    { value: "schedule", label: "Schedule" },
    { value: "posted", label: "Posted" },
  ]}
/>

// Icon mode
<SlidingTabs
  value={alignment}
  onValueChange={setAlignment}
  mode="icon"
  options={[
    { value: "left", label: "Left", icon: <AlignLeft /> },
    { value: "center", label: "Center", icon: <AlignCenter /> },
    { value: "right", label: "Right", icon: <AlignRight /> },
  ]}
/>

// Both mode (icon + text)
<SlidingTabs
  value={viewType}
  onValueChange={setViewType}
  mode="both"
  options={[
    { value: "grid", label: "Grid", icon: <LayoutGrid /> },
    { value: "list", label: "List", icon: <List /> },
  ]}
/>

// Different sizes
<SlidingTabs size="small" ... />  // 28px height
<SlidingTabs size="default" ... /> // 32px height (default)
<SlidingTabs size="large" ... />  // 40px height

// Equal width distribution (tabs are equal sized)
<SlidingTabs
  value={value}
  onValueChange={setValue}
  equalWidth={true}
  options={[
    { value: "drafts", label: "Drafts" },
    { value: "schedule", label: "Schedule" },
    { value: "posted", label: "Posted" },
  ]}
/>

// Full width (container fills parent, tabs keep natural sizing)
<SlidingTabs
  value={value}
  onValueChange={setValue}
  fullWidth={true}
  options={[
    { value: "all", label: "All" },
    { value: "drafts", label: "Drafts" },
    { value: "schedule", label: "Schedule" },
    { value: "posted", label: "Posted" },
  ]}
/>

Design Specifications

Sizes
  • • Small: 28px height, 3px padding, 12px font
  • • Default: 32px height, 3px padding, 13px font
  • • Large: 40px height, 4px padding, 14px font
Container
  • • Border radius: 48px (pill shape)
  • • Background: #f4f1f1 (light)
  • • Background: gray-800 (dark)
Slider
  • • Border radius: 80px
  • • Background: #FFF (light)
  • • Background: gray-700 (dark)
  • • Box shadow: multi-layer subtle shadow
Animation
  • • Duration: 300ms
  • • Easing: cubic-bezier(0.4, 0, 0.2, 1)
  • • Properties: transform, width
  • • Will-change: transform, width
Typography
  • • Font family: Inter
  • • Font weight: 500
  • • Letter spacing: -0.03em
  • • Gap between buttons: 4px
Colors
  • • Active: --color-font-primary
  • • Inactive: --color-font-secondary
  • • Hover: --color-font-primary

Implementation Notes

This is a controlled component that requires both value and onValueChange props.

  • The slider automatically calculates position based on button widths
  • Transitions are disabled during initial mount and container resize to prevent jarring animations
  • Uses ResizeObserver to handle responsive layout changes
  • Each button has equal flex growth (flex-1) for balanced spacing
  • The sliding background uses transform for smooth 60fps animations