# 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** #### React ```tsx import { Swap } from '@ark-ui/react/swap' import { CheckIcon, XIcon } from 'lucide-react' import { useState } from 'react' import styles from 'styles/swap.module.css' export const Fade = () => { const [swapped, setSwapped] = useState(false) return ( ) } ``` #### Solid ```tsx import { Swap } from '@ark-ui/solid/swap' import { CheckIcon, XIcon } from 'lucide-solid' import { createSignal } from 'solid-js' import styles from 'styles/swap.module.css' export const Fade = () => { const [swapped, setSwapped] = createSignal(false) return ( ) } ``` #### Vue ```vue ``` #### Svelte ```svelte ``` ### Flip Add a 3D flip effect by setting `perspective` on the root and using `rotateY` keyframes on the indicators. **Example: flip** #### React ```tsx import { Swap } from '@ark-ui/react/swap' import { PauseIcon, PlayIcon } from 'lucide-react' import { useState } from 'react' import styles from 'styles/swap.module.css' export const Flip = () => { const [swapped, setSwapped] = useState(false) return ( ) } ``` #### Solid ```tsx import { Swap } from '@ark-ui/solid/swap' import { PauseIcon, PlayIcon } from 'lucide-solid' import { createSignal } from 'solid-js' import styles from 'styles/swap.module.css' export const Flip = () => { const [swapped, setSwapped] = createSignal(false) return ( ) } ``` #### Vue ```vue ``` #### Svelte ```svelte ``` ### Rotate Rotate the indicators in and out with a spin transition. **Example: rotate** #### React ```tsx import { Swap } from '@ark-ui/react/swap' import { MoonIcon, SunIcon } from 'lucide-react' import { useState } from 'react' import styles from 'styles/swap.module.css' export const Rotate = () => { const [swapped, setSwapped] = useState(false) return ( ) } ``` #### Solid ```tsx import { Swap } from '@ark-ui/solid/swap' import { MoonIcon, SunIcon } from 'lucide-solid' import { createSignal } from 'solid-js' import styles from 'styles/swap.module.css' export const Rotate = () => { const [swapped, setSwapped] = createSignal(false) return ( ) } ``` #### Vue ```vue ``` #### Svelte ```svelte ``` ### Scale Scale the indicators up and down for a pop-in effect. **Example: scale** #### React ```tsx import { Swap } from '@ark-ui/react/swap' import { Volume2Icon, VolumeXIcon } from 'lucide-react' import { useState } from 'react' import styles from 'styles/swap.module.css' export const Scale = () => { const [swapped, setSwapped] = useState(false) return ( ) } ``` #### Solid ```tsx import { Swap } from '@ark-ui/solid/swap' import { Volume2Icon, VolumeXIcon } from 'lucide-solid' import { createSignal } from 'solid-js' import styles from 'styles/swap.module.css' export const Scale = () => { const [swapped, setSwapped] = createSignal(false) return ( ) } ``` #### Vue ```vue ``` #### Svelte ```svelte ``` ## 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** #### React **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. | #### Solid **Root Props:** | Prop | Type | Required | Description | |------|------|----------|-------------| | `asChild` | `(props: ParentProps<'span'>) => Element` | 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` | `(props: ParentProps<'span'>) => Element` | No | Use the provided child element as the default rendered element, combining their props and behavior. | **RootProvider Props:** | Prop | Type | Required | Description | |------|------|----------|-------------| | `value` | `Accessor` | Yes | | | `asChild` | `(props: ParentProps<'span'>) => Element` | No | Use the provided child element as the default rendered element, combining their props and behavior. | #### Vue **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. | #### Svelte **Root Props:** | Prop | Type | Required | Description | |------|------|----------|-------------| | `asChild` | `Snippet<[PropsFn<'span'>]>` | No | Use the provided child element as the default rendered element, combining their props and behavior. | | `lazyMount` | `boolean` | No | Whether to enable lazy mounting | | `ref` | `Element` | No | | | `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` | `'off' | 'on'` | Yes | | | `asChild` | `Snippet<[PropsFn<'span'>]>` | No | Use the provided child element as the default rendered element, combining their props and behavior. | | `ref` | `Element` | No | | **RootProvider Props:** | Prop | Type | Required | Description | |------|------|----------|-------------| | `value` | `Accessor` | Yes | | | `asChild` | `Snippet<[PropsFn<'span'>]>` | No | Use the provided child element as the default rendered element, combining their props and behavior. | | `ref` | `Element` | No | | ### Context