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

# transition

> Sets the insertion and removal transition animation for a component.

export const PlatformStatuses = ({statuses}) => {
  const StatusBadge = ({status, label}) => {
    const styles = {
      green: {
        backgroundColor: '#dcfce7',
        color: '#166534'
      },
      orange: {
        backgroundColor: '#fed7aa',
        color: '#9a3412'
      },
      red: {
        backgroundColor: '#fecaca',
        color: '#991b1b'
      },
      gray: {
        backgroundColor: '#f3f4f6',
        color: '#4b5563'
      }
    };
    const baseStyle = {
      display: 'inline-flex',
      alignItems: 'center',
      padding: '0.125rem 0.625rem',
      borderRadius: '9999px',
      fontSize: '0.875rem',
      fontWeight: '500'
    };
    const colorStyle = styles[status] || styles.green;
    return <span style={{
      ...baseStyle,
      ...colorStyle
    }}>
        {label || status}
      </span>;
  };
  const STATUS_CONFIG = {
    supported: {
      label: "Supported",
      color: "green"
    },
    partial: {
      label: "Partial",
      color: "orange"
    },
    "not-implemented": {
      label: "Not Implemented",
      color: "gray"
    }
  };
  const renderCard = (platform, value) => {
    if (!value) return null;
    const {status, note} = typeof value === "string" ? {
      status: value
    } : value;
    const config = STATUS_CONFIG[status];
    if (!config) return null;
    const titleMap = {
      ios: "SwiftUI",
      android: "Jetpack Compose",
      web: "Web"
    };
    return <Card key={platform} title={titleMap[platform] || platform}>
          <StatusBadge status={config.color} label={config.label} />
          {note && <div style={{
      marginTop: '0.5rem',
      fontSize: '0.875rem',
      color: '#6b7280'
    }}>
              {note}
            </div>}
      </Card>;
  };
  if (statuses == null) {
    return null;
  }
  return <Columns cols="3">
      {Object.entries(statuses).map(([platform, value]) => renderCard(platform, value))}
    </Columns>;
};

export const ComposeJS = ({code, name, height}) => {
  const encodedCode = useMemo(() => {
    if (!code) return "";
    try {
      return btoa(code);
    } catch (e) {
      console.error("Failed to encode code", e);
      return "";
    }
  }, [code]);
  if (!encodedCode) {
    return null;
  }
  return <iframe src={`https://www.metabind.ai/embed?code=${encodedCode}&name=${name ?? 'Example'}`} loading="lazy" style={{
    width: "100%",
    height: height || '350px',
    border: "1px solid #e5e7eb",
    borderRadius: "var(--rounded-2xl,1rem)",
    overflow: "hidden"
  }} title="ComposeJS Preview" />;
};

```typescript theme={null}
.transition(
    type:
        | "opacity" | "slide" | "scale" | "identity" | "blurReplace"
        | { move: "top" | "bottom" | "leading" | "trailing" }
        | { push: "top" | "bottom" | "leading" | "trailing" }
        | { scale: number; anchor?: UnitPoint }
        | { offset: { x?: number; y?: number } }
        | { blurReplace: "downUp" | "upUp" }
        | { asymmetric: { insertion: SimpleTransition; removal: SimpleTransition } }
        | { combined: SimpleTransition[] }
): Component
```

<ParamField path="type" type="Transition" required>
  The transition to apply when the component is inserted or removed. Can be a built-in string or a configuration object.

  **Built-in transitions:**

  * `"opacity"` -- fades in/out
  * `"slide"` -- slides in from the leading edge and out to the trailing edge
  * `"scale"` -- scales from zero to full size
  * `"identity"` -- no animation (instant)
  * `"blurReplace"` -- cross-fades with a blur effect

  **Directional transitions:**

  * `{ move: edge }` -- slides in/out from the specified edge
  * `{ push: edge }` -- pushes the old content out as the new content pushes in from the specified edge

  **Parameterized transitions:**

  * `{ scale: number, anchor?: UnitPoint }` -- scales from a specific value, optionally around an anchor point
  * `{ offset: { x?, y? } }` -- slides in/out by a specific offset
  * `{ blurReplace: "downUp" | "upUp" }` -- blur replace with a directional style

  **Composite transitions:**

  * `{ asymmetric: { insertion, removal } }` -- different transitions for insertion and removal
  * `{ combined: [...] }` -- multiple transitions applied simultaneously
