Introducing Magic UI Pro - 50+ blocks and templates to build beautiful landing pages in minutes.


Docs
Confetti

Confetti

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

Confetti

Installation

The confetti is built on canvas confetti.

npm install --save canvas-confetti

Copy and paste the following code into your project.

components/magicui/confetti.tsx
import { Button, ButtonProps } from "@/components/ui/button";
import confetti from "canvas-confetti";
import type { ReactNode } from "react";
import React, {
  createContext,
  forwardRef,
  useCallback,
  useEffect,
  useImperativeHandle,
  useMemo,
  useRef,
} from "react";
 
import type {
  GlobalOptions as ConfettiGlobalOptions,
  Options as ConfettiOptions,
} from "canvas-confetti";
 
import type { CreateTypes as ConfettiInstance } from "canvas-confetti";
 
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>
  );
}
 
export { Confetti, ConfettiButton };
 
export default Confetti;
 

Examples

Basic

Random Direction

Fireworks

Side Cannons

Stars

Custom Shapes

Emoji

Props

Confetti

PropTypeDescriptionDefault
particleCountIntegerThe number of confetti particles to launch50
angleNumberThe angle in degrees at which to launch confetti90
spreadNumberThe spread in degrees of the confetti45
startVelocityNumberThe initial velocity of the confetti45
decayNumberThe rate at which confetti slows down0.9
gravityNumberThe gravity applied to confetti particles1
driftNumberThe horizontal drift applied to particles0
flatBooleanWhether confetti particles are flatfalse
ticksNumberThe number of frames confetti lasts200
originObjectThe origin point of the confetti{ x: 0.5, y: 0.5 }
colorsArray of StringsArray of color strings in HEX format['#26ccff', '#a25afd', '#ff5e7e', '#88ff5a', '#fcff42', '#ffa62d', '#ff36ff']
shapesArray of StringsArray of shapes for the confetti['square', 'circle']
zIndexIntegerThe z-index of the confetti100
disableForReducedMotionBooleanDisables confetti for users who prefer no motionfalse
useWorkerBooleanUse Web Worker for better performancetrue
resizeBooleanWhether to resize the canvastrue
canvasHTMLCanvasElement or nullCustom canvas element to draw confettinull
scalarNumberScaling factor for confetti size1

ConfettiButton

PropTypeDescriptionDefault
optionsObjectOptions for the confetti
childrenReact.ReactNodeChildren to render inside the buttonnull

Credits