Skip to main content

useConfetti

The useConfetti hook provides a simple way to trigger confetti animations in your React application. It creates a canvas-based particle system with customizable particle count, origin position, and animation duration.

Installation

npm install @craft-ui/hooks

Import

import { useConfetti } from "@craft-ui/hooks";

Usage

import { useConfetti } from "@craft-ui/hooks";

function CelebrationButton() {
  const triggerConfetti = useConfetti();

  return (
    <button
      onClick={() => triggerConfetti({ particleCount: 100, origin: "center" })}
    >
      Celebrate!
    </button>
  );
}

Returns

triggerConfetti
(options?: ConfettiOptions) => void
A function that triggers the confetti animation when called. Accepts an optional configuration object.

ConfettiOptions

particleCount
number
default:50
The total number of particles to generate. The particles are split into 75% confetti pieces and 25% sequins.
origin
string
default:"center"
The origin point for the confetti animation. Can be one of:
  • "center" - Center of the screen
  • "top" - Top center
  • "bottom" - Bottom center
  • "left" - Left center
  • "right" - Right center
  • "top-left" - Top left corner
  • "top-right" - Top right corner
  • "bottom-left" - Bottom left corner
  • "bottom-right" - Bottom right corner
duration
number
default:3000
The duration in milliseconds before the canvas is cleaned up (if no particles remain).

Type Definition

interface ConfettiOptions {
  particleCount?: number;
  origin?:
    | "top"
    | "bottom"
    | "left"
    | "right"
    | "center"
    | "top-left"
    | "top-right"
    | "bottom-left"
    | "bottom-right";
  duration?: number;
}

function useConfetti(): (options?: ConfettiOptions) => void;

Examples

Success Message

import { useConfetti } from "@craft-ui/hooks";

function SuccessMessage() {
  const triggerConfetti = useConfetti();

  const handleSuccess = () => {
    triggerConfetti({
      particleCount: 150,
      origin: "top",
      duration: 5000,
    });
  };

  return (
    <button onClick={handleSuccess}>
      Complete Task
    </button>
  );
}

Multiple Origins

import { useConfetti } from "@craft-ui/hooks";

function PartyMode() {
  const triggerConfetti = useConfetti();

  const startParty = () => {
    // Fire from multiple corners
    triggerConfetti({ particleCount: 50, origin: "top-left" });
    setTimeout(() => {
      triggerConfetti({ particleCount: 50, origin: "top-right" });
    }, 200);
    setTimeout(() => {
      triggerConfetti({ particleCount: 50, origin: "bottom-left" });
    }, 400);
    setTimeout(() => {
      triggerConfetti({ particleCount: 50, origin: "bottom-right" });
    }, 600);
  };

  return <button onClick={startParty}>Party Time!</button>;
}

Form Submission

import { useConfetti } from "@craft-ui/hooks";
import { useState } from "react";

function ContactForm() {
  const triggerConfetti = useConfetti();
  const [submitted, setSubmitted] = useState(false);

  const handleSubmit = async (e: React.FormEvent) => {
    e.preventDefault();
    // Submit form...
    await submitForm();
    
    setSubmitted(true);
    triggerConfetti({
      particleCount: 200,
      origin: "center",
      duration: 4000,
    });
  };

  return (
    <form onSubmit={handleSubmit}>
      {submitted ? (
        <div>Thank you for your submission!</div>
      ) : (
        <>
          <input type="text" placeholder="Name" />
          <input type="email" placeholder="Email" />
          <button type="submit">Submit</button>
        </>
      )}
    </form>
  );
}

Achievement Unlock

import { useConfetti } from "@craft-ui/hooks";
import { useEffect } from "react";

function AchievementNotification({ achievement }) {
  const triggerConfetti = useConfetti();

  useEffect(() => {
    if (achievement.isNew) {
      triggerConfetti({
        particleCount: 100,
        origin: "bottom",
        duration: 3000,
      });
    }
  }, [achievement, triggerConfetti]);

  return (
    <div className="achievement">
      <h3>{achievement.title}</h3>
      <p>{achievement.description}</p>
    </div>
  );
}

Custom Button Component

import { useConfetti } from "@craft-ui/hooks";

interface ConfettiButtonProps {
  onClick?: () => void;
  children: React.ReactNode;
  confettiOptions?: ConfettiOptions;
}

function ConfettiButton({ onClick, children, confettiOptions }: ConfettiButtonProps) {
  const triggerConfetti = useConfetti();

  const handleClick = () => {
    triggerConfetti(confettiOptions);
    onClick?.();
  };

  return (
    <button onClick={handleClick}>
      {children}
    </button>
  );
}

// Usage
function App() {
  return (
    <ConfettiButton
      confettiOptions={{ particleCount: 150, origin: "top" }}
      onClick={() => console.log("Clicked!")}
    >
      Click me!
    </ConfettiButton>
  );
}

Common Patterns

Sequential Bursts

Create a sequence of confetti bursts:
const triggerSequence = () => {
  triggerConfetti({ particleCount: 30, origin: "left" });
  setTimeout(() => triggerConfetti({ particleCount: 30, origin: "right" }), 300);
  setTimeout(() => triggerConfetti({ particleCount: 60, origin: "center" }), 600);
};

Conditional Confetti

Trigger confetti based on conditions:
const handleScore = (points: number) => {
  if (points >= 100) {
    triggerConfetti({ particleCount: 200, origin: "center" });
  } else if (points >= 50) {
    triggerConfetti({ particleCount: 100, origin: "top" });
  }
};

Notes

  • The hook automatically creates a full-screen canvas overlay when triggered
  • The canvas is positioned with position: fixed and z-index: 9999
  • The canvas has pointer-events: none so it doesn’t interfere with user interactions
  • Particles are automatically cleaned up when they fall off the screen
  • The canvas is removed from the DOM when all particles have finished animating
  • Multiple confetti bursts can be active simultaneously
  • The hook automatically handles window resizing
  • All resources are properly cleaned up when the component unmounts

Build docs developers (and LLMs) love