shadcn.io is an independent project built on shadcn/ui
Components
Free
Comparison for React
Two layers in one frame and a divider to drag between them: before and after, raw and edited. For React with Base UI, by pointer, touch or key.
Two pictures share one frame, and a divider decides how much of each shows.
Use it when the difference between two versions is the point: an edit, a redesign, a restored photo. Two pictures to look at one after the other are a Carousel. In React, the divider is a slider with its own keys, and in right-to-left text the start layer sits on the right.
Installation
Sign in to get your install command
Free with an account. It carries your token, so the CLI installs everything in one go.
<ComparisonclassName="aspect-video max-w-lg rounded-xl">
<ComparisonItemposition="start">
<imgalt="Before: the valley in black and white"className="size-full object-cover grayscale"src="/valley.jpg"
/>
</ComparisonItem>
<ComparisonItemposition="end">
<imgalt="After: the valley in colour"className="size-full object-cover"src="/valley.jpg"
/>
</ComparisonItem>
<ComparisonHandle />
</Comparison>
Accessibility
ComparisonHandle is a slider (role="slider", 0 to 100, its value read as a percentage), labelled "Divider position" unless you pass aria-label. The arrows move the divider the way they point, so in right-to-left text the right arrow lowers the value. Each layer keeps its own alt; both stay in the reading order whatever the divider hides.
KeyAction
Arrow Right
Move the divider right by step.
Arrow Left
Move the divider left by step.
Arrow Up
Move the divider toward the end.
Arrow Down
Move the divider toward the start.
Shift+Arrow Right
Move it ten steps at once (any arrow).
Page Up
Move it ten steps toward the end.
Page Down
Move it ten steps toward the start.
Home
Show only the end layer.
End
Show only the start layer.
Demos
Hover
With mode set to hover, the divider follows a mouse with no press, and the grip only shows while the handle has keyboard focus. It's quicker to scan, less precise to stop. A finger still drags, since a touch screen has no hover.
Custom Handle
Children replace the line and the grip: here a thin rule and a round knob. The handle still places itself, takes the keys and stays a slider, so draw your own focus style with group-focus-visible/comparison-handle:, as the knob does.
Controlled
value and onValueChange hold the position, from 0 at the start edge to 100 at the end. The buttons below set it to a stop, and the readout follows a drag. onValueChangeEnd fires once a drag or a key press ends, for work that shouldn't run on every move.
Content
The layers can be anything, not only images: a dashed wireframe on the start side, the finished card on the end side, laid out alike so the divider lines them up. The frame is 16:9 unless you say otherwise (aspect-video); this one is 4:3. The layers don't take the pointer, so links and buttons inside them aren't clickable.
FAQ
What's the difference between drag and hover mode?
drag, the default, moves the divider while a pointer is pressed, and a click jumps it to that spot. hover follows a mouse with no press, which is faster to scan and harder to hold still. Touch drags in both modes. The Hover demo shows the second.
Does Comparison work on phones?
Yes. A finger drags the divider across the frame, and scrolling up and down still works because the frame only takes sideways moves (touch-action: pan-y). The handle is also a slider for keyboards and screen readers, so it works without a pointer at all.
Which side is ComparisonItem position start in React?
start shows on the divider's start side and end on its end side: left and right in left-to-right text, right and left in right-to-left. The value counts from the start edge, so 25 shows a quarter of the start layer.
How do I change the divider's look?
Pass children to ComparisonHandle and they replace the line and the grip. The handle keeps its place, its keys and its slider role. Give your knob a focus style through group-focus-visible/comparison-handle:, as the Custom Handle demo does, so keyboard users can see it.
How do I read or set the divider's position?
Pass value and onValueChange to Comparison, as the Controlled demo does, or only defaultValue to start somewhere other than 50. onValueChangeEnd gets the position when a drag or a key press ends. The position is also the --comparison-position CSS variable on the frame.
Does Comparison only work with images?
No. Any content can go in a ComparisonItem: a video, a chart, a UI before and after, as in the Content demo. The layers are stacked and clipped, so lay both out the same way. They don't take the pointer, so controls inside them can't be clicked.
How do I install Comparison in React?
Run the command in the Installation section on this page. It adds Comparison, ComparisonItem and ComparisonHandle to your ui folder, with lucide for the grip icon.
The command uses the shadcn CLI.
API Reference
Comparison
PropType
value?number
The divider's position, in percent from the start edge (controlled).
defaultValue?number
Where the divider starts when uncontrolled, in percent from the start
edge.
Default
50
onValueChange?(value: number) → void
Called with the new position, in percent, as the divider moves.
Parameters
value – number
Returns
void
mode?ComparisonMode
drag moves the divider while a pointer is pressed (a click jumps it
there); hover follows a mouse without a press. Touch drags in both.
Default
"drag"
onValueChangeEnd?(value: number) → void
Called with the position once a drag or a key press ends.
Parameters
value – number
Returns
void
step?number
How far an arrow key moves the divider, in percent; Shift, Page Up and
Page Down move ten times as far.
Default
1
className?string
Merged with the part's classes.
CSS variableType
--comparison-positionstring
The divider's place from the start edge (50%).
ComparisonItem
<div>data-slot="comparison-item"
PropType
position"start" | "end"
The divider's side this layer shows on: start (left in left-to-right
text) or end.