logo

VELOCIUI

Confetti

Confetti animations are best used to delight your users when something special happens


Component Preview

Confetti

Examples

Basic Confetti

Random Direction Confetti

Fireworks

Custom Shapes Confetti

Side Cannons Confetti

Stars Confetti


Usage

npm install canvas-confetti
confetti.tsx
import type { ReactNode } from "react";
import React, {
createContext,
forwardRef,
useCallback,
useEffect,
useImperativeHandle,
useMemo,
useRef,
} from "react";
import type {
GlobalOptions as ConfettiGlobalOptions,
CreateTypes as ConfettiInstance,
Options as ConfettiOptions,
} from "canvas-confetti";
import confetti 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);
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;
}
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>
);
}
Confetti.displayName = "Confetti";
export { Confetti, ConfettiButton };
export Confetti;

Props for Confetti

PropTypeDescriptionDefault
options
ConfettiOptions

Options for configuring individual confetti animations, such as colors, shapes, and particle count.

undefined
globalOptions
ConfettiGlobalOptions

Global options for confetti instance, including settings for resizing and using a worker.

{ resize: true, useWorker: true }
manualstart
boolean

If true, confetti animation will not start automatically; it requires manual triggering.

false
children
ReactNode

Optional children nodes to render inside the Confetti component.

undefined
ref
ConfettiRef

Ref object to access fire method from outside the component.

null

Props for ConfettiButton

PropTypeDescriptionDefault
options

ConfettiOptions & ConfettiGlobalOptions & { canvas?: HTMLCanvasElement }

Additional confetti options that apply when the button is clicked.

undefined
children
React.ReactNode

The content to display within the button.

undefined
...props
ButtonProps

Additional properties passed to the underlying Button component.

undefined