Confetti
Confetti animations are best used to delight your users when something special happens
Confetti
Installation
Install the following dependencies:
npm install canvas-confetti
Copy and paste the following code into your project.
import React, {
createContext,
forwardRef,
useCallback,
useEffect,
useImperativeHandle,
useMemo,
useRef,
type ReactNode,
} from "react"
import confetti, {
type GlobalOptions as ConfettiGlobalOptions,
type CreateTypes as ConfettiInstance,
type Options as ConfettiOptions,
} from "canvas-confetti"
import { Button, ButtonProps } from "@/components/ui/button"
type Api = {
fire: (options?: ConfettiOptions) => void
}
type Props = React.ComponentPropsWithRef<"canvas"> & {
options?: ConfettiOptions
globalOptions?: ConfettiGlobalOptions
manualstart?: boolean
children?: ReactNode
}
export type ConfettiRef = Api | null
const ConfettiContext = createContext<Api>({} as Api)
export const Confetti = forwardRef<ConfettiRef, Props>((props, ref) => {
const {
options,
globalOptions = { resize: true, useWorker: true },
manualstart = false,
children,
...rest
} = props
const instanceRef = useRef<ConfettiInstance | null>(null) // confetti instance
const canvasRef = useCallback(
// https://react.dev/reference/react-dom/components/common#ref-callback
// https://reactjs.org/docs/refs-and-the-dom.html#callback-refs
(node: HTMLCanvasElement) => {
if (node !== null) {
// <canvas> is mounted => create the confetti instance
if (instanceRef.current) return // if not already created
instanceRef.current = confetti.create(node, {
...globalOptions,
resize: true,
})
} else {
// <canvas> is unmounted => reset and destroy instanceRef
if (instanceRef.current) {
instanceRef.current.reset()
instanceRef.current = null
}
}
},
[globalOptions]
)
// `fire` is a function that calls the instance() with `opts` merged with `options`
const fire = useCallback(
(opts = {}) => instanceRef.current?.({ ...options, ...opts }),
[options]
)
const api = useMemo(
() => ({
fire,
}),
[fire]
)
useImperativeHandle(ref, () => api, [api])
useEffect(() => {
if (!manualstart) {
fire()
}
}, [manualstart, fire])
return (
<ConfettiContext.Provider value={api}>
<canvas ref={canvasRef} {...rest} />
{children}
</ConfettiContext.Provider>
)
})
interface ConfettiButtonProps extends ButtonProps {
options?: ConfettiOptions &
ConfettiGlobalOptions & { canvas?: HTMLCanvasElement }
children?: React.ReactNode
}
export function ConfettiButton({
options,
children,
...props
}: ConfettiButtonProps) {
const handleClick = (event: React.MouseEvent<HTMLButtonElement>) => {
const rect = event.currentTarget.getBoundingClientRect()
const x = rect.left + rect.width / 2
const y = rect.top + rect.height / 2
confetti({
...options,
origin: {
x: x / window.innerWidth,
y: y / window.innerHeight,
},
})
}
return (
<Button onClick={handleClick} {...props}>
{children}
</Button>
)
}
Update the import paths to match your project setup.
Examples
Basic
Random Direction
Fireworks
Side Cannons
Stars
Custom Shapes
Emoji
Props
Confetti
Prop | Type | Description | Default |
---|---|---|---|
particleCount | Integer | The number of confetti particles to launch | 50 |
angle | Number | The angle in degrees at which to launch confetti | 90 |
spread | Number | The spread in degrees of the confetti | 45 |
startVelocity | Number | The initial velocity of the confetti | 45 |
decay | Number | The rate at which confetti slows down | 0.9 |
gravity | Number | The gravity applied to confetti particles | 1 |
drift | Number | The horizontal drift applied to particles | 0 |
flat | Boolean | Whether confetti particles are flat | false |
ticks | Number | The number of frames confetti lasts | 200 |
origin | Object | The origin point of the confetti | { x: 0.5, y: 0.5 } |
colors | Array of Strings | Array of color strings in HEX format | ['#26ccff', '#a25afd', '#ff5e7e', '#88ff5a', '#fcff42', '#ffa62d', '#ff36ff'] |
shapes | Array of Strings | Array of shapes for the confetti | ['square', 'circle'] |
zIndex | Integer | The z-index of the confetti | 100 |
disableForReducedMotion | Boolean | Disables confetti for users who prefer no motion | false |
useWorker | Boolean | Use Web Worker for better performance | true |
resize | Boolean | Whether to resize the canvas | true |
canvas | HTMLCanvasElement or null | Custom canvas element to draw confetti | null |
scalar | Number | Scaling factor for confetti size | 1 |
ConfettiButton
Prop | Type | Description | Default |
---|---|---|---|
options | Object | Options for the confetti | |
children | React.ReactNode | Children to render inside the button | null |