Skip to content
WANCRAFT

A.186Buttons & Controls

Add to Cart Button

Throws the product along a parabola into the cart, bumps it, rolls the badge and confirms with a check.

NewReactMotionSVGTailwindintermediate

Overview

Add to Cart Button turns a purchase into a small physical gesture. On click it measures the cart element you point it at, clones the product image (or launches an accent dot) and throws it along a sampled parabolic path into the cart, shrinking as it travels. On landing the cart bumps and the companion CartIndicator rolls its badge to the new count, while the button itself swaps to a drawn check and "Added" for a moment, without changing width. The flight runs on the Web Animations API on a detached element, so React never re-renders per frame, and it lands inside a modal dialog when the button lives in one. Without a target, or with reduced motion, there is no flight: the badge updates and the check appears straight away. onAdd fires on click for your cart logic, onArrive when the item lands, and a polite live region announces "Added to cart, 3 items".

01Installation

Method 01 · Registry

CLI verified

Uses the shadcn CLI in a project initialised with shadcn init. Installs the file to components/ui/add-to-cart-button.tsx and adds its dependencies.

npx shadcn@latest add https://wancraft.my/r/add-to-cart-button.json

Method 02 · Copy and paste

  1. 1. Install the dependencies below.
  2. 2. Copy the source from the Code tab into components/ui/add-to-cart-button.tsx.
  3. 3. Import it and adjust props.
npm install motion
Dependencies
motion
Peer
react ≥ 18, tailwindcss ≥ 4
Added
13 Oct 2026
Updated
—

02Usage

The default configuration. Change properties above to generate a tailored example in the Usage tab.

example.tsx
import { AddToCartButton, CartIndicator } from "@/components/ui/add-to-cart-button";
import * as React from "react";

export function AddToCartButtonExample() {
  const cartRef = React.useRef<HTMLButtonElement>(null);
  const [count, setCount] = React.useState(0);

  return (
    <div className="flex items-center gap-6">
      <CartIndicator ref={cartRef} count={count} />
      <AddToCartButton
        target={cartRef}
        cartCount={count}
        onArrive={() => setCount((c) => c + 1)}
      />
    </div>
  );
}

03Props

PropTypeDefaultDescription
targetRefObject<HTMLElement | null> | string—Cart element (ref or selector), measured at click time. Optional: without it there is no flight.
sourceRefObject<HTMLElement | null> | string—Element cloned as the flying thumbnail, e.g. the product image. Default: an accent dot from the button.
onAdd() => void—Fires immediately on click.
onArrive() => void—Fires when the item lands (immediately without a target or with reduced motion). Update the count here.
cartCountnumber—Item count for the announcement "Added to cart, 3 items".
labelstring"Add to cart"Button text.
addedLabelstring"Added"Text of the confirmation state.
durationnumber800Flight time in ms.
arcnumber120Parabola height above the straight line, in px.
resetAfternumber1600How long the Added state lasts, in ms.
size"sm" | "md" | "lg""md"Button height: 36, 44 or 52 px.
accentstring"#FF7448"Dot, check badge and focus ring colour.
...propsButtonHTMLAttributes—Forwarded to the <button>; ref is forwarded.
CartIndicator{ count: number; label?: string; accent?: string }—Companion cart button with a rolling badge; named "Cart, 3 items". Forwards its ref for use as target.

04Features

  • 01Parabolic flight sampled into Web Animations keyframes, shrinking into the cart
  • 02Clones any source element (e.g. the product image) or flies an accent dot
  • 03Target and source are measured at click time, so layout changes and scrolling are safe
  • 04Cart bump via the individual scale property, composing with existing transforms
  • 05Fixed-width label swap to a drawn check and "Added"
  • 06CartIndicator companion with a rolling, spring-in count badge
motion · springmotion · tweeninput · clickinput · pressinput · keyboardinput · focus

05Accessibility

  • A native <button>; Enter and Space add to cart and get exactly the same feedback as a click.
  • A polite live region announces "Added to cart, 3 items" when the item lands (or just "Added to cart" without cartCount).
  • The flying clone is aria-hidden and inert, with ids stripped so it never duplicates the source's.
  • CartIndicator is a labelled button ("Cart, 3 items"); the badge digits are aria-hidden.
  • Visible focus rings in the accent colour.
  • Reduced motion: no flight or bump; the badge updates and the check appears instantly.