Show HN: Partycles – Zero-dependency React animations library with 11 effects

3 days ago 2

A lightweight, performant React library for adding delightful animation effects to your applications. Perfect for celebrating user achievements, form submissions, or any moment worth highlighting.

→ Try the Live Demo

npm version Bundle Size TypeScript  MIT

  • 🎯 Simple API - Just one hook to rule them all
  • 🎨 11 Beautiful Animations - Confetti, sparkles, hearts, stars, fireworks, bubbles, snow, emoji, coins, lightning, and petals
  • 📦 Tiny Bundle - Zero dependencies, optimized for performance
  • 🚀 Performant - Optimized animations using requestAnimationFrame
  • 🎮 Full Control - Customize colors, particle count, physics, and more
  • 📱 Responsive - Works seamlessly on all devices
  • 🔧 TypeScript Support - Fully typed for excellent DX
  • React 18 Ready - Built with the latest React features
npm install partycles # or yarn add partycles # or pnpm add partycles
import { useReward } from 'partycles'; function App() { const { reward, isAnimating } = useReward('rewardId', 'confetti'); return ( <button id="rewardId" onClick={reward} disabled={isAnimating}> Click me for a surprise! 🎉 </button> ); }

That's it! No configuration needed - it just works. 🎊

  • Zero Dependencies - No bloat, just pure React code
  • One Hook - Simple useReward hook handles everything
  • 11 Animations - From confetti to lightning, we've got you covered
  • Fully Typed - Great TypeScript support out of the box
  • Customizable - Tweak colors, physics, particle count, and more
  • Performant - Optimized animations that won't slow down your app

useReward(elementId, animationType, config?)

The main hook for creating reward animations.

  • elementId (string): The ID of the element to animate from
  • animationType (string): One of: 'confetti', 'sparkles', 'hearts', 'stars', 'fireworks', 'bubbles', 'snow', 'emoji', 'coins', 'lightning', 'petals'
  • config (optional): Animation configuration object
  • reward (function): Triggers the animation
  • isAnimating (boolean): Whether the animation is currently running
interface AnimationConfig { particleCount?: number; // Number of particles (default: varies by animation) spread?: number; // Spread angle in degrees (default: 50) startVelocity?: number; // Initial velocity (default: 45) decay?: number; // Velocity decay rate (default: 0.9) lifetime?: number; // Particle lifetime (default: 100) colors?: string[]; // Array of colors (or emojis for 'emoji' type) elementSize?: number; // Size of particles in pixels duration?: number; // Total animation duration physics?: { gravity?: number; // Gravity force (default: 0.5) wind?: number; // Horizontal wind force (default: 0) friction?: number; // Air friction (default: 0.99) }; }

Classic celebration effect with colorful paper pieces.

const { reward } = useReward('buttonId', 'confetti', { particleCount: 30, spread: 60, colors: ['#ff0000', '#00ff00', '#0000ff'] });

Twinkling star effect perfect for magical moments.

const { reward } = useReward('buttonId', 'sparkles', { particleCount: 20, elementSize: 15 });

Floating hearts for likes, favorites, or love actions.

const { reward } = useReward('buttonId', 'hearts', { particleCount: 15, colors: ['#ff1744', '#e91e63', '#ff4569'] });

Shooting stars effect for achievements and ratings.

const { reward } = useReward('buttonId', 'stars', { particleCount: 25, physics: { gravity: 0.3 } });

Explosive celebration for major milestones.

const { reward } = useReward('buttonId', 'fireworks', { particleCount: 40, spread: 180 });

Gentle floating bubbles for calm, playful effects.

const { reward } = useReward('buttonId', 'bubbles', { particleCount: 20, physics: { gravity: -0.1 } });

Peaceful falling snowflakes for winter themes.

const { reward } = useReward('buttonId', 'snow', { particleCount: 50, physics: { gravity: 0.05, wind: 0.1 } });

Customizable emoji explosions for any mood.

import { useReward, emojiPresets } from 'partycles'; // Use built-in presets const { reward } = useReward('buttonId', 'emoji', { colors: emojiPresets.celebration // 🎉🎊🥳🎈🎁🍾🥂🎆 }); // Or custom emojis const { reward } = useReward('buttonId', 'emoji', { particleCount: 30, colors: ['🎉', '🎊', '🎈', '🎁', '✨'] });

Available emoji presets:

  • emojiPresets.celebration - Party emojis
  • emojiPresets.love - Heart emojis
  • emojiPresets.happy - Happy face emojis
  • emojiPresets.nature - Nature emojis
  • emojiPresets.food - Food emojis

Falling coins for rewards and achievements.

const { reward } = useReward('buttonId', 'coins', { particleCount: 25, physics: { gravity: 0.5 } });

Electric energy bursts for powerful actions.

const { reward } = useReward('buttonId', 'lightning', { particleCount: 20, spread: 360 });

Delicate flower petals floating in the wind.

const { reward } = useReward('buttonId', 'petals', { particleCount: 40, physics: { gravity: 0.08, wind: 0.15 } });
function ContactForm() { const { reward } = useReward('submit-btn', 'confetti'); const handleSubmit = async (e) => { e.preventDefault(); const success = await submitForm(); if (success) { reward(); } }; return ( <form onSubmit={handleSubmit}> {/* form fields */} <button id="submit-btn" type="submit"> Send Message </button> </form> ); }
function LikeButton() { const [liked, setLiked] = useState(false); const { reward } = useReward('like-btn', 'hearts', { particleCount: 10, elementSize: 20 }); const handleLike = () => { setLiked(!liked); if (!liked) { reward(); } }; return ( <button id="like-btn" onClick={handleLike}> {liked ? '❤️' : '🤍'} Like </button> ); }
function Achievement({ unlocked, name }) { const { reward } = useReward('achievement', 'fireworks', { particleCount: 50, spread: 120 }); useEffect(() => { if (unlocked) { reward(); } }, [unlocked, reward]); return ( <div id="achievement" className="achievement-badge"> 🏆 {name} </div> ); }
  1. Unique IDs: Ensure each animated element has a unique ID
  2. Performance: Avoid triggering multiple animations simultaneously
  3. Accessibility: Provide alternative feedback for users who prefer reduced motion
  4. Mobile: Test animations on mobile devices and adjust particle counts if needed
// Respect user preferences const prefersReducedMotion = window.matchMedia('(prefers-reduced-motion: reduce)').matches; const { reward } = useReward('buttonId', 'confetti', { particleCount: prefersReducedMotion ? 0 : 30 });
const { reward } = useReward('buttonId', 'confetti', { physics: { gravity: 0.2, // Lower gravity = floatier particles wind: 0.1, // Positive = blow right, negative = blow left friction: 0.95 // Higher = more air resistance } });
function DynamicReward() { const [intensity, setIntensity] = useState(1); const { reward } = useReward('buttonId', 'confetti', { particleCount: 30 * intensity, spread: 50 + (20 * intensity), startVelocity: 45 * intensity }); return ( <> <input type="range" min="0.5" max="2" step="0.1" value={intensity} onChange={(e) => setIntensity(parseFloat(e.target.value))} /> <button id="buttonId" onClick={reward}> Celebrate! </button> </> ); }

We welcome contributions! Please see our Contributing Guide for details.

  1. Fork the repository
  2. Create your feature branch (git checkout -b feature/amazing-feature)
  3. Commit your changes (git commit -m 'Add amazing feature')
  4. Push to the branch (git push origin feature/amazing-feature)
  5. Open a Pull Request

MIT © Jonathan Leane


Made with ❤️ by developers, for developers

Read Entire Article