Next.js interaction pattern
Animated Tabs in Next.js with Reduced Motion
Add keyboard-operable animated tabs to a Next.js route, keep state inside a client boundary, and provide an instant reduced-motion path.
Reviewed 2026-09-01
Start with a narrow client boundary
Keep the route, headings, and data loading in Server Components. Place the installed tabs and the state they coordinate in a small client component. Menace Tabs uses React state, event handlers, focus management, and Motion, so the tab root cannot be a Server Component.
npx shadcn add https://menaceui.com/r/menace-tabs.jsonThe registry item adds the TypeScript component, its CSS Module, and the motion package dependency. Review those changes before importing the component into the route.
Keep the tab relationship intact
Compose Tabs, TabsList, TabsTrigger, and TabsPanel together. The implementation generates matching trigger and panel IDs, applies tab roles, and moves only enabled triggers into the roving tab stop.
- Give the tab list an accessible label.
- Use stable, unique values for each trigger and panel pair.
- Do not hide the active panel with a separate CSS condition.
- Use manual activation when changing a tab performs expensive work; Enter or Space then selects the focused tab.
Understand keyboard and direction behavior
Horizontal lists use Left and Right Arrow. Vertical lists use Up and Down Arrow. Home and End move to the first and last enabled trigger, and optional looping controls what happens at an edge. Right-to-left direction reverses horizontal arrow meaning.
Test focus separately from selection. Automatic activation selects on focus, while manual activation lets keyboard focus move without replacing the panel.
Provide an instant motion path
The installed component reads the user motion preference. When reduced motion is active, the shared selection marker and panel transition use zero-duration changes. Keep this branch if you tune spring values or replace the indicator.
Motion should explain selection, not delay content. Avoid fetching a panel only after its animation starts. See the motion performance checklist for measurement steps.
Failure modes to test
- A controlled value that does not match any enabled trigger.
- All triggers disabled, then one becoming enabled.
- Nested buttons or links inside a tab trigger.
- Long labels at 320px and a vertical list in a narrow panel.
- Automatic activation tied to a slow network request.
The component pageincludes the current API and a live preview for replaying these states.