The full editor
One component with every tool. Use it as is, or trim it down.
"use client";
import { ImageEditor } from "@/components/image-editor/image-editor";
export default function FullEditorExample() {
return <ImageEditor className="h-[680px] overflow-hidden rounded-xl border" src="/samples/mountain-lake.jpg" />;
}Installation
pnpm dlx shadcn@latest add @photocn/image-editorChoose the tools
tools sets which tools appear. panels replaces a built-in panel or adds a panel for your own tool. Custom panels use useImageEditor(), just like the built-in ones.
"use client";
import { CropIcon, SlidersHorizontalIcon, StickerIcon, WandSparklesIcon } from "lucide-react";
import { useImageEditor } from "photocn/react";
import { Button } from "@/components/ui/button";
import { ImageEditor } from "@/components/image-editor/image-editor";
// The full editor, trimmed to three built-in tools plus one of your own.
export default function CustomToolsExample() {
return (
<ImageEditor
className="h-[640px] overflow-hidden rounded-xl border"
defaultTool="filters"
panels={{ stamp: <StampPanel /> }}
showRecipes={false}
src="/samples/dog.jpg"
tools={[
{ value: "filters", label: "Filters", icon: WandSparklesIcon },
{ value: "adjust", label: "Adjust", icon: SlidersHorizontalIcon },
{ value: "compose", label: "Crop", icon: CropIcon },
{ value: "stamp", label: "Looks", icon: StickerIcon },
]}
/>
);
}
// Custom panels get the same API as the built-in ones.
function StampPanel() {
const editor = useImageEditor();
const looks = {
Moody: { exposure: -0.15, contrast: 0.35, shadows: -0.2 },
Airy: { exposure: 0.2, contrast: -0.15, highlights: -0.3 },
Punchy: { contrast: 0.45, bloom: 0.15 },
} as const;
return (
<div className="flex flex-col gap-2 p-4">
<h2 className="text-sm font-semibold">One-tap looks</h2>
{Object.entries(looks).map(([name, look]) => (
<Button
key={name}
onClick={() => editor.adjust.setLights({ ...editor.adjust.value.lights, ...look })}
variant="outline"
>
{name}
</Button>
))}
</div>
);
}On phones
When the editor is narrower than 640px, it switches to a phone layout: the tools move to a bar at the bottom, and each tool's controls open in a sheet. Set layout="compact" or layout="desktop" to choose a layout yourself. See Compact layout.
Props
ImageEditor takes every option of useImageEditorState, plus:
| Prop | Type | Description |
|---|---|---|
editor | ImageEditorApi | An editor you created with useImageEditorState(). |
tools | EditorToolbarTool[] | The tools to show. Default: all of them. |
panels | Record<toolId, ReactNode> | Replace a panel or add your own. |
layout | "auto" | "desktop" | "compact" | Default "auto": compact below 640px. |
showOpenButton | boolean | Show the Open button. Default true. |
showRecipes | boolean | Show the save/load look buttons. Default true. |
showHistogram | boolean | Show the histogram. Default true. |
toolbarExtra | ReactNode | Extra buttons, placed before Export. |
onSave | (result) => void | Adds a Save button to the export panel, e.g. to upload the result. |