> ## Documentation Index
> Fetch the complete documentation index at: https://docs.twenty.com/llms.txt
> Use this file to discover all available pages before exploring further.

# AnimatedIconCrossfade

> Crossfade between two icons as application state changes.

export const StoryEmbed = ({storyId, title, height = 120}) => <>
    <Tabs>
      <Tab title="Light">
        <iframe title={`${title} (light)`} src={`https://storybook.twenty.com/iframe.html?id=${storyId}&viewMode=story&globals=colorScheme:light`} width="100%" height={height} loading="lazy" allow="clipboard-write" style={{
  border: 0
}} />
      </Tab>
      <Tab title="Dark">
        <iframe title={`${title} (dark)`} src={`https://storybook.twenty.com/iframe.html?id=${storyId}&viewMode=story&globals=colorScheme:dark`} width="100%" height={height} loading="lazy" allow="clipboard-write" style={{
  border: 0
}} />
      </Tab>
    </Tabs>
    <a href={`https://storybook.twenty.com/?path=/story/${storyId}`}>
      Open in Storybook
    </a>
  </>;

`AnimatedIconCrossfade` crossfades between `activeIcon` and `inactiveIcon` node content according to `isActive`. It does not own the action or state. Compose it inside a labeled [Button](/ui/primitives/input/button).

<StoryEmbed storyId="ui-components-layout-animatediconcrossfade--display" title="AnimatedIconCrossfade example" height={100} />

## Usage

```tsx theme={null}
import { useState } from 'react';
import { AnimatedIconCrossfade } from 'twenty-ui/components/layout';
import { IconPencil, IconX } from 'twenty-ui/icon';
import { Button } from 'twenty-ui/primitives/input';

export const EditToggle = () => {
  const [isEditing, setIsEditing] = useState(false);

  return (
    <Button
      size="sm"
      aria-expanded={isEditing}
      onClick={() => setIsEditing(!isEditing)}
      startIcon={
        <AnimatedIconCrossfade
          isActive={isEditing}
          activeIcon={<IconX size={14} />}
          inactiveIcon={<IconPencil size={14} />}
        />
      }
    >
      Edit actions
    </Button>
  );
};
```

Both nodes stay mounted as `isActive` changes, preserving their local state. The layers animate opacity and scale, and disable transitions for reduced motion. Both layers are decorative; keep the accessible action name on the surrounding control. For a standalone labeled image, set `role="img"` and `aria-label` on the root.

Native attributes, events, `className`, `style`, and `ref` target an inline span, which can sit inside a button. `render` replaces that target through Base UI composition. The root dimensions default to the small icon theme token. Size each icon node explicitly, and use root width and height styles for larger artwork.

Replace `ActiveIcon` and `InactiveIcon` constructors with lowercase `activeIcon` and `inactiveIcon` nodes. Move the former `size` option to the node dimensions and root styles.

## Props

<ParamField body="activeIcon" type="ReactNode" required>
  Decorative node content for the active state.
</ParamField>

<ParamField body="className" type="string">
  CSS class applied to the inline span root.
</ParamField>

<ParamField body="inactiveIcon" type="ReactNode" required>
  Decorative node content for the inactive state.
</ParamField>

<ParamField body="isActive" type="boolean" required>
  Shows activeIcon when true and inactiveIcon otherwise.
</ParamField>

<ParamField body="render" type="ReactElement<unknown, string | JSXElementConstructor<any>> | ComponentRenderFn<HTMLProps, {}>">
  Replaces the span root while preserving both transition layers.
</ParamField>

<ParamField body="style" type="CSSProperties">
  Inline styles applied to the root, including width and height.
</ParamField>


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.