Installation
pnpm dlx shadcn@latest add https://dithercn.dev/r/image-compare.jsonUsage
import { ImageCompare } from "@/components/dithercn/image-compare";
export function Showcase() {
return (
<ImageCompare
src="/photos/launch.jpg"
alt="The launch pad at dawn, dithered and original"
className="w-full rounded-xl"
/>
);
}Drag the divider, click anywhere on the image to send it there, or tab to the handle and use the arrow keys. By default the left side is dithered with the duotone preset and the right side is the original photo. When the image first comes into view, the divider nudges left and right once so readers see that it moves.
Each side is either { kind: "original" } or { kind: "dither", ...look }, where the look takes the same props as Dither image: preset, color, palette, method, cell and the rest.
Examples
Two dither looks
Both sides can be dithered. Here newsprint meets Game Boy greens.
Feature section
A landing page block that shows what the brand treatment does to a photo.
Props
| Prop | Type | Default |
|---|---|---|
| src* Image URL. Cross-origin images need CORS headers. | Type: string | Default – |
| alt* Describes the image and the comparison. Used as the group's label. | Type: string | Default – |
| before The left side. | Type: { kind: "original" } | { kind: "dither", ...DitherLook } | Default { kind: "dither" } |
| after The right side. | Type: { kind: "original" } | { kind: "dither", ...DitherLook } | Default { kind: "original" } |
| beforeLabel Label on the left side, also used in the slider's name. | Type: string | Default "Dithered" or "Original" |
| afterLabel Label on the right side. | Type: string | Default "Dithered" or "Original" |
| labels Show the corner labels. They fade out when the divider covers them. | Type: boolean | Default true |
| value Divider position from the left, 0 to 100. Makes the slider controlled. | Type: number | Default – |
| defaultValue Starting position when uncontrolled. | Type: number | Default 50 |
| onValueChange Called on every move, from pointer or keyboard. | Type: (value: number) => void | Default – |
| hint Nudge the divider once when the image first comes into view. | Type: boolean | Default true |
| ratio Width over height. | Type: number | Default The image's ratio |
| fit How the image fills its box, on both sides. | Type: "cover" | "contain" | Default "cover" |
Notes
- The handle is a
role="slider"with a value from 0 to 100. Arrow keys move it by 1, Shift and arrow or Page Up and Page Down by 10, Home and End to the ends. - Clicks glide the divider over 200ms; drags and key presses follow exactly.
- On touch screens a sideways drag moves the divider and a vertical swipe still scrolls the page.
- Under reduced motion there is no hint and no glide.