Add this component

npx zentauri-ui add image-compare

Accessibility notes

Keyboard accessible by default with semantic markup, visible focus treatment, and tokenized states. Add descriptive labels for icon-only or decorative usage.

Dependency notes

Check Installation for shared peers. This component keeps styling in Tailwind classes and the --zui-* token contract.

Media

Image compare slider with a draggable divider

Reveal the difference between two aligned images by dragging a divider, clicking a position, or using the keyboard. Use it for edits, renovations, maps, product states, and visual QA.

Mountain landscape after color grading
Mountain landscape before color grading
BeforeAfter

Image compare variants playground

Adjust the appearance, size, corner radius, and initial divider position. Drag the handle or focus it and use Arrow, Page, Home, and End keys.


Mountain landscape after color grading
Mountain landscape before color grading
BeforeAfter

Image Compare API

Generated from the package prop types and variant definitions.

ImageCompare

ImageCompareProps

Variants

PropTypeDefault
appearance
bluecyandefaultemeraldgradient-bluegradient-sunsetgreenindigoorangepinkpurplered+6
default
disabled
falsetrue
false
radius
fulllgmdnonesm
md
size
lgmdsm
md

Behavior

PropTypeDefault
after*ReactNodenone
afterLabelReactNodenone
before*ReactNodenone
beforeLabelReactNodenone
defaultPositionnumber | undefinednone
onPositionChange((position: number) => void) | undefinednone
positionnumber | undefinednone
separatorLabelstring | undefinednone
stepnumber | undefinednone
Inherited HTML props
PropTypeDefault
classNamestring | undefinednone
idstring | undefinednone
onClickMouseEventHandler<HTMLDivElement> | undefinednone
styleCSSProperties | undefinednone
titlestring | undefinednone

ImageCompareVariant

ImageCompareVariantProps

Behavior

PropTypeDefault
appearance'default' | 'blue' | 'cyan' | 'green' | 'red' | 'slate' | 'zinc' | 'sky' | 'rose' | 'purple' | 'pink' | 'orange' | 'yellow' | 'teal' | 'indigo' | 'emerald' | 'gradient-blue' | 'gradient-sunset' | null | undefinednone
disabledboolean | null | undefinednone
radius'md' | 'sm' | 'lg' | 'none' | 'full' | null | undefinednone
size'md' | 'sm' | 'lg' | null | undefinednone
CSS variable overrides

Image compare CSS variables

Override these image compare variables on :root, a theme selector, or a component wrapper.

54 variables

Pattern: --zui-<component>-<slot?>-<variant?>-<property>-<state?>-dark?

:root {
  --zui-image-compare-bg: #f1f5f9;
  --zui-image-compare-border: #e2e8f0;
  --zui-image-compare-blue-accent: #2563eb;
  --zui-image-compare-cyan-accent: #0891b2;
  --zui-image-compare-default-accent: #0f172a;
  --zui-image-compare-emerald-accent: #059669;
  --zui-image-compare-focus-ring: #2563eb;
  --zui-image-compare-gradient-blue-accent: linear-gradient(180deg,#2563eb,#7c3aed);
  --zui-image-compare-gradient-sunset-accent: linear-gradient(180deg,#f97316,#e11d48);
  --zui-image-compare-green-accent: #16a34a;
  --zui-image-compare-handle-bg: #ffffff;
  --zui-image-compare-handle-border: #ffffff99;
  --zui-image-compare-handle-fg: #0f172a;
  --zui-image-compare-handle-shadow: 0 4px 16px #0f172a40;
  --zui-image-compare-indigo-accent: #4f46e5;
  --zui-image-compare-label-bg: #0f172abf;
  --zui-image-compare-label-fg: #ffffff;
  --zui-image-compare-orange-accent: #ea580c;
  --zui-image-compare-pink-accent: #db2777;
  --zui-image-compare-purple-accent: #7c3aed;
  --zui-image-compare-red-accent: #dc2626;
  --zui-image-compare-rose-accent: #e11d48;
  --zui-image-compare-sky-accent: #0284c7;
  --zui-image-compare-slate-accent: #475569;
  --zui-image-compare-teal-accent: #0d9488;
  --zui-image-compare-yellow-accent: #ca8a04;
  --zui-image-compare-zinc-accent: #52525b;
}

/* Dark theme variables follow the same names with -dark appended. */
.dark {
  --zui-image-compare-bg-dark: #1e293b;
  --zui-image-compare-border-dark: #1e293b;
  /* ...same variables with -dark at the end */
}

What it does

Image Compare stacks two pieces of media and clips the before layer at a percentage-based divider. Pointer dragging and click-to-position update the reveal continuously without adding an animation dependency.

Composition and API

Pass React nodes to before and after. Use defaultPosition for uncontrolled state or position and onPositionChange for controlled state. Configure step, labels, appearance, size, radius, and disabled behavior through typed props.

Common use cases

  • Show photo retouching and color-grading results.
  • Compare renovation, restoration, or landscaping progress.
  • Reveal map layers, scans, renders, or product configurations.
  • Review visual regressions and design implementation changes.

Accessibility

The divider exposes slider semantics with its current value, range, label, and disabled state. Arrow, Page, Home, and End keys provide precise operation without a pointer, while visible focus styling identifies the active control.

Next.js integration notes

The static component is a client entry because it manages pointer and keyboard interaction. It accepts Next.js Image components as before and after nodes, while the route can remain a Server Component and export static metadata.

FAQ

Can Image Compare be controlled from application state?

Yes. Pass position with onPositionChange for controlled usage, or defaultPosition for internal state. Values are clamped from 0 to 100.

Does the divider support keyboard controls?

Yes. Focus the divider and use Arrow keys for one step, Page Up or Page Down for ten steps, and Home or End for the limits.

Must the before and after content be img elements?

No. Both props accept React nodes, so they can contain responsive images, Next.js Image components, illustrations, maps, or any aligned visual content.

How do I customize its colors and dimensions?

Choose appearance, size, and radius variants, add a className for layout, or override the documented --zui-image-compare-* CSS variables.