This is an example project build in top of the boilerplate template Ui Component Library by BoilerplateHQ.

What you see is what you get. Meaning, this is exactly what you get when you clone the repository and run the project.

Confetti Button

Beautifully designed button that makes it rain confetti!

Preview

This is how the code looks like in action.

Step-By-Step Guide

1. Install Dependencies

Toast by Sonner - Follow instructions by shadcn/ui - toast

cli

1npm install sonner

Button component by shadcn/ui - button

cli

1npx shadcn-ui@latest add button

2. Copy the Source Code

Confetti

@/ui/Confetti.tsx

1'use client';
2
3// Credit to: BoilerplateHQ.com
4// Import Types
5// Import External Packages
6import { useEffect, useRef } from 'react';
7// Import Components
8// Import Functions & Actions & Hooks & State
9// Import Data
10// Import Assets & Icons
11
12interface ConfettiParticle {
13	color: string; // RGB color string
14	x: number; // x-position
15	y: number; // y-position
16	diameter: number; // Size of the particle
17	tilt: number; // Tilt angle
18	tiltAngleIncrement: number; // Tilt angle increment per frame
19	tiltAngle: number; // Current tilt angle
20	particleSpeed: number; // Speed of the particle
21	waveAngle: number; // Angle for the wave movement
22	waveAngleIncrement: number; // Wave angle increment per frame
23	opacity: number; // Opacity of the particle
24}
25
26const generateConfettiParticles = (count: number): ConfettiParticle[] => {
27	const particles: ConfettiParticle[] = [];
28	for (let i = 0; i < count; i++) {
29		particles.push({
30			color: `rgb(${Math.floor(Math.random() * 256)}, ${Math.floor(Math.random() * 256)}, ${Math.floor(Math.random() * 256)})`,
31			x: Math.random() * window.innerWidth,
32			y: Math.random() * window.innerHeight - window.innerHeight,
33			diameter: Math.random() * 10 + 1,
34			tilt: Math.random() * 10 - 10,
35			tiltAngleIncrement: Math.random() * 0.07 + 0.05,
36			tiltAngle: 0,
37			particleSpeed: Math.random() * 10 + 1,
38			waveAngle: 0,
39			waveAngleIncrement: Math.random() * 0.1 + 0.05,
40			opacity: 1,
41		});
42	}
43	return particles;
44};
45
46const updateConfettiParticles = (
47	particles: ConfettiParticle[],
48	duration: number
49): ConfettiParticle[] => {
50	const opacityDecrement = 1 / (60 * (duration / 1000));
51	return particles
52		.map((particle) => ({
53			...particle,
54			x: particle.x + Math.sin(particle.waveAngle) * 2, // Horizontal movement
55			y: particle.y + particle.particleSpeed, // Vertical movement due to gravity
56			tiltAngle: particle.tiltAngle + particle.tiltAngleIncrement,
57			tilt: particle.tilt + Math.sin(particle.tiltAngle) * 12, // Tilt
58			waveAngle: particle.waveAngle + particle.waveAngleIncrement, // Wave movement
59			opacity: Math.max(0, particle.opacity - opacityDecrement),
60		}))
61		.filter((particle) => particle.y < window.innerHeight); // Keep particles within the canvas
62};
63
64/**
65 * Renders a confetti animation on a canvas element.
66 * @param isActive - Determines whether the confetti animation is active.
67 * @param duration - The duration of the confetti animation in milliseconds.
68 * @returns The Confetti component.
69 */
70export default function Confetti({
71	isActive,
72	duration,
73}: {
74	isActive: boolean;
75	duration: number;
76}) {
77	const canvasRef = useRef<HTMLCanvasElement>(null);
78
79	useEffect(() => {
80		if (!isActive || !canvasRef.current) return;
81
82		const canvas = canvasRef.current;
83		const ctx = canvas.getContext('2d');
84		if (!ctx) return;
85
86		canvas.width = window.innerWidth;
87		canvas.height = window.innerHeight;
88
89		let particles = generateConfettiParticles(1000);
90		let animationFrameId: number;
91
92		const animate = () => {
93			ctx.clearRect(0, 0, canvas.width, canvas.height);
94			particles = updateConfettiParticles(particles, duration);
95
96			// Draw particles
97			particles.forEach((particle) => {
98				ctx.beginPath();
99				ctx.arc(particle.x, particle.y, particle.diameter / 2, 0, 2 * Math.PI);
100				ctx.fillStyle = particle.color;
101				ctx.globalAlpha = particle.opacity;
102				ctx.fill();
103			});
104			ctx.globalAlpha = 1;
105
106			animationFrameId = requestAnimationFrame(animate);
107		};
108
109		animate();
110
111		const timeoutId = setTimeout(() => {
112			cancelAnimationFrame(animationFrameId);
113		}, duration);
114
115		return () => {
116			clearTimeout(timeoutId);
117			cancelAnimationFrame(animationFrameId);
118		};
119	}, [isActive, duration]);
120
121	return (
122		<canvas
123			ref={canvasRef}
124			className="h-lvh w-full absolute"
125			style={{
126				pointerEvents: 'none',
127				zIndex: 9999,
128			}}
129		/>
130	);
131}

