Skip to content

Confetti

A short burst of paper pieces, launched from a fire() handle or when isActive turns true. The canvas appears for the burst, then removes itself.

Motionv0.1.0experimentalWCAG 2.2 AAView spec

Optional, and kept apart.

Nothing here is included in a project unless you add it, and the plain component it builds on is unchanged. This effect is decoration: it is hidden from screen readers, stands still for visitors who prefer reduced motion, and can be paused. Use Still on a preview to see that version.

import { useRef } from "react";
import { Button, Confetti, type ConfettiHandle } from "@rdloom/react";

export default function ConfettiWithButtonExample() {
  const confetti = useRef<ConfettiHandle>(null);
  return (
    <div className="flex w-full justify-center">
      <Confetti ref={confetti} />
      <Button onPress={() => confetti.current?.fire({ origin: { x: 0.5, y: 0.7 } })}>Celebrate</Button>
    </div>
  );
}

Installation

npx rdloom add confetti

Copies the source into src/components/rdloom. Edit it freely: rdloom upgrade merges later versions into your changes.

It also adds rdloom-motion.css next to your tokens and imports it from the tokens file, once: no setup, and no inline styles. The plain components you already use are untouched.

Use another registry client
npx shadcn@latest add https://rdloom.vimalbhatt.com/r/confetti.json

Works, but without upgrade tracking, and you import the motion CSS yourself.

Usage

import { Button, Confetti, type ConfettiHandle } from "@rdloom/react";

<div className="flex w-full justify-center">
  <Confetti ref={confetti} />
  <Button onPress={() => confetti.current?.fire({ origin: { x: 0.5, y: 0.7 } })}>Celebrate</Button>
</div>

On active

Press the button when you are done

import { useState } from "react";
import { Button, Confetti } from "@rdloom/react";

export default function ConfettiOnActiveExample() {
  const [done, setDone] = useState(false);
  return (
    <div className="flex w-full flex-col items-center gap-3">
      <Confetti isActive={done} particleCount={60} onComplete={() => setDone(false)} />
      <Button onPress={() => setDone(true)}>Finish the task</Button>
      <p role="status" className="text-sm text-[var(--rd-color-text-muted)]">
        {done ? "Task complete" : "Press the button when you are done"}
      </p>
    </div>
  );
}

API Reference

Defined by the spec. Components also accept the props of the React Aria component they wrap.

PropTypeDefault
isActive

Fires a burst each time it changes to true.

booleanfalse
particleCount

Pieces per burst.

number80
spread

Spread of the burst in degrees.

number70
origin

Where the burst starts, as fractions of the viewport.

{ x: number; y: number }none
colors

Colours of the pieces, any CSS colours. Defaults to the theme colours.

string[]none
onComplete

Called when the last piece has gone.

() => voidnone
isPaused

Stops the effect: fire() then does nothing.

booleanfalse

Accessibility

Role presentation, WCAG 2.2 AA. Tested with axe and keyboard tests; screen reader checks are in the audit checklist.

Keyboard

Not interactive.

Screen readers announce

  • Silent: the canvas is hidden from assistive technology, so announce the success separately

What your code must do

  • Decoration only: the canvas is aria-hidden and pointer-events: none, so it never blocks a click or takes focus
  • Nothing at all is drawn for reduced motion, inside rdm-still or with isPaused
  • The burst lasts about three seconds and then the canvas is removed from the page
  • A celebration must also be stated in text: pair it with a status message (WCAG 4.1.3)
  • No flashing: pieces fall and fade, nothing blinks

Guidelines

Use it when

  • After a completed task, a purchase or a milestone

Avoid it when

  • Errors or routine saves
  • Anywhere the burst could hide controls the person still needs

Don't

  • Using confetti as the only sign of success
  • Firing it repeatedly

Design tokens

The semantic tokens this component uses. Change them once and every component follows; see Design tokens.

  • --rd-color-action-primary
  • --rd-color-feedback-success
  • --rd-color-feedback-warning
  • --rd-color-feedback-danger