Confetti
ComponentsCelebration engineCanvas confetti & celebration engine for React — hand-written physics, zero dependencies, three consumption layers
Live demo
Install
$ npm install @usefy/confettiQuick start
import { fireConfetti } from "@usefy/confetti";
function ShipItButton() {
return (
<button onClick={() => fireConfetti({ origin: { y: 0.8 }, spread: 70 })}>
🚀 Ship it
</button>
);
}API reference
<Confetti /> — overlay with an imperative controller
import { useRef } from "react";
import { Confetti, type ConfettiController } from "@usefy/confetti";
function CheckoutSuccess() {
const confetti = useRef<ConfettiController>(null);
return (
<>
<Confetti
controllerRef={confetti}
onComplete={() => console.log("settled")}
/>
<button onClick={() => confetti.current?.fire({ origin: { y: 0.7 } })}>
Complete purchase
</button>
</>
);
}
| Prop | Default | Description |
|---|---|---|
variant | "overlay" | "overlay" portals a fixed full-viewport canvas after hydration; "inline" fills the parent element (give it position: relative) |
zIndex | 1100 | Overlay stacking order (above @usefy/spotlight-tour's 1000) |
controllerRef | — | Receives { fire, emit, stop, clear } (stable identity) |
onComplete | — | Called on each idle edge (all particles dead, no emitter) |
fireOnMount | false | true or a FireOptions — one burst after mount (respects reduced motion) |
reducedMotion | "respect" | "respect" no-ops under prefers-reduced-motion; "ignore" animates anyway |
className / style | — | Merged onto the canvas element |
The canvas is aria-hidden with pointer-events: none — it never intercepts input, and the engine pauses automatically while the tab is hidden.
useConfetti() — a canvas you own
import { useConfetti } from "@usefy/confetti";
function CardCelebration() {
const { canvasRef, fire, isActive } = useConfetti();
return (
<div style={{ position: "relative", overflow: "hidden" }}>
<canvas
ref={canvasRef}
style={{ position: "absolute", inset: 0, width: "100%", height: "100%", pointerEvents: "none" }}
/>
<button disabled={isActive} onClick={() => fire({ origin: { y: 0.9 } })}>
{isActive ? "Celebrating…" : "🎉 Celebrate"}
</button>
</div>
);
}
Returns { canvasRef, fire, emit, stop, clear, isActive } — all functions stable; isActive re-renders only on idle↔active edges, never per frame.
Go deeper
This page is the quick reference. For every example, prop, and edge case, read the full README — or open Storybook to change props live.