A simple, lightweight React hook for creating confetti animations with directional shooting and intelligent auto-triggering. Perfect for celebrations, notifications, and interactive experiences.
- π― Directional shooting: Left, right, top, or center burst patterns
- π Source positioning: Confetti originates from any element position
- β‘ Auto-trigger: Automatic confetti on component mount with smart controls
- π Loop mode: Continuous confetti celebrations with customizable intervals
- π¨ Customizable: Particle count, colors, timing, and physics
- π‘ One-line usage: Just call
triggerConfetti('direction') - π± Responsive: Works on all screen sizes
- π§ TypeScript: Full type safety included
- πͺΆ Lightweight: Zero dependencies (except React)
npm install react-confetti-shooterOr with bun:
bun add react-confetti-shooterimport React from 'react';
import { useConfetti } from 'react-confetti-shooter';
function MyComponent() {
const { triggerConfetti, ConfettiRenderer } = useConfetti();
return (
<div>
<ConfettiRenderer />
<button onClick={() => triggerConfetti('center')}>
Celebrate! π
</button>
</div>
);
}import React from 'react';
import { useConfetti } from 'react-confetti-shooter';
function WelcomeComponent() {
const { ConfettiRenderer } = useConfetti({
autoTrigger: {
enabled: true,
direction: 'center',
count: 100,
delay: 1000 // Triggers once after 1 second
}
});
return (
<div>
<ConfettiRenderer />
<h1>Welcome! π</h1>
</div>
);
}import React from 'react';
import { useConfetti } from 'react-confetti-shooter';
function PartyComponent() {
const { ConfettiRenderer } = useConfetti({
autoTrigger: {
enabled: true,
direction: 'center',
count: 75,
delay: 500,
loop: true, // Enable continuous mode
interval: 3000 // Trigger every 3 seconds
}
});
return (
<div>
<ConfettiRenderer />
<h1>π Party Mode Activated! π</h1>
</div>
);
}Hook that manages confetti animations with intelligent auto-triggering.
Parameters:
options:UseConfettiOptions(optional) - Configuration options
Options:
autoTrigger:AutoTriggerOptions(optional) - Auto-trigger configurationenabled:boolean- Whether to auto-trigger confettidirection:Direction(optional, default: 'center') - Direction to shootcount:number(optional, default: 50) - Number of particlesdelay:number(optional, default: 0) - Initial delay in millisecondsloop:boolean(optional, default: false) - Enable continuous triggeringinterval:number(optional, default: 3000) - Time between loops (ms)
Returns:
Manually triggers a confetti burst.
Parameters:
direction:'left' | 'right' | 'top' | 'center'- Direction to shoot confetticount:number(optional, default: 50) - Number of particles to createsourceX:number(optional, default: center X) - X coordinate of confetti sourcesourceY:number(optional, default: center Y) - Y coordinate of confetti source
React component that renders the confetti particles. Must be included in your JSX.
Boolean indicating if confetti animation is currently running.
Number of active confetti particles currently on screen.
Perfect for welcome screens, success notifications, and one-time celebrations.
// Triggers once when component mounts
const { ConfettiRenderer } = useConfetti({
autoTrigger: {
enabled: true,
direction: 'center',
count: 100,
delay: 1500 // Wait 1.5 seconds
}
});Behavior:
- Triggers confetti once after the specified delay
- Will not trigger again unless the component is remounted
- Perfect for: Welcome screens, achievement unlocks, form submissions
Great for party modes, celebration pages, and ongoing festivities.
// Continuous confetti every few seconds
const { ConfettiRenderer } = useConfetti({
autoTrigger: {
enabled: true,
direction: 'center',
count: 50,
delay: 1000, // Initial delay
loop: true, // Enable looping
interval: 4000 // Repeat every 4 seconds
}
});Behavior:
- Initial trigger after
delaymilliseconds - Continues triggering every
intervalmilliseconds - Stops when
enabledis set to false or component unmounts - Perfect for: Party modes, celebration pages, background ambiance
const { triggerConfetti, ConfettiRenderer } = useConfetti();
<button onClick={() => triggerConfetti('left', 60)}>
Shoot Left β
</button><button onClick={() => triggerConfetti('right', 60)}>
Shoot Right β
</button><button onClick={() => triggerConfetti('top', 80)}>
Shoot Up β
</button><button onClick={() => triggerConfetti('center', 100)}>
Explode π₯
</button>function InteractiveConfetti() {
const { triggerConfetti, ConfettiRenderer } = useConfetti();
const handleButtonClick = (
direction: Direction,
event: React.MouseEvent<HTMLButtonElement>
) => {
const rect = event.currentTarget.getBoundingClientRect();
const sourceX = rect.left + rect.width / 2;
const sourceY = rect.top + rect.height / 2;
triggerConfetti(direction, 50, sourceX, sourceY);
};
return (
<div className="grid grid-cols-2 gap-4">
<ConfettiRenderer />
{(['left', 'right', 'top', 'center'] as const).map(direction => (
<button
key={direction}
onClick={(e) => handleButtonClick(direction, e)}
className="p-4 bg-blue-500 text-white rounded-lg hover:bg-blue-600"
>
{direction.charAt(0).toUpperCase() + direction.slice(1)}
</button>
))}
</div>
);
}function DynamicConfetti() {
const [isPartyMode, setIsPartyMode] = useState(false);
const { ConfettiRenderer, isAnimating } = useConfetti({
autoTrigger: {
enabled: isPartyMode,
direction: 'center',
count: 60,
delay: 500,
loop: true,
interval: 2500
}
});
return (
<div>
<ConfettiRenderer />
<button
onClick={() => setIsPartyMode(!isPartyMode)}
className={`px-6 py-3 rounded-lg text-white font-semibold ${
isPartyMode ? 'bg-red-500' : 'bg-green-500'
}`}
>
{isPartyMode ? 'βΉοΈ Stop Party' : 'π Start Party Mode'}
</button>
<p className="mt-4">
Status: {isPartyMode ? 'π Party Active' : 'π΄ Party Stopped'}
{isAnimating && ' | β¨ Animating'}
</p>
</div>
);
}function MultiStageCelebration() {
const [stage, setStage] = useState(0);
const stages = [
{ direction: 'left', count: 30, delay: 500 },
{ direction: 'right', count: 30, delay: 1000 },
{ direction: 'center', count: 100, delay: 1500 }
];
const { ConfettiRenderer } = useConfetti({
autoTrigger: stage < stages.length ? {
enabled: true,
...stages[stage]
} : { enabled: false }
});
useEffect(() => {
if (stage < stages.length - 1) {
const timer = setTimeout(() => setStage(s => s + 1), 2000);
return () => clearTimeout(timer);
}
}, [stage]);
return (
<div>
<ConfettiRenderer />
<div className="text-center p-8">
<h2>π Epic Celebration Sequence! π</h2>
<p>Stage {stage + 1} of {stages.length}</p>
<button
onClick={() => setStage(0)}
className="mt-4 px-4 py-2 bg-purple-500 text-white rounded"
>
π Restart Sequence
</button>
</div>
</div>
);
}The library includes vibrant, celebration-ready colors:
#ff6b6b(Coral Red)#4ecdc4(Turquoise)#45b7d1(Sky Blue)#ffd93d(Sunshine Yellow)#ff8c42(Orange Burst)#a8e6cf(Mint Green)#ff6f91(Bubblegum Pink)
- Physics: Realistic gravity and motion
- Rotation: Particles rotate as they fall
- Cleanup: Automatic removal when off-screen
- Performance: Optimized with
requestAnimationFrame
function SuccessPage() {
const { ConfettiRenderer } = useConfetti({
autoTrigger: {
enabled: true,
direction: 'center',
count: 150,
delay: 800
}
});
return (
<div className="min-h-screen flex items-center justify-center bg-green-50">
<ConfettiRenderer />
<div className="text-center p-8 bg-white rounded-xl shadow-lg">
<div className="text-6xl mb-4">π</div>
<h1 className="text-3xl font-bold text-green-600 mb-2">Success!</h1>
<p className="text-gray-600">Your payment has been processed.</p>
</div>
</div>
);
}function CelebrationDashboard() {
const { triggerConfetti, ConfettiRenderer, particleCount, isAnimating } = useConfetti();
const [autoMode, setAutoMode] = useState(false);
const { ConfettiRenderer: AutoRenderer } = useConfetti({
autoTrigger: {
enabled: autoMode,
direction: 'center',
count: 40,
loop: true,
interval: 3000
}
});
return (
<div className="p-6 max-w-4xl mx-auto">
<ConfettiRenderer />
<AutoRenderer />
<h1 className="text-4xl font-bold text-center mb-8">
π Confetti Control Center π
</h1>
{/* Manual Controls */}
<div className="bg-white rounded-lg p-6 shadow-md mb-6">
<h2 className="text-xl font-semibold mb-4">Manual Triggers</h2>
<div className="grid grid-cols-2 md:grid-cols-4 gap-4">
{(['left', 'right', 'top', 'center'] as const).map(direction => (
<button
key={direction}
onClick={() => triggerConfetti(direction, 60)}
className="py-3 px-4 bg-blue-500 text-white rounded-lg hover:bg-blue-600 transition-colors"
disabled={isAnimating}
>
{direction.charAt(0).toUpperCase() + direction.slice(1)}
</button>
))}
</div>
</div>
{/* Auto Mode */}
<div className="bg-white rounded-lg p-6 shadow-md mb-6">
<h2 className="text-xl font-semibold mb-4">Auto Mode</h2>
<button
onClick={() => setAutoMode(!autoMode)}
className={`px-6 py-3 rounded-lg text-white font-semibold transition-colors ${
autoMode ? 'bg-red-500 hover:bg-red-600' : 'bg-green-500 hover:bg-green-600'
}`}
>
{autoMode ? 'βΉοΈ Stop Auto Mode' : 'π Start Auto Mode'}
</button>
</div>
{/* Status */}
<div className="bg-gray-100 rounded-lg p-4">
<h3 className="font-semibold mb-2">Status</h3>
<div className="grid grid-cols-2 gap-4 text-sm">
<div>Active Particles: <span className="font-mono">{particleCount}</span></div>
<div>Animation: <span className={`font-mono ${isAnimating ? 'text-green-600' : 'text-gray-500'}`}>
{isAnimating ? 'Running' : 'Idle'}
</span></div>
<div>Auto Mode: <span className={`font-mono ${autoMode ? 'text-blue-600' : 'text-gray-500'}`}>
{autoMode ? 'Enabled' : 'Disabled'}
</span></div>
</div>
</div>
</div>
);
}Full TypeScript support with comprehensive type definitions:
import {
useConfetti,
Direction,
UseConfettiOptions,
AutoTriggerOptions
} from 'react-confetti-shooter';
// Type-safe direction
const direction: Direction = 'center';
// Fully typed options
const options: UseConfettiOptions = {
autoTrigger: {
enabled: true,
direction: 'center',
count: 50,
delay: 1000,
loop: true,
interval: 3000
}
};
// Type-safe hook usage
const { triggerConfetti, ConfettiRenderer, isAnimating, particleCount } = useConfetti(options);- β Chrome/Edge 60+
- β Firefox 55+
- β Safari 12+
- β Mobile browsers
- Automatic particle cleanup when off-screen
- Efficient
requestAnimationFrameanimations - Memory leak prevention with proper cleanup
- Lightweight bundle size
- Adapts to all screen sizes
- Viewport-relative positioning
- Touch-friendly interactions
# Clone and setup
git clone https://github.com/LynchzDEV/lynchz-confetti.git
cd lynchz-confetti
bun install
# Build
bun run build
# Development
bun run dev
# Test locally
open test-example.html
open test-loop-example.htmlContributions are welcome! Please feel free to submit issues and pull requests.
- Fork the repository
- Create your feature branch (
git checkout -b feature/amazing-feature) - Commit your changes (
git commit -m 'Add amazing feature') - Push to the branch (
git push origin feature/amazing-feature) - Open a Pull Request
MIT License - see the LICENSE file for details.
Built with β€οΈ for the React community. Perfect for adding joy, celebration, and interactive delight to your applications!
Ready to make your users smile? Let's shoot some confetti! πβ¨