Skip to content
Magic UI SolidMagicUI Solid

Confetti

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

Confetti
@/demos/ConfettiDemo.tsx
import type { ConfettiRef } from '@/registry/magicui/confetti'
import { Confetti } from '@/registry/magicui/confetti'
export function ConfettiDemo() {
let confettiRef: ConfettiRef | undefined
return (
<div class="
relative flex h-125 w-full flex-col items-center justify-center
overflow-hidden rounded-lg border bg-background
"
>
<span class="
pointer-events-none bg-linear-to-b from-black to-gray-300/80
bg-clip-text text-center text-8xl leading-none font-semibold
whitespace-pre-wrap text-transparent
dark:from-white dark:to-slate-900/10
"
>
Confetti
</span>
<Confetti
ref={(api) => {
confettiRef = api
}}
class="absolute top-0 left-0 z-0 size-full"
onMouseEnter={() => {
confettiRef?.fire({})
}}
/>
</div>
)
}
Terminal window
pnpm dlx shadcn@latest add @magicui-solid/confetti
import { Confetti, type ConfettiRef } from '@/components/magicui/confetti'
export function ConfettiDemo() {
let confettiRef: ConfettiRef | undefined
return (
<div>
<Confetti
ref={(api) => {
confettiRef = api
}}
manualstart
/>
<button
onClick={() => {
confettiRef?.fire()
}}
>
Trigger Confetti
</button>
</div>
)
}
@/demos/ConfettiBasicCannonDemo.tsx
import { ConfettiButton } from '@/registry/magicui/confetti'
export function ConfettiBasicCannonDemo() {
return (
<div class="relative">
<ConfettiButton>Confetti</ConfettiButton>
</div>
)
}
@/demos/ConfettiRandomDirectionDemo.tsx
import { ConfettiButton } from '@/registry/magicui/confetti'
export function ConfettiRandomDirectionDemo() {
return (
<div class="relative">
<ConfettiButton
options={{
get angle() {
return Math.random() * 360
},
}}
>
Random Confetti
</ConfettiButton>
</div>
)
}
@/demos/ConfettiFireworksDemo.tsx
import confetti from 'canvas-confetti'
import { Button } from '@/components/ui/button'
export function ConfettiFireworksDemo() {
const handleClick = () => {
const duration = 5 * 1000
const animationEnd = Date.now() + duration
const defaults = { startVelocity: 30, spread: 360, ticks: 60, zIndex: 0 }
const randomInRange = (min: number, max: number) =>
Math.random() * (max - min) + min
const interval = window.setInterval(() => {
const timeLeft = animationEnd - Date.now()
if (timeLeft <= 0) {
clearInterval(interval)
return
}
const particleCount = 50 * (timeLeft / duration)
void confetti({
...defaults,
particleCount,
origin: { x: randomInRange(0.1, 0.3), y: Math.random() - 0.2 },
})
void confetti({
...defaults,
particleCount,
origin: { x: randomInRange(0.7, 0.9), y: Math.random() - 0.2 },
})
}, 250)
}
return (
<div class="relative">
<Button onClick={handleClick}>Trigger Fireworks</Button>
</div>
)
}
@/demos/ConfettiSideCannonsDemo.tsx
import confetti from 'canvas-confetti'
import { Button } from '@/components/ui/button'
export function ConfettiSideCannonsDemo() {
const handleClick = () => {
const end = Date.now() + 3 * 1000
const colors = ['#a786ff', '#fd8bbc', '#eca184', '#f8deb1']
const frame = () => {
if (Date.now() > end)
return
void confetti({
particleCount: 2,
angle: 60,
spread: 55,
startVelocity: 60,
origin: { x: 0, y: 0.5 },
colors,
})
void confetti({
particleCount: 2,
angle: 120,
spread: 55,
startVelocity: 60,
origin: { x: 1, y: 0.5 },
colors,
})
requestAnimationFrame(frame)
}
frame()
}
return (
<div class="relative">
<Button onClick={handleClick}>Trigger Side Cannons</Button>
</div>
)
}
@/demos/ConfettiStarsDemo.tsx
import confetti from 'canvas-confetti'
import { Button } from '@/components/ui/button'
export function ConfettiStarsDemo() {
const handleClick = () => {
const defaults = {
spread: 360,
ticks: 50,
gravity: 0,
decay: 0.94,
startVelocity: 30,
colors: ['#FFE400', '#FFBD00', '#E89400', '#FFCA6C', '#FDFFB8'],
}
const shoot = () => {
void confetti({
...defaults,
particleCount: 40,
scalar: 1.2,
shapes: ['star'],
})
void confetti({
...defaults,
particleCount: 10,
scalar: 0.75,
shapes: ['circle'],
})
}
setTimeout(shoot, 0)
setTimeout(shoot, 100)
setTimeout(shoot, 200)
}
return (
<div class="relative">
<Button onClick={handleClick}>Trigger Stars</Button>
</div>
)
}
@/demos/ConfettiCustomShapesDemo.tsx
import confetti from 'canvas-confetti'
import { Button } from '@/components/ui/button'
export function ConfettiCustomShapesDemo() {
const handleClick = () => {
const scalar = 2
const triangle = confetti.shapeFromPath({
path: 'M0 10 L5 0 L10 10z',
})
const square = confetti.shapeFromPath({
path: 'M0 0 L10 0 L10 10 L0 10 Z',
})
const coin = confetti.shapeFromPath({
path: 'M5 0 A5 5 0 1 0 5 10 A5 5 0 1 0 5 0 Z',
})
const tree = confetti.shapeFromPath({
path: 'M5 0 L10 10 L0 10 Z',
})
const defaults = {
spread: 360,
ticks: 60,
gravity: 0,
decay: 0.96,
startVelocity: 20,
shapes: [triangle, square, coin, tree],
scalar,
}
const shoot = () => {
void confetti({
...defaults,
particleCount: 30,
})
void confetti({
...defaults,
particleCount: 5,
})
void confetti({
...defaults,
particleCount: 15,
scalar: scalar / 2,
shapes: ['circle'],
})
}
setTimeout(shoot, 0)
setTimeout(shoot, 100)
setTimeout(shoot, 200)
}
return (
<div class="relative flex items-center justify-center">
<Button onClick={handleClick}>Trigger Shapes</Button>
</div>
)
}
@/demos/ConfettiEmojiDemo.tsx
import confetti from 'canvas-confetti'
import { Button } from '@/components/ui/button'
export function ConfettiEmojiDemo() {
const handleClick = () => {
const scalar = 2
const unicorn = confetti.shapeFromText({ text: '🦄', scalar })
const defaults = {
spread: 360,
ticks: 60,
gravity: 0,
decay: 0.96,
startVelocity: 20,
shapes: [unicorn],
scalar,
}
const shoot = () => {
void confetti({
...defaults,
particleCount: 30,
})
void confetti({
...defaults,
particleCount: 5,
})
void confetti({
...defaults,
particleCount: 15,
scalar: scalar / 2,
shapes: ['circle'],
})
}
setTimeout(shoot, 0)
setTimeout(shoot, 100)
setTimeout(shoot, 200)
}
return (
<div class="relative justify-center">
<Button onClick={handleClick}>Trigger Emoji</Button>
</div>
)
}
Prop Type Default Description
ref (api: ConfettiRef) => void - Callback exposing an imperative fire(options?: ConfettiOptions) method
options ConfettiOptions {} Default options for automatic or imperative confetti triggers
globalOptions ConfettiGlobalOptions { resize: true, useWorker: true } Global options passed to canvas-confetti.create()
manualstart Boolean false Prevents automatic confetti firing on component mount
class String undefined Custom CSS classes applied to the canvas element
Method Parameters Return Type Description
fire options?: ConfettiOptions Promise<void> / void Imperatively triggers a confetti burst on the canvas instance
Field Type Default Description
particleCount Integer 50 Number of confetti particles to launch
angle Number 90 Launch angle in degrees
spread Number 45 Spread angle in degrees
startVelocity Number 45 Initial particle velocity
decay Number 0.9 Velocity decay rate
gravity Number 1 Gravity applied to particles
drift Number 0 Horizontal drift factor
flat Boolean false Renders flat 2D shapes
ticks Number 200 Number of animation frames confetti lasts
origin { x: number, y: number } { x: 0.5, y: 0.5 } Normalized viewport origin coordinates (0.0 to 1.0)
colors Array of Strings HEX array Custom HEX color palette
shapes Array of Strings ['square', 'circle', 'star'] Particle shapes or custom text shapes
zIndex Integer 9999 Z-index of the confetti burst
scalar Number 1 Scaling factor for particle dimensions
Prop Type Default Description
options ConfettiOptions & ConfettiGlobalOptions {} Options for the confetti launched on button click
children JSX.Element null Button label and child elements

Original:

magicui.design/docs/components/confetti