Select assets, compare aligned images, annotate a single image, approve or reject selections, and export a review with comments.
Review room
0 selected · 0 approved
Select one asset to annotate, or two aligned images to compare.
pnpm dlx shadcn@latest add @latheui/review-contact-sheetPart of Lathe Pro. Set up your project once with your license key: npx latheui init --key <key>. See Lathe Pro.
Review Contact Sheet is part of Lathe Pro
The preview is live and free to try. The source, the install and every assembly come with Pro: $79 once, with lifetime updates.
Depends on
| Prop | Type | Default | Description |
|---|---|---|---|
| value / defaultValue | ReviewAsset[] | [] (defaultValue) | Controlled or initial assets with unique id, title, src, alt and optional pending/approved/rejected status. |
| onValueChange | (assets: ReviewAsset[]) => void | — | Receives updated decisions after persistence succeeds. |
| onReview | (ids: string[], decision, signal: AbortSignal) => void | Promise<void> | — | Optional persistence callback. Reject for retry, honor signal for cancellation; omit for local review. |
| onAnnotationsChange | (assetId: string, notes: Annotation[]) => void | — | Receive annotation edits for external persistence. Notes are also kept locally for export. |
| disabled | boolean | false | Disable selection, decisions, annotations and export. |
| className | string | — | Classes for the section. |
Replace the demo data and callbacks with your application. Honor AbortSignal in asynchronous callbacks so cancellation also stops transport work.
"use client"
import * as React from "react"
import { Checkbox } from "@/components/ui/checkbox"
import { ReviewContactSheet } from "@/components/review-contact-sheet"
const assets = [
{
id: "original",
title: "Lake · original",
src: "/demo/photos/compare-flat.webp",
alt: "An autumn lake before color grading",
},
{
id: "graded",
title: "Lake · graded",
src: "/demo/photos/compare-graded.webp",
alt: "An autumn lake after color grading",
},
{
id: "coast",
title: "Coastal study",
src: "/demo/photos/fermin-rodriguez-penelas-b8keuzqmnoq.webp",
alt: "A bay at sunset",
},
]
export default function ReviewDemo() {
const [fail, setFail] = React.useState(false)
return (
<div className="w-full max-w-2xl space-y-3">
<ReviewContactSheet
defaultValue={assets}
onReview={async (_ids, _decision, signal) => {
await new Promise((resolve) => setTimeout(resolve, 400))
if (signal.aborted) return
if (fail) {
setFail(false)
throw new Error("Review could not be saved. Try again.")
}
}}
/>
<Checkbox
label="Fail next review"
checked={fail}
onCheckedChange={(checked) => setFail(checked === true)}
className="text-xs text-muted-foreground"
/>
</div>
)
}