Image Comparison
Before and after layers split by a draggable, keyboard operable slider handle.
pnpm dlx @glinui/cli@latest add image-comparisonInstallation
pnpm dlx @glinui/cli@latest add image-comparisonCredits. Adapted from Motion Primitives (MIT, (c) 2024 ibelick). motion/react and springs removed (pointer capture and CSS clip-path), slider role with keys, ReactNode slots, tokens, reduced motion, RTL Attribution details.
Prefer the package?
Usage
The layers are any ReactNode, so you can compare images, screenshots, UI states or whole themes.
Variants
Vertical split, and hover mode that follows a fine pointer.
<ImageComparison orientation="vertical" before={a} after={b} /><ImageComparison hover before={a} after={b} />States
Compare themes or any other pair of states.
<ImageComparison beforeLabel="Light" afterLabel="Dark" before={light} after={dark} />In a layout
A redesign section with a heading above the comparison.
See the redesign
<section aria-labelledby="redesign"> <h3 id="redesign">See the redesign</h3> <ImageComparison before={a} after={b} /></section>Accessibility
- The handle is role slider with aria-valuenow, aria-valuetext, aria-orientation and an accessible name.
- Everything a pointer can do works from the keyboard: arrows, Page keys, Home and End.
- touch-action is pan-y (pan-x when vertical) so a horizontal drag moves the handle while the page still scrolls the other way.
- Both layers are named groups, so their content stays readable.
Keyboard Interactions
| Key | Action |
|---|---|
| ArrowLeft / ArrowRight | Move the handle (reversed in RTL). |
| ArrowUp / ArrowDown | Move the handle in vertical mode. |
| Shift + Arrow, PageUp / PageDown | Move five steps. |
| Home / End | Jump to 0 or 100. |
ARIA Attributes
- `role="slider"` with `aria-valuemin`, `aria-valuemax`, `aria-valuenow`
- `aria-valuetext` reads both percentages
- `aria-orientation`
Reduced Motion
The short clip-path glide on keyboard moves only runs at motion level full. Dragging is always immediate.
Affected properties
clip-path transitionhandle position transitionAPI Reference
| Prop | Type | Required | Default | Description |
|---|---|---|---|---|
before / after | ReactNode | - | - | The two layers. Before is shown from the start edge up to the handle. |
beforeLabel / afterLabel | string | - | Before / After | Names for the layers and the corner chips. |
showLabels | boolean | No | true | Show the corner chips. |
value / defaultValue / onValueChange | number / number / (value) => void | - | 50 | Handle position, percent of the before layer. |
orientation | "horizontal" | "vertical" | No | horizontal | Direction of the split. |
step | number | No | 2 | Arrow key step in percent. Shift multiplies by 5. |
hover | boolean | No | false | Follow a fine pointer without pressing. Touch still drags. |
label | string | No | Comparison position | Accessible name of the slider. |
variant | SurfaceVariant | "default" | No | ambient (glinr) | Frame surface. |
after | React.ReactNode | Yes | - | Layer shown behind, revealed past the handle. Any ReactNode. |
afterLabel | string | No | "After" | Auto-generated from TypeScript source. |
before | React.ReactNode | Yes | - | Layer shown from the start edge up to the handle. Any ReactNode. |
beforeLabel | string | No | "Before" | Auto-generated from TypeScript source. |
defaultValue | number | No | 50 | Auto-generated from TypeScript source. |
onValueChange | (value: number) => void | No | - | Auto-generated from TypeScript source. |
value | number | No | - | Handle position, 0 to 100 (percent of the before layer shown). |
Source
Import directly from the package or browse the source on GitHub. Click any file to view it.
Import
import { ImageComparison } from "@glinui/ui"Dependencies
@glinui/ui@glinui/motion@phosphor-icons/reactGenerated API Snapshot
Beta
Generated API Snapshot
BetaAuto-extracted from TypeScript source in packages/ui/src/components/image-comparison.tsx. This section is in beta and may lag behind hand-curated docs. Regenerate with pnpm --filter @glinui/docs api:generate.
Generated: 2026-10-08T01:41:21.546Z · Full index: /docs/api-metadata
Primary Props Type
ImageComparisonProps
ImageComparisonProps
| Prop | Type | Required | Default | Description |
|---|---|---|---|---|
after | React.ReactNode | Yes | - | Layer shown behind, revealed past the handle. Any ReactNode. |
afterLabel | string | No | "After" | - |
before | React.ReactNode | Yes | - | Layer shown from the start edge up to the handle. Any ReactNode. |
beforeLabel | string | No | "Before" | - |
defaultValue | number | No | 50 | - |
hover | boolean | No | false | Follow a fine pointer without pressing. Touch still drags. |
label | string | No | "Comparison position" | Accessible name of the slider handle. |
onValueChange | (value: number) => void | No | - | - |
orientation | "horizontal" | "vertical" | No | "horizontal" | - |
showLabels | boolean | No | true | Show the visible Before and After chips. |
step | number | No | 2 | Arrow key step in percent. Shift or Page keys move 5 steps. |
value | number | No | - | Handle position, 0 to 100 (percent of the before layer shown). |
variant | SurfaceVariant | "default" | No | - | - |