EmpireUI
Sign inGet Pro
← BlogEnglish · 6 minparticles backgroundreact animationanimated background

How to Add a Particles Background in React (Free & Lightweight)

Learn how to add a stunning particles background to any React app for free — no heavy dependencies, just clean code and smooth animation.

Why a Particles Background Elevates Your React UI

A particles background is one of the most versatile visual effects you can add to a React application. Whether you are building a SaaS landing page, a personal portfolio, or an admin dashboard, floating particles instantly communicate depth, motion, and technical sophistication — without overwhelming your content.

The trick is keeping it lightweight. Many developers reach for heavy canvas libraries that bloat their bundle and hammer the CPU on mobile devices. This guide shows you how to build or integrate a performant particles background using nothing more than the browser's native requestAnimationFrame API, plain CSS, and a single small React component — or, if you want a ready-made zero-config solution, the free components available on Empire UI.

Particles effects pair especially well with dark glassmorphism UIs. If you have not explored that aesthetic yet, the Empire UI style hub has 40 fully themed variants you can drop straight into your project.

Build a Particles Background From Scratch in React

The core idea is simple: maintain an array of particle objects in a useRef, draw them onto a <canvas> element each frame, and clean up the animation loop on unmount. Here is a minimal but production-ready implementation:

import { useEffect, useRef } from 'react';

interface Particle {
  x: number;
  y: number;
  vx: number;
  vy: number;
  radius: number;
  alpha: number;
}

export function ParticlesBackground() {
  const canvasRef = useRef<HTMLCanvasElement>(null);

  useEffect(() => {
    const canvas = canvasRef.current!;
    const ctx = canvas.getContext('2d')!;
    let animId: number;

    const resize = () => {
      canvas.width = window.innerWidth;
      canvas.height = window.innerHeight;
    };
    resize();
    window.addEventListener('resize', resize);

    const COUNT = 80;
    const particles: Particle[] = Array.from({ length: COUNT }, () => ({
      x: Math.random() * canvas.width,
      y: Math.random() * canvas.height,
      vx: (Math.random() - 0.5) * 0.6,
      vy: (Math.random() - 0.5) * 0.6,
      radius: Math.random() * 2 + 1,
      alpha: Math.random() * 0.6 + 0.2,
    }));

    const draw = () => {
      ctx.clearRect(0, 0, canvas.width, canvas.height);

      for (const p of particles) {
        p.x += p.vx;
        p.y += p.vy;
        if (p.x < 0 || p.x > canvas.width) p.vx *= -1;
        if (p.y < 0 || p.y > canvas.height) p.vy *= -1;

        ctx.beginPath();
        ctx.arc(p.x, p.y, p.radius, 0, Math.PI * 2);
        ctx.fillStyle = `rgba(255,255,255,${p.alpha})`;
        ctx.fill();
      }

      // Draw connecting lines between close particles
      for (let i = 0; i < particles.length; i++) {
        for (let j = i + 1; j < particles.length; j++) {
          const dx = particles[i].x - particles[j].x;
          const dy = particles[i].y - particles[j].y;
          const dist = Math.sqrt(dx * dx + dy * dy);
          if (dist < 120) {
            ctx.beginPath();
            ctx.moveTo(particles[i].x, particles[i].y);
            ctx.lineTo(particles[j].x, particles[j].y);
            ctx.strokeStyle = `rgba(255,255,255,${0.15 * (1 - dist / 120)})`;
            ctx.lineWidth = 0.5;
            ctx.stroke();
          }
        }
      }

      animId = requestAnimationFrame(draw);
    };

    draw();
    return () => {
      cancelAnimationFrame(animId);
      window.removeEventListener('resize', resize);
    };
  }, []);

  return (
    <canvas
      ref={canvasRef}
      className="fixed inset-0 -z-10 pointer-events-none"
    />
  );
}

This component covers all the essentials: responsive resizing, proper cleanup on unmount, velocity-based movement with boundary bouncing, and a subtle line-connection effect that makes the network feel alive. Drop <ParticlesBackground /> at the root of your layout and it will render behind all other content via fixed inset-0 -z-10.

For performance on mobile, reduce COUNT to 40 or lower. You can also skip the line-connection loop entirely for an even lighter result — the dots alone still look excellent over a dark or gradient background.

Using Empire UI's Ready-Made Particles Component

If you prefer a zero-configuration drop-in, Empire UI ships a production-ready <ParticlesBackground> component with theme tokens baked in. It supports all 40 visual styles — glassmorphism, neumorphism, neobrutalism, claymorphism, and more — and is fully typed for TypeScript projects.

