July 31, 2026

Deltaphoto

A small image comparison component for React. Give it two photos and it handles the rest.

The apartment living room after renovationAn apartment living room before renovation
Drag or tap anywhere on the image.

I kept needing the same interaction: compare a redesign, show a restoration, explain an edit, or make a visual change obvious without asking someone to flip between two images. The available components worked, but most of them felt like unfinished plumbing rather than something I wanted to put on a page.

So I made the version I wanted to reach for: deliberately small, already designed, and useful without a setup screen full of decisions.


Getting started

The finished API is only two required props. Everything else is an optional refinement for a particular comparison.

DeltaphotoThe package is bundled locally while its npm release is prepared.
Example.tsx
import { Deltaphoto } from "deltaphoto";
import "deltaphoto/styles.css";

<Deltaphoto
  before="/room-before.jpg"
  after="/room-after.jpg"
/>

It fills the width of its parent and keeps a 3:2 aspect ratio by default. The component ships with its interaction styles, so the surrounding layout is entirely yours.


The interaction

The divider is built on a native range input. That means the same control works with a mouse, a finger, a trackpad, and a keyboard instead of maintaining a separate path for each one.

Drag anywhere

The full image is the target, not just the small handle.

Scroll naturally

Vertical page movement stays available on touch screens.

Keyboard stops

Arrow keys jump cleanly between the start, middle, and end.

Smooth drag

The divider eases toward the pointer without lagging behind it.


Making it yours

The defaults are meant to be the version you ship. The options are there for real content differences—labels, framing, and controlled state—not for rebuilding the component piece by piece.

Custom labels and framing
<Deltaphoto
  before="/kitchen-old.jpg"
  after="/kitchen-new.jpg"
  beforeLabel="Original"
  afterLabel="Renovated"
  initialPosition={38}
  aspectRatio="16 / 10"
  foregroundColor="#f7f3ea"
  backgroundColor="#1b1a18"
/>

Pass a class name or normal inline styles to the root when the comparison needs a different radius or surrounding treatment. Both images always share the same crop, which keeps the comparison visually honest.


Props

PropTypeDefaultDescription
beforestringrequiredURL for the image shown on the left.
afterstringrequiredURL for the image shown on the right.
beforeAltstring"Before"Alternative text for the before image.
afterAltstring"After"Alternative text for the after image.
beforeLabelstring"Before"Label shown over the before image.
afterLabelstring"After"Label shown over the after image.
showLabelsbooleantrueShow or hide both image labels.
initialPositionnumber50Initial divider position from 0 to 100.
positionnumberControlled divider position from 0 to 100.
onPositionChangefunctionRuns whenever the divider moves.
aspectRatioCSS value"3 / 2"Aspect ratio of the comparison surface.
objectFitCSS value"cover"How both images fit inside the surface.
foregroundColorCSS color"#000"Color used for the grip and component text.
backgroundColorCSS color"#fff"Color used for labels, the divider, and the handle.
ariaLabelstringcomparison descriptionAccessible name for the range control.
classNamestring""Class applied to the root element.

Standard div attributes, including data attributes and event handlers, are passed to the root element.


Package status

The component is working and packaged locally. I am using it on this page before publishing it more broadly—the best way to find the awkward edges is to depend on it yourself.

The npm release is still to come. Until then, this page is the source of truth for what the component is meant to do.