</ParamField>

## Support

<PlatformStatuses
  statuses={{
ios: { status: "supported" },
android: "not-implemented",
web: "not-implemented",
}}
/>

## Usage

### Opacity (fade)

```typescript theme={null}
const body = () => {
    const [isVisible, setIsVisible] = useState(true)
    return VStack([
        Button("Toggle", () => setIsVisible(!isVisible)),
        isVisible ? Text("Hello").transition("opacity") : Empty()
    ])
}
```

### Slide

```typescript theme={null}
const body = () => {
    const [show, setShow] = useState(false)
    return VStack([
        Button("Toggle", () => setShow(!show)),
        show ? Text("Sliding text").transition("slide") : Empty()
    ])
}
```

### Scale

```typescript theme={null}
const body = () => {
    const [show, setShow] = useState(false)
    return VStack([
        Button("Toggle", () => setShow(!show)),
        show ? Circle()
            .frame({ width: 50, height: 50 })
            .foregroundStyle(Color("blue"))
            .transition("scale") : Empty()
    ])
}
```

### Move from edge

```typescript theme={null}
const body = () => {
    const [show, setShow] = useState(false)
    return VStack([
        Button("Toggle", () => setShow(!show)),
        show ? Text("From bottom")
            .transition({ move: "bottom" }) : Empty()
    ])
}
```

### Push from edge

```typescript theme={null}
const body = () => {
    const [show, setShow] = useState(false)
    return VStack([
        Button("Toggle", () => setShow(!show)),
        show ? Text("Pushed in")
            .transition({ push: "leading" }) : Empty()
    ])
}
```

### Scale with anchor

```typescript theme={null}
const body = () => {
    const [show, setShow] = useState(false)
    return VStack([
        Button("Toggle", () => setShow(!show)),
        show ? Text("Scale from top")
            .transition({ scale: 0, anchor: "top" }) : Empty()
    ])
}
```

### Offset transition

```typescript theme={null}
const body = () => {
    const [show, setShow] = useState(false)
    return VStack([
        Button("Toggle", () => setShow(!show)),
        show ? Text("Offset")
            .transition({ offset: { x: 100, y: 0 } }) : Empty()
    ])
}
```

### Blur replace

```typescript theme={null}
const body = () => {
    const [show, setShow] = useState(false)
    return VStack([
        Button("Toggle", () => setShow(!show)),
        show ? Text("Blur")
            .transition({ blurReplace: "downUp" }) : Empty()
    ])
}
```

### Asymmetric transition

Use a different animation for insertion and removal.

```typescript theme={null}
const body = () => {
    const [show, setShow] = useState(false)
    return VStack([
        Button("Toggle", () => setShow(!show)),
        show ? Text("Asymmetric")
            .transition({
                asymmetric: {
                    insertion: "slide",
                    removal: "opacity"
                }
            }) : Empty()
    ])
}
```

### Combined transitions

Apply multiple transitions simultaneously.

```typescript theme={null}
const body = () => {
    const [show, setShow] = useState(false)
    return VStack([
        Button("Toggle", () => setShow(!show)),
        show ? Text("Combined")
            .transition({
                combined: ["opacity", "scale"]
            }) : Empty()
    ])
}
```

## Notes

* Transitions only play when a component is conditionally inserted or removed from the view hierarchy.
* The `"identity"` transition can be used to explicitly disable animations on insertion/removal.
