Side By Side Slide
An image comparison slider where a spring-animated divider follows the cursor on hover, revealing before/after images.
Installation
File Structure
Usage
import { SideBySideSlide } from "@/components/unlumen-ui/side-by-side-slide";
<SideBySideSlide
beforeImage="/images/before.jpg"
afterImage="/images/after.jpg"
className="aspect-video rounded-xl"
/>;Vertical orientation
<SideBySideSlide
beforeImage="/images/before.jpg"
afterImage="/images/after.jpg"
orientation="vertical"
className="aspect-video rounded-xl"
/>Custom divider
<SideBySideSlide
beforeImage="/images/before.jpg"
afterImage="/images/after.jpg"
dividerColor="#3b82f6"
dividerWidth={4}
dividerShadow="0 0 16px rgba(59,130,246,0.6)"
handleColor="#3b82f6"
className="aspect-video rounded-xl"
/>No handle
<SideBySideSlide
beforeImage="/images/before.jpg"
afterImage="/images/after.jpg"
showHandle={false}
className="aspect-video rounded-xl"
/>Custom spring
<SideBySideSlide
beforeImage="/images/before.jpg"
afterImage="/images/after.jpg"
springOptions={{ stiffness: 500, damping: 40 }}
className="aspect-video rounded-xl"
/>API Reference
SideBySideSlide
beforeImagestring—Source URL for the "before" image (left or top half).
afterImagestring—Source URL for the "after" image (right or bottom half).
beforeAlt?string"Before"Alt text for the before image.
afterAlt?string"After"Alt text for the after image.
orientation?"horizontal" | "vertical""horizontal"Direction of the divider. Horizontal splits left/right; vertical splits top/bottom.
initialPosition?number50Starting position of the divider as a percentage (0–100). The divider springs back to this value on mouse leave.
dividerColor?string"white"CSS color value for the divider line.
dividerWidth?number2Thickness of the divider line in pixels.
dividerShadow?string"0 0 8px rgba(0,0,0,0.3)"CSS box-shadow applied to the divider line.
showHandle?booleantrueWhether to render the circular drag handle on the divider.
handleSize?number40Diameter of the circular handle in pixels.
handleColor?string"white"Background color of the circular handle.
cursor?"none" | "col-resize" | "row-resize" | "pointer""none"CSS cursor style applied when hovering over the component. Use `none` to hide the cursor entirely.
springOptions?SpringOptions{ stiffness: 300, damping: 30 }Motion spring config forwarded to `useSpring`. Controls how the divider accelerates and settles.
className?string—Extra CSS classes applied to the root container.
Notes
- The divider position is driven by
useMotionValue+useSpring, bypassing React's render cycle for smooth 60 fps tracking. - Both images are sized
object-cover— they always fill the container regardless of intrinsic dimensions. Wrap the component in anaspect-*class to control the ratio. - On mouse leave the divider springs back to
initialPosition, giving users a clear preview of the default split. clip-path: inset()is used to reveal the before image rather than resizing it, so both images remain full-size at all times.- Set
cursor="none"(the default) for the cleanest look; switch to"col-resize"or"row-resize"to hint that the area is interactive.
Credits
Built by Léo. Inspired by the Side By Side Slide Framer component by Alexander Ghavas.
Keep in mind
Most components on this site are inspired by or recreated from existing work across the web. I'm not here to take credit; just to learn, experiment, and sometimes push things a bit further. If something looks familiar and I forgot to mention you, reach out and I'll fix that right away.