Confetti
Confetti is a React and Tailwind CSS effect component from Magic UI, licensed MIT. Copy it and paste it into your project.
Magic UI
MIT
effects
What it is
Confetti is a React and Tailwind CSS effect component from Magic UI, licensed MIT. Copy it and paste it into your project.
How to use it
Open it in the catalogue, press copy, and paste it into your project. The prompt you copy carries the code and the rules that tell your agent to reproduce it without changing anything.
Five free copies a month. No card.
Needs
npm i canvas-confetti @types/canvas-confetti
Licence
This component comes from Magic UI and is redistributed under the MIT licence, which permits commercial use. The copyright notice travels with the code.
Confetti
"use client"
import type { ReactNode } from "react"
import React, {
createContext,
forwardRef,
useCallback,
useEffect,
useImperativeHandle,
useMemo,
useRef,
} from "react"
import type {
GlobalOptions as ConfettiGlobalOptions,
CreateTypes as ConfettiInstance,
Options as ConfettiOptions,
} from "canvas-confetti"
import confetti from "canvas-confetti"
import { Button } from "@/components/ui/button"
export type ConfettiRef = {
fire: (options?: ConfettiOptions) => Promise<void> | void
}
type Props = React.ComponentPropsWithRef<"canvas"> & {
options?: ConfettiOptions
globalOptions?: ConfettiGlobalOptions
manualstart?: boolean
children?: ReactNode
}
const ConfettiContext = createContext<ConfettiRef | null>(null)
const ConfettiComponent = forwardRef<ConfettiRef, Props>((props, ref) => {
const {
options,
globalOptions = { resize: true, useWorker: true },
manualstart = false,
children,
className,
...rest
} = props
const canvasNodeRef = useRef<HTMLCanvasElement | null>(null)
const instanceRef = useRef<ConfettiInstance | null>(null)
const optionsRef = useRef(options)
const globalOptionsRef = useRef(globalOptions)
useEffect(() => {
optionsRef.current = options
}, [options])
useEffect(() => {
globalOptionsRef.current = globalOptions
}, [globalOptions])
useEffect(() => {
if (canvasNodeRef.current && !instanceRef.current) {
instanceRef.current = confetti.create(canvasNodeRef.current, {
resize: true,
useWorker: true,
...globalOptionsRef.current,
})
}
return () => {
instanceRef.current?.reset()
instanceRef.current = null
}
}, [])
const fire = useCallback(async (opts: ConfettiOptions = {}) => {
try {
await instanceRef.current?.({
...optionsRef.current,
...opts,
})
} catch (error) {
console.error("Confetti error:", error)
}
}, [])
const api = useMemo<ConfettiRef>(() => ({ fire }), [fire])
useImperativeHandle(ref, () => api, [api])
useEffect(() => {
if (!manualstart) {
void fire()
}
}, [manualstart, fire])
return (
<ConfettiContext.Provider value={api}>
<canvas ref={canvasNodeRef} className={className} {...rest} />
{children}
</ConfettiContext.Provider>
)
})
ConfettiComponent.displayName = "Confetti"
export const Confetti = ConfettiComponent
export interface ConfettiButtonProps extends React.ComponentPropsWithoutRef<
typeof Button
> {
options?: ConfettiOptions &
ConfettiGlobalOptions & { canvas?: HTMLCanvasElement }
}
export const ConfettiButton = forwardRef<
HTMLButtonElement,
ConfettiButtonProps
>(({ options, children, onClick, ...props }, ref) => {
const handleClick: ConfettiButtonProps["onClick"] = async (event) => {
try {
onClick?.(event)
if (event?.defaultPrevented) return
const target = event?.currentTarget
if (target && "getBoundingClientRect" in target) {
const rect = target.getBoundingClientRect()
const origin = {
x: (rect.left + rect.width / 2) / window.innerWidth,
y: (rect.top + rect.height / 2) / window.innerHeight,
}
await confetti({
zIndex: 9999,
...options,
origin,
})
}
} catch (error) {
console.error("Confetti button error:", error)
}
}
return (
<Button ref={ref} type="button" onClick={handleClick} {...props}>
{children}
</Button>
)
})
ConfettiButton.displayName = "ConfettiButton"