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

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