Getting started
Foundations
Integrations
Form19
Display5
Layout4
Navigation4
Overlay8
Feedback4
Motion19
#Installation
npx @dinachi/cli@latest add compare-slider#Usage
tsx
import { CompareSlider } from "@/components/ui/compare-slider"tsx
<CompareSlider
before={<img src="/before.jpg" alt="Before retouching" />}
after={<img src="/after.jpg" alt="After retouching" />}
/>#Examples
#Drag modes
| Mode | Behaviour | Use it when |
|---|---|---|
handle | Only the divider starts a drag. Both layers stay live: buttons take clicks, text takes selection, links take focus. | Either side is real UI, or has anything in it worth clicking. |
panel | Pressing anywhere jumps the divider there. | Both sides are images. There is nothing to click, and a thin handle is a needlessly small target. |
panel makes both layers inert, which reads as a bug on anything interactive. That is why
it is not the default.
#When to use it
The divider works because both layers are registered: the same frame, in the same place, at the same size.
| Use it for | Not for |
|---|---|
| A before/after photo, a design diff, a map with a layer toggled. | Two things that merely sit side by side. With nothing aligned there is nothing to reveal, and a pair of panels says it better. |
#Behaviour
- A pointer drag tracks 1:1, with no easing. That is direct manipulation, and smoothing there reads as lag.
- A keyboard step is carried over 100ms. One press moves the divider a fixed slice of the panel, which in a single frame strobes rather than moves. A held arrow key composes into one continuous travel rather than a queue of restarts.
- Grabbing the handle keeps your offset, so the divider does not teleport under your
finger. Pressing the panel in
panelmode deliberately does the opposite. - A second pointer mid-drag is ignored until the first lifts, rather than jumping the divider away from the finger already holding it.
#Accessibility
- The handle is a
sliderwitharia-valuemin,aria-valuemaxand a livearia-valuenow, so the position is readable rather than only visible. - Arrow keys step, Shift steps five times as far, Home and End jump to either end. The announced value updates on the key press, not when the animation lands.
- Give it a
labelwhen the panel needs saying which two things are being compared. The default, "Compare position", describes the control and not the content. - Reduced motion places each keyboard step immediately instead of carrying it.
#API Reference
| Prop | Type | Default | Description |
|---|---|---|---|
| before | ReactNode | — | Content shown on the left of the divider |
| after | ReactNode | — | Content shown on the right of the divider |
| defaultPosition | number | 50 | Starting divider position, 0–100 |
| step | number | 2 | Percentage points moved per arrow key press |
| drag | "handle" | "panel" | "handle" | What starts a drag. handle keeps both layers interactive |
| label | string | "Compare position" | Accessible name for the divider handle |
| stickyHandle | boolean | false | Keep the knob at the vertical middle of the viewport. Set it when the panel is taller than the screen |
| onPositionChange | (position: number) => void | — | Fired on release and on each keyboard step |