photocn

Search

Search the docs

0

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-editor

Choose 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:

PropTypeDescription
editorImageEditorApiAn editor you created with useImageEditorState().
toolsEditorToolbarTool[]The tools to show. Default: all of them.
panelsRecord<toolId, ReactNode>Replace a panel or add your own.
layout"auto" | "desktop" | "compact"Default "auto": compact below 640px.
showOpenButtonbooleanShow the Open button. Default true.
showRecipesbooleanShow the save/load look buttons. Default true.
showHistogrambooleanShow the histogram. Default true.
toolbarExtraReactNodeExtra buttons, placed before Export.
onSave(result) => voidAdds a Save button to the export panel, e.g. to upload the result.