Button

@/component/ConfettiButton.tsx

1'use client';
2
3// Import Types
4// Import External Packages
5import { useState } from 'react';
6import { toast } from 'sonner';
7// Import Components
8import { Button } from '@/ui/Button';
9import Confetti from '@/ui/Confetti';
10// Import Functions & Actions & Hooks & State
11// Import Data
12// Import Assets & Icons
13
14export default function ConfettiButton() {
15	const [isConfettiActive, setIsConfettiActive] = useState(false);
16	return (
17		<>
18			<Button
19				onClick={() => {
20					toast.success(
21						'Oh WOW! You actually clicked the button! See ... it is well-designed! As gratitude, enjoy some confetti. 🎉'
22					);
23
24					setTimeout(() => {
25						setIsConfettiActive(true);
26					}, 3000);
27
28					setTimeout(() => {
29						setIsConfettiActive(false);
30					}, 6000);
31				}}
32				variant="default"
33				size="lg"
34				className="hover:bg-primary/80 animate-pulse shadow-2xl shadow-neutral-500"
35			>
36				Click Me
37			</Button>
38			<Confetti isActive={isConfettiActive} duration={3000} />
39		</>
40	);
41}

3. Use in your App

page.tsx

1export default function Page() {
2	return (
3		<div className='relative dark:bg-black max-w-5xl mx-auto'>
4			<div className="mx-auto max-w-7xl py-16">
5				<div className="mx-auto text-center">
6					<h1 className="text-4xl font-bold tracking-tight text-gray-900 dark:text-white sm:text-6xl max-w-2xl mx-auto">
7						Buttons Rule The World
8					</h1>
9
10					<p className="mt-6 text-lg leading-8 text-muted-foreground dark:text-zinc-200 max-w-4xl mx-auto">
11						We knead the code and bake the pixels to perfection to serve you the
12						most delightful online buttons - LIKE, SUBMIT, and everything in
13						between.
14					</p>
15					<div className="mt-10 flex items-center justify-center gap-x-6">
16						<ConfettiButton />
17					</div>
18				</div>
19			</div>
20		</div>
21	);
22}

Tech Stack

Only the finest ingredients are used in our products. We made sure that we only use the best technologies available which offer a free tier! Yes, you read that right. Everything can be set up for USD 0. The only thing you will need is a domain name.

Tailwind

Tailwind

The most popular CSS framework. It is easy to use, well documented and has a great community.

CSS Framework

shadcn/ui

shadcn/ui

Beautifully designed open-source component library

UI Library

Example Usage

See this component live in action. Are you using the component on your website? Shoot us an email (support@boilerplatehq.com) and we will link to you.

BoilerplateHQ
External link to https://boilerplateHQ.com

BoilerplateHQ

BoilerplateHQ is using the confetti effect after someone made a purchase!

Example Usage of React Component

Your Website?

Are you using this component? Shoot us an email and we will link to you.

Frequently Asked Questions

We have compiled a list of frequently asked questions. If you have any other questions, please do not hesitate to contact us via email or using the chat function. We are here to help!

What are you waiting for?

Button up and get started with this amazing product!

Excited about buttons as we are?

Get the latest buttons and button news straight to your inbox.

DISCLAIMER: This is the Newsletter of BoilerplateHQ! Powered by Beehiiv.com. Unsubscribe at anytime by clicking the unsubscribe button in any email sent to you. Read our privacy policy