Browse the live preview and copy the component source directly from the Empire UI component library. Every component is free, open-source, and designed to work with Tailwind CSS. You can also generate custom variants through the Empire UI MCP server, which lets AI coding assistants scaffold themed components on demand inside your IDE.

The MCP integration is particularly powerful for particles backgrounds because you can specify particle count, connection distance, colour palette, and blend mode in a single natural-language prompt, and the server returns a fully working TSX file styled to match your chosen Empire UI theme.

Customising Colours, Density, and Interactivity

A particles background becomes truly memorable when it responds to user input. Adding mouse-tracking repulsion takes only a few extra lines inside the draw loop. Store the cursor position in a mouseRef, then push each particle away from the cursor when it falls within a threshold radius — typically 100px works well.

For colour customisation, replace the hardcoded rgba(255,255,255,…) with CSS custom properties read at runtime: getComputedStyle(canvas).getPropertyValue('--particle-color'). This lets you swap the entire palette by toggling a Tailwind dark-mode class or a CSS variable on the root element, making the component respect your app's theme toggle without any prop drilling.

Density tuning is a balance between visual richness and CPU budget. A count of 60–100 particles with connection lines enabled is ideal for desktop hero sections. For scroll-triggered or section-level particles (not full-screen), dropping to 20–30 with no connection lines keeps the GPU load negligible even on low-end Android devices.

Pairing Particles With Other Empire UI Animated Backgrounds

Particles work best as a single background layer, but Empire UI offers several complementary effects you can layer on top as foreground accents. The aurora background effect, for example, uses a slow-moving radial gradient that sits naturally *behind* particles, giving depth without visual conflict.

For hero sections that demand maximum impact, try combining a particles background with a spotlight effect tied to the cursor. The spotlight creates a cone of light that reveals particles in its path while leaving the rest in soft darkness — the result is cinematic and requires very little code beyond what we have already covered in this article.

Check the Empire UI cursor collection for custom cursor designs that complement particle-heavy UIs. A minimal dot cursor or a trailing-light cursor reinforces the particle aesthetic end to end, from background to pointer.

Performance Best Practices and Accessibility

Canvas animations bypass React's rendering cycle, which is a feature, not a bug — but it means you must handle cleanup manually. Always cancel requestAnimationFrame in the useEffect cleanup function and remove resize event listeners. Forgetting this is the single most common source of memory leaks in particle components.

Accessibility is often overlooked for decorative animations. Add aria-hidden="true" to the canvas element so screen readers ignore it entirely. You should also respect the prefers-reduced-motion media query: read it once on mount and set velocity to 0 or skip the animation loop entirely if the user has requested reduced motion. Empire UI components handle this automatically.

Finally, avoid triggering layout or style recalculations inside the animation loop. Read canvas.width and canvas.height — which are plain properties, not layout-triggering getters — rather than getBoundingClientRect(). This keeps each frame budget well under the 16 ms target required for a smooth 60 fps particles background on all devices.

FAQ

What is the lightest way to add a particles background in React?

The lightest approach is a custom <canvas> component that uses requestAnimationFrame directly — no third-party library required. A single 80-line TSX file is all you need for a smooth, responsive particles background with connecting lines.

Does a particles background hurt Core Web Vitals?

Not if implemented correctly. Because the canvas element is positioned fixed and painted outside React's render tree, it does not affect Largest Contentful Paint or Cumulative Layout Shift. Keep your particle count under 100 and avoid forced reflows inside the draw loop to maintain a stable 60 fps.

How do I make the particles background respect dark mode?

Read a CSS custom property (e.g. --particle-color) inside the draw function using getComputedStyle. Update the variable in your dark-mode CSS class and the canvas will pick up the new colour on the next frame automatically, with no React re-renders needed.

Can I use a particles background with Next.js App Router?

Yes. Mark the component with 'use client' at the top of the file since it relies on useEffect and browser APIs. The canvas renders only on the client and does not interfere with server-side rendering or static generation.

Is the Empire UI particles component free to use in commercial projects?

Yes — Empire UI is fully open-source and free for both personal and commercial use. You can copy, modify, and ship any component without attribution requirements, though a link back to empire-ui.com is always appreciated.

Free components in 40 styles
React & Tailwind, copy-paste ready.
Browse →

Read next

How to Create an Aurora Background in React (Free Tailwind)How to Add a Shooting Stars Background in React (Free)10 Best Free Animated Background Effects for React in 2026How to Add a Spotlight Effect in React + Tailwind (Free)