Compare
moco/compareFreeComponent
Before/after clip-path wipe with pointer, keyboard, and slider-role support.
Live preview
live · rendered by the registry
Rendered by moco on their own page — this is the component running, not a screenshot of it.Install it
npx shadcn@latest add https://mocoui.site/r/compare.jsonSource
1"use client";23import * as React from "react";4import "./compare.css";56export interface CompareProps {7 before: React.ReactNode;8 after: React.ReactNode;9 height?: number;10 beforeLabel?: string;11 afterLabel?: string;12}1314/**15 * Before/after wipe. Works for images and for live DOM. Drag the handle,16 * click anywhere to position it, or focus it and use the arrow keys.17 */18export function Compare({19 before,20 after,21 height = 300,22 beforeLabel = "before",23 afterLabel = "after",24}: CompareProps) {25 const [pct, setPct] = React.useState(50);26 const box = React.useRef<HTMLDivElement>(null);27 const dragging = React.useRef(false);2829 const setFromClientX = (x: number) => {30 const el = box.current;31 if (!el) return;32 const r = el.getBoundingClientRect();33 setPct(Math.min(100, Math.max(0, ((x - r.left) / r.width) * 100)));34 };3536 return (37 <div38 ref={box}39 className="moco-cmp"40 style={{ height }}41 onPointerDown={(e) => {42 dragging.current = true;43 (e.currentTarget as HTMLElement).setPointerCapture(e.pointerId);44 setFromClientX(e.clientX);45 }}46 onPointerMove={(e) => dragging.current && setFromClientX(e.clientX)}47 onPointerUp={() => (dragging.current = false)}48 onPointerCancel={() => (dragging.current = false)}49 >50 <span className="moco-cmp-label" style={{ left: 12 }}>51 {beforeLabel}52 </span>53 <span className="moco-cmp-label" style={{ right: 12 }}>54 {afterLabel}55 </span>5657 <div style={{ position: "absolute", inset: 0 }}>{before}</div>58 <div className="moco-cmp-after" style={{ clipPath: `inset(0 0 0 ${pct}%)` }}>59 {after}60 </div>6162 <div className="moco-cmp-handle" style={{ left: `${pct}%` }}>63 <button64 type="button"65 className="moco-cmp-grab"66 role="slider"67 aria-label={`Reveal ${afterLabel} over ${beforeLabel}`}68 aria-valuemin={0}69 aria-valuemax={100}70 aria-valuenow={Math.round(pct)}71 aria-valuetext={`${Math.round(pct)} percent`}72 onKeyDown={(e) => {73 if (e.key === "ArrowLeft") setPct((v) => Math.max(0, v - 4));74 if (e.key === "ArrowRight") setPct((v) => Math.min(100, v + 4));75 if (e.key === "Home") setPct(0);76 if (e.key === "End") setPct(100);77 }}78 >79 ⇄80 </button>81 </div>82 </div>83 );84}
What it pulls in
Other registry items
Files it writes
More from moco
All 20 items| Component | Registry | Kind | Access | Installs | Command |
|---|---|---|---|---|---|
| Codecode | moco | Components | Free | 1 dep · 3 files | |
| Count Upcountup | moco | Components | Free | no deps · 2 files | |
| Covercover | moco | Components | Free | no deps · 2 files | |
| Diagram Kitdiagram | moco | Components | Free | no deps · 2 files | |
| Dot Griddotgrid | moco | Components | Free | no deps · 2 files | |
| Figurefigure | moco | Components | Free | no deps · 2 files | |
| Hookshooks | moco | Components | Free | no deps | |
| Islandisland | moco | Components | Free | no deps · 2 files |
