Command Palette

Search for a command to run...

Image Compare

A before/after image comparison slider. Drag (or hover) to reveal one image over another, horizontally or vertically, with optional labels, a custom handle, and a loading skeleton. Responsive out of the box.

A before/after image comparison slider. Two images are stacked in the same box; a draggable divider clips the top image to reveal the one beneath. Drag the handle — or set hover to reveal on pointer move — to wipe between them. The container always fits the parent width and derives its height from the images' aspect ratio, so it stays responsive.

It is self-contained: a single ResizeObserver tracks the width and pointer-capture drives the slider. Styling is plain Tailwind classes, with only the dynamic values (height, clip-path, slider offset) applied inline, and the default handle uses lucide-react chevrons. Labels, a custom handle, and a loading skeleton are all optional.

Installation

One-time setup: add the @ikui registry to your components.json.

components.json
{
  "registries": {
    "@ikui": "https://ik-ui.pages.dev/r/{name}.json"
  }
}

Then install the component:

pnpm dlx shadcn@latest add @ikui/image-compare

Usage

import { ImageCompare } from "@/components/image-compare";
<ImageCompare
  leftImage="/before.png"
  rightImage="/after.png"
  leftImageLabel="Before"
  rightImageLabel="After"
/>

Examples

Vertical

Set vertical to compare top vs. bottom instead of left vs. right. The leftImage becomes the top image and the rightImage the bottom one.

Fill the parent

By default the container derives its height from the images' aspect ratio. Set fill to make it follow the parent's height instead — the parent must have a definite height. Pair it with objectFit="contain" to letterbox images so they are never cropped, regardless of how the parent's size matches the images' ratio.

Props

PropTypeDefault
leftImage
string
-
rightImage
string
-
vertical
boolean
false
hover
boolean
false
aspectRatio
"taller" | "wider"
"taller"
fill
boolean
false
objectFit
"cover" | "contain"
"cover"
sliderPositionPercentage
number
0.5
onSliderPositionChange
(position: number) => void
-
leftImageLabel
ReactNode
-
rightImageLabel
ReactNode
-
leftImageAlt
string
""
rightImageAlt
string
""
handle
ReactNode
-
skeleton
ReactNode
-
className
string
-

ImageCompare forwards a ref to its outermost container element (HTMLDivElement), so you can measure or scroll it.