Image Comparison
Before/after drag slider. Clip the left panel to the drag position. Works with any React children.
Use and inspectUse this when the motion supports the surrounding content. Connect callbacks and state to your app, keep the reset preview as a local demonstration, and add a reduced-motion fallback for movement or looping. The preview is synthetic; it does not call a backend.Next: inspect the source below and replace the demo inputs with your own data.
Image ComparisonBefore/after drag slider. Clip the left panel to the drag position. Works with any React children.
"use client";
import { useState, useRef } from "react";
type ImageComparisonProps = {
before: React.ReactNode;
after: React.ReactNode;
defaultPos?: number;
};
export function ImageComparison({ before, after, defaultPos = 50 }: ImageComparisonProps) {
const [pos, setPos] = useState(defaultPos);
const [dragging, setDragging] = useState(false);
const containerRef = useRef<HTMLDivElement>(null);
function getPercent(clientX: number) {
const el = containerRef.current;
if (!el) return pos;
const rect = el.getBoundingClientRect();
const x = Math.max(0, Math.min(clientX - rect.left, rect.width));
return (x / rect.width) * 100;
}
function handleMouseMove(e: React.MouseEvent) {
if (!dragging) return;
setPos(getPercent(e.clientX));
}
function handleTouchMove(e: React.TouchEvent) {
setPos(getPercent(e.touches[0].clientX));
}
return (
<div
ref={containerRef}
className="mp-image-comparison"
onMouseMove={handleMouseMove}
onMouseUp={() => setDragging(false)}
onMouseLeave={() => setDragging(false)}
>
<div className="mp-image-comparison-after">{after}</div>
<div className="mp-image-comparison-before" style={{ width: pos + "%" }}>
<div className="mp-image-comparison-before-inner">{before}</div>
</div>
<div
className="mp-image-comparison-handle"
style={{ left: pos + "%" }}
onMouseDown={() => setDragging(true)}
onTouchMove={handleTouchMove}
>
<div className="mp-image-comparison-handle-bar" />
</div>
</div>
);
}Props
| Prop | Type | Default | Description |
|---|---|---|---|
| beforerequired | ReactNode | — | Before panel content |
| afterrequired | ReactNode | — | After panel content |
| defaultPos | number | 50 | Initial slider position (0–100) |