Back to Dictionary
Page & View Transitions
●○○Ready to Use
Recommended default

Tab Underline Slide

A highlighting background or underline that smoothly glides from the currently selected tab to the newly clicked tab, reinforcing spatial relationship.

Use

Top-level navigation bars

Avoid

Vertical menus with varying heights

Safer Alternative

No safer default listed.

Risk Level

Low risk

Live Demo

What It Is

A highlighting background or underline that smoothly glides from the currently selected tab to the newly clicked tab, reinforcing spatial relationship.

When to Use

  • Top-level navigation bars
  • Segmented controls
  • Pricing plan toggles (Monthly/Yearly)

When NOT to Use

  • Vertical menus with varying heights

Configuration Tips

  • 01Use layoutId in framer-motion to make the underline easily glide between isolated DOM elements
  • 02Keep the animation snappy (150-250ms)

You've Seen It In

VercelApple.comStripe

AI Implementation Prompts

Move from a fast scaffold to production details, then tune timing and edge states.

Create a tab bar where the active underline smoothly slides over to the tab I just clicked.

Reference Implementation

The same implementation approach used by the live preview, kept compact enough to inspect and adapt.

"use client";
import { useState, useEffect } from 'react';
import { motion } from 'framer-motion';

const tabs = ['Overview', 'Integrations', 'Billing', 'Settings'];

export function TabUnderlineSlideDemo({ isPlaying = false }: { isPlaying?: boolean }) {
    const [activeTab, setActiveTab] = useState('Overview');

    useEffect(() => {
        if (!isPlaying) {
            setActiveTab('Overview');
            return;
        }
        let currentIndex = 0;
        const interval = setInterval(() => {
            currentIndex = (currentIndex + 1) % tabs.length;
            setActiveTab(tabs[currentIndex]);
        }, 1500);
        return () => clearInterval(interval);
    }, [isPlaying]);

    return (
        <div className="relative flex h-64 w-full items-center justify-center overflow-hidden rounded-2xl bg-white p-6 shadow-sm ring-1 ring-stone-200">
            <div className="flex space-x-2 border-b border-stone-200">
                {tabs.map((tab) => (
                    <button
                        key={tab}
                        onClick={() => setActiveTab(tab)}
                        className={`relative px-4 py-3 text-sm font-medium transition-colors focus-visible:outline-none ${activeTab === tab ? 'text-stone-900' : 'text-stone-500 hover:text-stone-700'
                            }`}
                    >
                        {tab}
                        {activeTab === tab && (
                            <motion.div
                                layoutId="tab-underline-slide"
                                className="absolute bottom-[-1px] left-0 right-0 h-0.5 bg-stone-900"
                                initial={false}
                                transition={{ type: "spring", bounce: 0.2, duration: 0.5 }}
                            />
                        )}
                    </button>
                ))}
            </div>
        </div>
    );
}