---
title: Cambio Image
description: A molecular image component featuring physics-based zoom transitions, blur-up loading, and gesture-driven dismissal
---

<ComponentPreview name="cambio-image-demo" />

This component is based on [Cambio Image by Raphael Salaja](https://cambio.raphaelsalaja.com/).

## Installation

<Installation name="cambio-image" dependencies={["cambio", "lucide-react"]} />

<Admonition type="warning" title="Required CSS">
To ensure the zoomed image sits above all other UI elements (like sticky navbars), add this style to your `globals.css`. This handles the z-index elevation when the component enters the `open` state.

```css
.root {
  isolation: isolate;
}

[data-state="open"] {
  z-index: 999 !important;
}
```

</Admonition>

## Usage

The component mirrors the standard HTML `<img>` API but requires explicit dimensions to calculate the zoom physics correctly.

```tsx
import { CambioImage } from "@/components/ui/cambio-image"

export default function Example() {
  return (
    <CambioImage
      src="https://images.unsplash.com/photo-1506905925346-21bda4d32df4?w=800&h=600&fit=crop"
      alt="Beautiful mountain landscape"
      width={800}
      height={600}
      motion="smooth"
      className="rounded-lg"
    />
  )
}
```

## Examples

### Grid Gallery

When rendering images in a grid/masonry layout, the browser's stacking context can cause overlapping issues during the closing animation.

Pass the `index` prop (usually from your map function) to ensure the active image always animates above its neighbors.

```tsx
<div className="grid grid-cols-3 gap-4">
  {images.map((img, i) => (
    <CambioImage
      key={img.src}
      src={img.src}
      index={i} // Critical for correct layering
      {...img}
    />
  ))}
</div>
```

<ComponentPreview name="cambio-image-grid-demo" />

### Dismiss on Scroll

For a more fluid mobile experience, you can enable dismissOnScroll. This mimics native mobile gallery behavior where scrolling away closes the image.

```tsx
<CambioImage
  src="..."
  dismissOnScroll={true}
  dismissOnImageClick={true} // Optional: click image to close
/>
```

<ComponentPreview name="cambio-image-dismiss-on-scroll-demo" />

<Admonition type="note" title="Scroll Context">
  The `dismissOnScroll` behavior may not function as expected when the component
  is rendered within constrained scroll containers such as demo previews or tab
  panels used above. For the best experience testing this functionality, view
  the [full-screen demo](/view/cambio-image-dismiss-on-scroll-demo) where scroll
  events propagate correctly.
</Admonition>

### Motion Presets

You can customize the animation physics using the motion prop. See the Motion Presets table in the [API Reference](#api-reference) for all available options.

```tsx
<div className="flex gap-4">
  {/* Standard snappy feel */}
  <CambioImage src="/img-1.jpg" motion="snappy" width={400} height={300} />

  {/* Bouncy, playful feel */}
  <CambioImage src="/img-2.jpg" motion="bouncy" width={400} height={300} />
</div>
```

<ComponentPreview name="cambio-image-motion-preset-demo" />

### Icon-Only Expand Mode

For a cleaner UI with explicit expand/collapse controls, use `showExpandIcon` and `iconsOnlyMode`. This displays floating expand/close buttons instead of relying on click-to-zoom behavior.

```tsx
<CambioImage
  src="..."
  showExpandIcon={true}
  iconsOnlyMode={true}
  dismissible={true}
/>
```

<ComponentPreview name="cambio-image-icons-expand-demo" />

## Notes

### Disabling Initial Blur

The component uses an `IntersectionObserver` to trigger a blur-to-focus animation when the image enters the viewport. For images "above the fold" (LCP candidates), you should disable this to prevent layout shifts or visual delays.

```tsx
<CambioImage src="/hero.jpg" enableInitialAnimation={false} loading="eager" />
```

### Advanced Motion Config

For granular control, you can pass an object to the `motion` prop to configure specific phases of the animation independently:

```tsx
<CambioImage
  motion={{
    trigger: "smooth", // The image expanding
    backdrop: "reduced", // The background fade
    popup: "bouncy", // The final resting state
  }}
/>
```

## API Reference

### Props

| Prop                     | Type                | Default    | Description                                                      |
| ------------------------ | ------------------- | ---------- | ---------------------------------------------------------------- |
| `src`                    | `string`            | `-`        | Required. The source URL.                                        |
| `alt`                    | `string`            | `-`        | Required. Accessibility text.                                    |
| `width`                  | `number`            | `-`        | Required. Intrinsic width.                                       |
| `height`                 | `number`            | `-`        | Required. Intrinsic height.                                      |
| `motion`                 | `Preset \| Config`  | `"snappy"` | Motion preset or custom config.                                  |
| `loading`                | `"lazy" \| "eager"` | `"lazy"`   | Image loading strategy.                                          |
| `index`                  | `number`            | `0`        | Z-index offset for list rendering.                               |
| `dismissible`            | `boolean`           | `true`     | Allow dismissing the zoomed image (disabled when iconsOnlyMode). |
| `dismissOnScroll`        | `boolean`           | `false`    | Close the modal on window scroll.                                |
| `dismissOnImageClick`    | `boolean`           | `false`    | Close by clicking the zoomed image itself.                       |
| `showExpandIcon`         | `boolean`           | `false`    | Display floating expand/close icons.                             |
| `iconsOnlyMode`          | `boolean`           | `false`    | Use only icons for expand/close, disable click-to-zoom behavior. |
| `enableInitialAnimation` | `boolean`           | `true`     | Blur-to-focus effect on viewport entry.                          |
| `draggable`              | `boolean`           | `false`    | Allows the image to be dragged (ghost image).                    |
| `className`              | `string`            | `-`        | Additional CSS classes for styling.                              |

### Motion Presets

| Preset    | Description                        |
| --------- | ---------------------------------- |
| `snappy`  | Fast, linear transition. (Default) |
| `smooth`  | Eased, slower transition.          |
| `bouncy`  | Spring-physics based animation.    |
| `reduced` | Minimal motion for accessibility.  |
