Confetti

A React confetti component that fires a canvas burst on click - for success screens, checkouts and sign-ups.

Confetti playground

The canvas is pinned to the window - the whole page is the stage.

Install Confetti

1. New project? One command installs the package and wires everything - see Installation:

bash
pnpm dlx zyncat-ui init

2. Import the component - it loads its own stylesheet:

tsx
import { Confetti } from '@zyncat/ui/confetti';

Usage

tsx
import { Confetti, type ConfettiHandle } from '@zyncat/ui/confetti';
 
const confetti = useRef<ConfettiHandle>(null);
 
<Confetti ref={confetti} field="viewport" />
<Button onClick={() => confetti.current?.fire()}>Celebrate</Button>

Compose Confetti in your application. No Tailwind or external styling library is required; every value resolves from Zyncat UI's token vocabulary.

Confetti props

Confetti

PropTypeDefaultDescription
...htmlAttributesHTMLAttributes—Standard <canvas> attributes (aria-*, data-*, title, ...) forwarded to the canvas.
emitterConfettiEmitter'sides'Where pieces are launched from: two side cannons, a full-width fall, or two floor-corner cannons.
countnumber170Pieces one fire sends, clamped to 1-520. The field itself holds 520; beyond that the oldest piece is retired.
durationnumber0.15Seconds the emitter stays open, clamped to 0-10. Pieces leave front-loaded inside that window, so 0.15 reads as one shove and 2.5 as a burst that tapers into a fall.
speednumber1Multiplies the simulation rate; sampled live on every frame.
fieldConfettiField'container'`container` fills the nearest positioned ancestor; `viewport` portals the canvas to the body and pins it to the window above the toast layer, so a transformed ancestor cannot trap it. Switching field remounts the canvas and drops pieces in flight.
refRef<ConfettiHandle>—Imperative handle - call `fire()` on it to send a burst, `clear()` to empty the field.
classNamestring—Extra class(es) merged onto the canvas.
styleConfettiStyle—Inline styles merged onto the canvas.

ConfettiFireOptions

PropTypeDefaultDescription
countnumber—Pieces this burst sends, clamped to 1-520. Falls back to the `count` prop.
durationnumber—Seconds this burst's emitter stays open, clamped to 0-10. Falls back to the `duration` prop.
emitterConfettiEmitter—Emitter geometry for this burst. Falls back to the `emitter` prop.

ConfettiHandle

PropTypeDefaultDescription
fire*(options?: ConfettiFireOptions) => void—Sends one burst. Bursts coexist - firing again while pieces are still airborne adds to the field rather than replacing it.
clear*() => void—Empties the field and stops the simulation immediately.

ConfettiStyle

PropTypeDefaultDescription
--confetti-paper-1string | number—`--confetti-paper-1` - The five papers a burst draws from. Default: `oklch(0.53 0.2 288)`.
--confetti-paper-2string | number—`--confetti-paper-2` - The five papers a burst draws from. Default: `var(--accent)`.
--confetti-paper-3string | number—`--confetti-paper-3` - The five papers a burst draws from. Default: `oklch(0.78 0.115 62)`.
--confetti-paper-4string | number—`--confetti-paper-4` - The five papers a burst draws from. Default: `oklch(0.67 0.18 12)`.
--confetti-paper-5string | number—`--confetti-paper-5` - The five papers a burst draws from. Default: `var(--text-strong)`.
--confetti-weightsstring | number—`--confetti-weights` - How often each paper appears, in slot order. Default: `1 1 1 1 0.45`.
--confetti-inkstring | number—`--confetti-ink` - What the reverse side of a piece darkens toward. Default: `var(--text-strong)`.
--confetti-lightstring | number—`--confetti-light` - What the glossy face of a piece brightens toward. Default: `var(--bg-surface)`.
--confetti-shadestring | number—`--confetti-shade` - How far the reverse side leans toward the ink. Default: `42%`.
--confetti-glossstring | number—`--confetti-gloss` - How far the glossy face leans toward the light. Default: `66%`.
--confetti-layerstring | number—`--confetti-layer` - The z-index of a viewport-field burst. Default: `var(--layer-toast)`.

Frequently asked questions

A confetti animation is a celebration effect that throws coloured paper across the screen and lets it tumble and fall. This one is a canvas particle simulation: every piece gets its own drag, lift, gravity, spin and flip across three depth layers, so it settles like real paper rather than looping a fixed animation.

Render <Confetti ref={confetti} /> once, then call confetti.current?.fire() from the button's onClick. fire() also takes a per-burst override - fire({ count: 300, duration: 2.5, emitter: 'top' }) - and bursts coexist rather than cancelling each other, so rapid clicks stack. Under prefers-reduced-motion the canvas renders nothing and fire() is a no-op.

It is a live canvas animation, not a confetti GIF, Lottie file or transparent overlay video. Nothing is downloaded and nothing is pre-rendered: the pieces are simulated per frame in a <canvas> element, so the burst is a different shape every time and stays sharp at any screen size or pixel density.

No - this is a React component for the web, so it runs on a page, not inside a slide deck. PowerPoint and Google Slides need an animated GIF or a video file; if you want confetti on a website, a landing page or a success screen instead, this component is the one to use.

Yes. It is a React 19 component with zero runtime dependencies and no Tailwind, so it drops into Next.js as-is. With field="viewport" the canvas is portalled to document.body after hydration and renders nothing on the server; with the default field="container" it fills the nearest positioned ancestor.

count sets the pieces one burst sends (1-520, default 170) and duration is how long the emitter stays open in seconds - 0.15 reads as one shove, 2.5 tapers into a fall. Colours are CSS custom properties on the canvas: set --confetti-paper-1 through --confetti-paper-5, plus --confetti-weights, --confetti-gloss and --confetti-shade to retune the palette.

Set in Geist & Newsreader — animated by the house engineZyncat UI · Rev 0.11 · MIT · Built by Tabsir Ahammed · Source on GitHub