# Image Cropper

Photo crop screen where you pan and pinch under a fixed frame that never shows a gap. Straighten, rotate, flip and switch aspect ratios, with a round crop for profile photos.

> Composed · Pro · Mobile-Native — React Native, Expo Go incompatible (dev build required).

## Install

```bash
npx @lumioui/cli add pro/image-cropper
```

## Import

```tsx
import { DEFAULT_ASPECTS, ImageCropper } from "@/components/ui/image-cropper/image-cropper";
```

## Variants

- `cover` — Banner 3:1: `npx @lumioui/cli add pro/image-cropper-cover`
- `photo-edit` — Photo edit: `npx @lumioui/cli add pro/image-cropper-photo-edit`
- `profile` — Profile photo: `npx @lumioui/cli add pro/image-cropper-profile`

## Props

### ImageCropper

| Prop | Type | Notes |
| --- | --- | --- |
| `source` | `ImageSourcePropType` | The photo to crop. |
| `imageSize?` | `{ width: number` | Intrinsic pixel size. Resolved with Image.getSize when omitted. |
| `height` | `number }` | — |
| `aspect?` | `CropAspect` | Frame aspect (width / height) or `"original"`. Changing it morphs the frame in place. Default `"original"`. |
| `rotation?` | `number` | Controlled quarter-turn rotation in degrees (multiples of 90). |
| `defaultRotation?` | `number` | Uncontrolled initial rotation. Default 0. |
| `onRotationChange?` | `(rotation: number) => void` | — |
| `straighten?` | `number` | Controlled straighten angle in degrees, −45…45. |
| `defaultStraighten?` | `number` | Uncontrolled initial straighten. Default 0. |
| `onStraightenChange?` | `(deg: number) => void` | Fires when the straighten ruler settles on a whole degree (and on flip / reset). |
| `flipped?` | `boolean` | Controlled horizontal mirror. |
| `defaultFlipped?` | `boolean` | Uncontrolled initial mirror. Default false. |
| `onFlippedChange?` | `(flipped: boolean) => void` | — |
| `ruler?` | `boolean` | Show the straighten ruler under the photo. Default false. |
| `shape?` | `"rect" | "circle"` | `rect` (default) or `circle` — a round avatar mask (forces a square frame). |
| `grid?` | `"auto" | "always" | "never"` | Rule-of-thirds grid: `auto` shows while touching (default), `always`, or `never`. |
| `maxZoom?` | `number` | Maximum zoom over the cover scale. Default 5. |
| `inset?` | `number` | Inset between the stage edge and the frame. Default 20. |
| `onCropChange?` | `(crop: CropRect) => void` | Fires with the crop each time the pose settles. |
| `onInteractionChange?` | `(active: boolean) => void` | Fires when a pan, pinch or straighten starts / ends. |
| `committed?` | `boolean` | The crop is confirmed: the mask closes, the framed photo lifts and  gestures lock. Back to false reopens the editor. Default false. |
| `onCommitSettled?` | `() => void` | Fires once the commit has landed and held (~850ms after `committed`) —  the moment to move on. |
| `accessibilityLabel?` | `string` | Screen-reader description. Default "Crop photo". |
| `className?` | `string` | — |

### StraightenDial

| Prop | Type | Notes |
| --- | --- | --- |
| `value` | `number` | Angle in degrees. |
| `onValueChange` | `(deg: number) => void` | Fires when the ruler settles on a whole degree (drag release, tap on the  readout, screen-reader step). |
| `range?` | `number` | Range either side of zero. Default 45. |
| `tone?` | `"auto" | "light"` | `auto` follows the theme; `light` draws white ink for a black stage. Default `auto`. |
| `disabled?` | `boolean` | — |
| `live?` | `SharedValue<number>` | Written every frame with the live angle — link it to a stage on the UI thread. |
| `activity?` | `SharedValue<number>` | Eases 0 → 1 while the ruler is in use (for guides that follow it). |
| `onActiveChange?` | `(active: boolean) => void` | Fires when a drag starts and when it has settled. |
| `className?` | `string` | — |

### AspectPicker

| Prop | Type | Notes |
| --- | --- | --- |
| `value` | `CropAspect` | — |
| `onValueChange` | `(aspect: CropAspect) => void` | — |
| `options?` | `AspectOption[]` | Presets, laid out in equal widths. Default Original / Square / 4:5 / 16:9. |
| `disabled?` | `boolean` | — |
| `className?` | `string` | — |

### CropTool

| Prop | Type | Notes |
| --- | --- | --- |
| `kind` | `"rotate" | "flip"` | `rotate` turns 90° counter-clockwise; `flip` mirrors. The glyph performs it. |
| `onPress` | `() => void` | — |
| `disabled?` | `boolean` | — |
| `accessibilityLabel?` | `string` | — |
| `className?` | `string` | — |

### CropReset

| Prop | Type | Notes |
| --- | --- | --- |
| `changed` | `boolean` | The crop differs from the untouched photo — "Reset" ignites in place of the label. |
| `onPress` | `() => void` | — |
| `label?` | `string` | Shown while nothing has changed. Default "Crop". |
| `resetLabel?` | `string` | Default "Reset". |
| `disabled?` | `boolean` | — |
| `className?` | `string` | — |

Source: https://lumioui.com/components/image-cropper