Utilities
Swap

Swap

Animate between two visual states with smooth transitions.

Loading...

Anatomy

<Swap.Root>
  <Swap.Indicator />
</Swap.Root>

Examples

Fade

Swap between two icons with a fade animation. Set the swap prop to toggle between the on and off indicators.

Flip

Add a 3D flip effect by setting perspective on the root and using rotateY keyframes on the indicators.

Rotate

Rotate the indicators in and out with a spin transition.

Scale

Scale the indicators up and down for a pop-in effect.

Guides

How It Works

Swap renders two indicators stacked on top of each other in a 1x1 CSS grid. The swap prop controls which indicator is visible. Each indicator uses the presence system, so you get data-state="open" and data-state="closed" attributes to drive your CSS animations.

Animating Indicators

Target data-state on each indicator to define enter and exit animations:

.indicator[data-state='open'] {
  animation: fade-in 200ms ease-out;
}

.indicator[data-state='closed'] {
  animation: fade-out 100ms ease-in;
}

@keyframes fade-in {
  from {
    opacity: 0;
  }
  to {
    opacity: 1;
  }
}

@keyframes fade-out {
  from {
    opacity: 1;
  }
  to {
    opacity: 0;
  }
}

You can combine animations for richer effects. For example, scale with fade:

.indicator[data-state='open'] {
  animation:
    scale-in 200ms ease-out,
    fade-in 200ms ease-out;
}

.indicator[data-state='closed'] {
  animation:
    scale-out 100ms ease-in,
    fade-out 100ms ease-in;
}

3D Flip Animation

For a flip effect, set perspective on the root and use backface-visibility: hidden on indicators:

.flip-indicator {
  backface-visibility: hidden;
}

.flip-indicator[data-state='open'] {
  animation: flip-in 400ms ease;
}

.flip-indicator[data-state='closed'] {
  animation: flip-out 200ms ease;
}

@keyframes flip-in {
  from {
    transform: rotateY(180deg);
  }
  to {
    transform: rotateY(0deg);
  }
}

@keyframes flip-out {
  from {
    transform: rotateY(0deg);
  }
  to {
    transform: rotateY(180deg);
  }
}

Lazy Mount

Use lazyMount and unmountOnExit to control when indicators mount and unmount. This keeps the DOM clean when indicators aren't visible.

<Swap.Root swap={swapped} lazyMount unmountOnExit>
  <Swap.Indicator type="on">...</Swap.Indicator>
  <Swap.Indicator type="off">...</Swap.Indicator>
</Swap.Root>

API Reference

Props

Root

Renders a <span> element.

PropDefaultType
asChild
boolean

Use the provided child element as the default rendered element, combining their props and behavior.

For more details, read our Composition guide.
lazyMountfalse
boolean

Whether to enable lazy mounting

swapfalse
boolean

Whether the swap is in the "on" state.

unmountOnExitfalse
boolean

Whether to unmount on exit.

Indicator

Renders a <span> element.

PropDefaultType
type
'on' | 'off'

asChild
boolean

Use the provided child element as the default rendered element, combining their props and behavior.

For more details, read our Composition guide.

RootProvider

Renders a <span> element.

PropDefaultType
value
UseSwapReturn

asChild
boolean

Use the provided child element as the default rendered element, combining their props and behavior.

For more details, read our Composition guide.

Context