# Quick start

> [llms.txt](https://zriz.dev/llms.txt)

Go from an empty project to a cropper that hands you the crop.

## Set it up with an agent

Paste this prompt into Claude Code, Cursor or any other coding agent. It reads this page and does the steps below for you:

```text
Add zriz to this project. Read https://zriz.dev/docs/quick-start.md and follow it: install @zriz/react and @zriz/core, import its stylesheet and render a cropper with corner handles, the straighten dial and a file picker for the photo. Build it from the zriz parts, not a crop box of your own, and give every part that asks for one an aria-label. When it works, show me where onValueCommit hands back the crop.
```

## Install

```sh
pnpm add @zriz/react @zriz/core
```

npm, Yarn and Bun work too. [FAQ](https://zriz.dev/docs/faq.md) lists what zriz needs.

## Render a cropper

Save this component in its own file and render `<PhotoCropper />` from a page. It loads a sample photo, and the stylesheet gives it the default look:

```tsx
"use client";

import * as Cropper from "@zriz/react/cropper";
import "@zriz/react/styles.css";

const photo = "https://picsum.photos/id/1015/1600/1200";

export const PhotoCropper = () => {
  return (
    <Cropper.Root src={photo}>
      <Cropper.Viewport
        style={{ maxWidth: 400, height: 300, padding: 24 }}
      >
        <Cropper.Image />
        <Cropper.Mask />
        <Cropper.Area aria-label="Crop area">
          <Cropper.Handle position="top-left" />
          <Cropper.Handle position="top-right" />
          <Cropper.Handle position="bottom-right" />
          <Cropper.Handle position="bottom-left" />
        </Cropper.Area>
      </Cropper.Viewport>
      <Cropper.Dial
        aria-label="Straighten"
        style={{ maxWidth: 361 }}
      >
        <Cropper.DialTicks />
      </Cropper.Dial>
    </Cropper.Root>
  );
};
```

`Viewport` needs a size, and the image and the mask fill it. The 24 px padding leaves room for the handles, see [viewport](https://zriz.dev/docs/viewport.md). The cropper now drags, pinches, resizes and straightens:

The page at https\://zriz.dev/docs/quick-start runs this cropper: a 3:2 photo with four corner handles, the straighten dial, and open, reset and download in the corners.

The demo above runs this component at the zriz site's size, with a public domain photo, open, reset and download buttons and the site's styles. Like the code, it shows no angle above the dial.

## Let the user pick a photo

Keep the chosen file in state and pass it to `src`:

```tsx
"use client";

import * as Cropper from "@zriz/react/cropper";
import "@zriz/react/styles.css";
import { useState } from "react";

export const PhotoCropper = () => {
  const [photo, setPhoto] = useState<File | null>(null);

  return (
    <>
      <input
        aria-label="Choose a photo"
        type="file"
        accept="image/*"
        onChange={(e) => setPhoto(e.target.files?.[0] ?? null)}
      />
      <Cropper.Root
        src={photo}
        onError={(error) => console.error(error.code)}
      >
        <Cropper.Viewport
          style={{ maxWidth: 400, height: 300, padding: 24 }}
        >
          <Cropper.Image />
          <Cropper.Mask />
          <Cropper.Area aria-label="Crop area">
            <Cropper.Handle position="top-left" />
            <Cropper.Handle position="top-right" />
            <Cropper.Handle position="bottom-right" />
            <Cropper.Handle position="bottom-left" />
          </Cropper.Area>
        </Cropper.Viewport>
        <Cropper.Dial
          aria-label="Straighten"
          style={{ maxWidth: 361 }}
        >
          <Cropper.DialTicks />
        </Cropper.Dial>
      </Cropper.Root>
    </>
  );
};
```

`src` takes a URL, a `File` or a `Blob`. While it is `null`, the viewport stays empty. When a photo cannot load, `onError` gets a `ZrizError`, and [errors](https://zriz.dev/docs/errors.md) lists its codes. A URL from another domain needs CORS headers, and a new photo keeps the current crop. [Loading images](https://zriz.dev/docs/loading-images.md) explains both.

## Get the crop

Add `onValueCommit` to `Root` in the picker above. It fires once when a gesture ends, a key press on the crop or the dial included, or on the frame after a write such as `rotate90()`. The first photo sends no commit until the user touches it, and a later photo sends one only when it has to refit the crop, see [events](https://zriz.dev/docs/events.md).

```tsx
<Cropper.Root
  src={photo}
  onError={(error) => console.error(error.code)}
  onValueCommit={(value, details) => {
    console.log(value, details.pixelCrop);
  }}
>
```

`value` is the crop as fractions of the photo, ready to store and load back. [Value](https://zriz.dev/docs/value.md) lists every field. `details.pixelCrop` is the export plan for that crop in source pixels, or `null` before a photo loads. Its `pixelRect` is the crop rectangle, and it is `null` with a straighten angle or perspective. `getPixelCrop()` on the cropper from `actionsRef` reads it for an untouched photo, see [hooks](https://zriz.dev/docs/hooks.md). [Exporting](https://zriz.dev/docs/exporting.md) turns the crop into a file in the browser or on your server.

[Playground](https://zriz.dev/docs/playground.md) changes the main props live, and [full editor](https://zriz.dev/docs/full-editor.md) puts every part in one component.
