Slide only for depth

Tag forward and back navigations. Cross-fade tabs that sit side by side.

Next.js docsnextjs.org
The transition type is not automatic. You decide which links are "forward" and which are "back" based on your app's navigation hierarchy.
Directional motionRead docs ↗

From vercel-react-view-transitions, SKILL.md.

List to detail and back

Open an item, then go back.

One slide both waysBad
enter="slide-from-right"
Typed: forward, backGood
addTransitionType('nav-back')
<Link href={`/items/${id}`} transitionTypes={['nav-forward']}>Open</Link>
<Link href="/items" transitionTypes={['nav-back']}>← Items</Link>

export function DirectionalTransition({ children }) {
  return (
    <ViewTransition
      enter={{ 'nav-forward': 'nav-forward', 'nav-back': 'nav-back', default: 'none' }}
      exit={{ 'nav-forward': 'nav-forward', 'nav-back': 'nav-back', default: 'none' }}
      default="none"
    >
      {children}
    </ViewTransition>
  );
}

Tabs sit side by side

Switch tabs in each pane.

Directional slideBad
enter="nav-forward"
Cross-fadeGood
<ViewTransition> + sliding bar
{tabs.map(t => (
  <button key={t.value} onClick={() => startTransition(() => setActive(t.value))}>
    {t.label}
    {active === t.value && (
      <ViewTransition name="tab-indicator" share="tab-underline">
        <span className="active-underline" />
      </ViewTransition>
    )}
  </button>
))}
<ViewTransition>
  <TabPanel tab={active} />
</ViewTransition>

Why it matters, pros and cons

Why it matters

Horizontal motion encodes position: forward slides left, back slides right. Tabs have no depth, so a slide claims a direction that isn't there.

Pros

  • One wrapper holds the type map.
  • Types stack; each boundary picks its own.
  • A cross-fade plus a sliding bar reads as "same place".

Cons

  • Browser back and router.back() carry no types.
  • You label every link by hand.
  • Suspense reveals after a navigation have no type.