Back to Dictionary
Page & View Transitions
●○○Ready to Use
Recommended defaultTab 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>
);
}