Confetti
Free · MITReact Native · ExpoA celebration burst: paper pieces launch from a point or rain from the top on closed-form physics (air drag, gravity, sway), spinning and turning over as they fall; one burst per trigger, a static sparkle under reduced motion.
Installation
$ npx shadcn@latest add https://goodui.dev/native/r/confetti.jsonThen run npx expo install --fix so react-native-reanimated, react-native-svg match your Expo SDK. Files land in components/goodui-native/confetti.
Usage
import { useState } from "react";
import { View } from "react-native";
import { Button } from "@/components/goodui-native/button/button";
import { Confetti } from "@/components/goodui-native/confetti/confetti";
export function GoalReached() {
const [celebrations, setCelebrations] = useState(0);
return (
<View>
<Button onPress={() => setCelebrations((c) => c + 1)}>Log 10,000 steps</Button>
<Confetti trigger={celebrations} announcement="Goal reached" />
</View>
);
}When to use
- A real milestone the person worked for: a goal reached, a first sale, a streak, a course finished.
- The end of a flow worth celebrating, together with a clear confirmation (success-check or the screen's own result).
- Routine confirmations such as saving a form: let the button show success.
- As the only confirmation of an action: pair it with a visible result.
- A loading or progress indicator: use progress-ring or the button's own pending state.
Examples
Burst out of a button through a ref
const buttonRef = useRef<View>(null);
const confetti = useRef<ConfettiHandle>(null);
<View ref={buttonRef} collapsable={false}>
<Button onPress={() => confetti.current?.fire()}>Claim reward</Button>
</View>
<Confetti ref={confetti} origin={buttonRef} count={36} />Inside a card instead of the whole screen
<View style={{ overflow: "hidden" }}>
{card}
<Confetti trigger={done} fullScreen={false} origin="center" power={0.6} />
</View>API reference
| Prop | Type | Default |
|---|---|---|
| trigger Fires one burst every time this changes to a new truthy value after mount (a counter, a timestamp, `true`). | number | string | boolean | null | — |
| ref Imperative handle: `ref.current?.fire()`. | Ref<ConfettiHandle> | — |
| origin `top` rains across the width; a point or an element ref bursts upward from there. Default `top`. | ConfettiOrigin | "top" |
| count Pieces per burst, 8 to 120. Default 44: enough to read as a celebration, light enough for low-end phones. | number | 44 |
| spread Half-angle of a point burst in degrees, around straight up. Default 55. | number | 55 |
| power Launch speed multiplier for point bursts. Default 1. | number | 1 |
| colors Piece colors. Defaults to the theme accent and status colors. | string[] | — |
| duration Milliseconds a burst lives. Default 2400. | number | 2400 |
| fullScreen Render above everything through the portal (default), or inside the nearest positioned parent. | boolean | true |
| haptic Cue as a burst fires. `false` turns it off. Default `celebrate`. | HapticName | false | "celebrate" |
| announcement Read by screen readers when a burst fires, for example "Goal reached". The pieces themselves are hidden. | string | — |
| onComplete Called when a burst has finished. | () => void | — |
Motion and haptics
- Point bursts launch in a cone around straight up (spread), denser in the middle; 'top' rains pieces across the width with staggered starts.
- Every piece follows a closed-form path with linear air drag and gravity plus a growing sideways sway, spins, and turns over (scaleY follows a cosine) like paper; it fades in the last 28 % of its life.
- Up to three bursts overlap; a fourth replaces the oldest.
- Reduced motion: no pieces travel. Seven four-point sparkles at the origin fade in, hold and fade out (about 1.1 s).
- Haptic: celebrate as each burst fires (haptic prop; false turns it off). Never repeated during the burst.
- One linear clock per burst on the UI thread; each piece is a plain Animated.View whose style is computed from that clock, no per-frame React state.
- Default 44 pieces; count is clamped to 8 to 120. Keep it under about 60 for low-end Android.
- Nothing is mounted between bursts.
Accessibility
- The pieces are hidden from screen readers and never take touches (pointerEvents none).
- Pass `announcement` to have a burst read out, for example 'Goal reached'.
Notes for AI
- trigger fires on every change to a new truthy value after mount (a counter works best); it does not fire on the first render.
- For a burst from an element pass a ref to a View with collapsable={false} as origin; it is measured when the burst fires.
- fullScreen (default) renders through the ThemeProvider portal above everything. With fullScreen={false} it fills the nearest parent; give that parent overflow hidden.
- Default colors are the accent and status colors from the theme; pass colors to use your own tokens.
Related
A success or error moment: the circle springs in, the check draws itself while a ring pulses out and the title rises in; the error variant draws a cross and shakes once. Inline or fullscreen, optionally with confetti.
A like toggle with one moment of delight: the heart squashes and springs back filled, a ring expands and particles burst while the count rolls; it updates optimistically and rolls back if the request fails.
A circular progress ring that fills on a spring, laps itself past 100 % with a gap that lifts the lap off the ring, and bounces once with a success haptic when it completes.