# Swap URL: https://ark-ui.com/docs/utilities/swap Source: https://raw.githubusercontent.com/chakra-ui/ark/refs/heads/main/website/src/content/pages/utilities/swap.mdx Animate between two visual states with smooth transitions. --- ## Anatomy ```tsx ``` ## Examples ### Fade Swap between two icons with a fade animation. Set the `swap` prop to toggle between the `on` and `off` indicators. **Example: fade** ```ripple import { Swap } from 'ark-ripple/swap'; import { Check, X } from 'lucide-ripple'; import { track } from 'ripple'; import styles from 'styles/swap.module.css'; export component Fade() { let swapped = track(false); } ``` ### Flip Add a 3D flip effect by setting `perspective` on the root and using `rotateY` keyframes on the indicators. **Example: flip** ```ripple import { Swap } from 'ark-ripple/swap'; import { Pause, Play } from 'lucide-ripple'; import { track } from 'ripple'; import styles from 'styles/swap.module.css'; export component Flip() { let swapped = track(false); } ``` ### Rotate Rotate the indicators in and out with a spin transition. **Example: rotate** ```ripple import { Swap } from 'ark-ripple/swap'; import { Moon, Sun } from 'lucide-ripple'; import { track } from 'ripple'; import styles from 'styles/swap.module.css'; export component Rotate() { let swapped = track(false); } ``` ### Scale Scale the indicators up and down for a pop-in effect. **Example: scale** ```ripple import { Swap } from 'ark-ripple/swap'; import { Volume2, VolumeX } from 'lucide-ripple'; import { track } from 'ripple'; import styles from 'styles/swap.module.css'; export component Scale() { let swapped = track(false); } ``` ## 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: ```css .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: ```css .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: ```css .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. ```tsx ... ... ``` ## API Reference ### Props **Component API Reference** **Root Props:** | Prop | Type | Required | Description | |------|------|----------|-------------| | `asChild` | `boolean` | No | Use the provided child element as the default rendered element, combining their props and behavior. | | `lazyMount` | `boolean` | No | Whether to enable lazy mounting | | `swap` | `boolean` | No | Whether the swap is in the "on" state. | | `unmountOnExit` | `boolean` | No | Whether to unmount on exit. | **Indicator Props:** | Prop | Type | Required | Description | |------|------|----------|-------------| | `type` | `'on' | 'off'` | Yes | | | `asChild` | `boolean` | No | Use the provided child element as the default rendered element, combining their props and behavior. | **RootProvider Props:** | Prop | Type | Required | Description | |------|------|----------|-------------| | `value` | `UseSwapReturn` | Yes | | | `asChild` | `boolean` | No | Use the provided child element as the default rendered element, combining their props and behavior. | ### Context