SVG Particle Effect for React

Three.js particle field from SVG source. Pass icon components from lucide-react, react-icons, Heroicons, Tabler, Phosphor, or any <svg /> - works out of the box. Particles react to the cursor and spring home.

Component

Installation

 

Usage

Import

Import the component into your page or section.

import { SvgParticle } from "@/components/svg-particle";

Choose an icon

Import from your usual SVG icon package - no extra setup; pass the component as svg={...}.

import { Rocket } from "lucide-react";
// or: FaGithub from "react-icons/fa", Heroicons, @tabler/icons-react, etc.

Render

Pass the icon element directly as the svg prop.

<SvgParticle svg={<Rocket size={240} fill="white" />} />;

Guidelines

  • Icon libraries work out of the box: pass the same JSX you would render elsewhere (lucide-react, react-icons, @heroicons/react, @tabler/icons-react, phosphor-react, radix icons, etc.). No special wrappers or SVG string extraction required.
  • The svg prop accepts a React element (<Rocket />) or a raw SVG string. Elements are serialized with renderToStaticMarkup before raster sampling.
  • currentColor in the serialized markup is replaced with white so library defaults still produce alpha for particle placement.
  • For stroke-only icons, pass fill="white" (or explicit stroke/fill colors) when you want a filled silhouette instead of a thin outline.
  • The SVG is never shown as a visible layer; it is only used as geometry for particle home positions.
  • Use particleGap and maxParticles to balance density and performance; tune repel and return props for interaction feel.

Props

All props are optional unless marked required. Use these to customize every aspect of the component.

PropTypeDefaultDescription
svgrequiredReactElement | stringSVG source for particles: a React element from any SVG icon library (lucide-react, react-icons, Heroicons, Tabler, Phosphor, custom <svg />) or a raw SVG string.
widthnumber400Canvas width in pixels.
heightnumber400Canvas height in pixels.
colorstring"#d4c6ff"Particle glow color.
particleGapnumber4Sampling gap in pixels when extracting points from the SVG.
particleSizenumber3.4Rendered particle size.
maxParticlesnumber4200Upper bound for particle count after SVG sampling.
repelRadiusnumber52Radius around cursor where particles are displaced.
repelStrengthnumber0.28Strength of cursor displacement force.
returnStrengthnumber0.04Spring strength pulling particles back to home positions.
dampingnumber0.9Velocity damping applied each frame.
classNamestringAdditional classes for the outer wrapper.

Accessibility

  • Decorative WebGL visual effect with no interactive controls, buttons, links, or focusable elements, so it is invisible to keyboard and tab navigation.
  • Interaction is pointer-only via onPointerMove and onPointerLeave with no keyboard or touch-focus equivalent, so cursor repulsion is unavailable to keyboard users.
  • No ARIA roles, labels, or alt text are set on the canvas wrapper, so add an aria-hidden attribute or a descriptive label depending on whether the effect is purely ornamental or conveys meaning.
  • The component does not check prefers-reduced-motion, so the particle simulation should be guarded with a matchMedia('(prefers-reduced-motion: reduce)') query to pause or render a static frame for users who request reduced motion.
  • Because the silhouette comes from an SVG sampled only as particle geometry, any meaning carried by the source icon is not exposed to assistive technology and should be conveyed in adjacent text.

Performance

  • Motion is expressed by mutating a Three.js BufferGeometry position attribute and uploading it each frame rather than animating layout properties like top, left, width, or height, so it never triggers browser layout or paint.
  • Rendering uses a single GPU points draw call with a custom shader, additive blending, and depthWrite disabled, keeping it on the compositor, while dpr is capped at [1, 2] and powerPreference is set to high-performance.
  • The per-frame physics loop in useFrame iterates every particle on the CPU in JavaScript, so cost scales with particle count and should be bounded with particleGap and maxParticles to avoid main-thread jank.
  • Animation runs on the requestAnimationFrame loop provided by react-three-fiber with no IntersectionObserver or visibility gating, so it keeps simulating even when off-screen and could be paused when not visible.
  • Cleanup is handled by disposing the geometry and material on unmount and using an isCancelled flag for the async SVG sampling effect, preventing leaked WebGL resources and stale state updates.

Examples

Lucide - Rocket

Lucide Rocket icon passed as a React element with default particle settings.

import { Rocket } from "lucide-react";
import { SvgParticle } from "@/components/svg-particle";

<SvgParticle svg={<Rocket size={240} fill="white" />} />;

react-icons - React logo

React logo from react-icons with denser particles and a stronger displacement feel.

import { FaReact } from "react-icons/fa";
import { SvgParticle } from "@/components/svg-particle";

<SvgParticle
  svg={<FaReact size={240} />}
  color="#93c5fd"
  particleGap={3}
  particleSize={3.8}
  repelRadius={60}
  repelStrength={0.35}
/>;

Lucide - Heart

Lucide Heart with warm color and snappy return spring for a bouncy feel.

import { Heart } from "lucide-react";
import { SvgParticle } from "@/components/svg-particle";

<SvgParticle
  svg={<Heart size={240} fill="white" />}
  color="#fca5a5"
  maxParticles={2500}
  returnStrength={0.055}
  damping={0.88}
/>;

react-icons - GitHub

GitHub Octocat from react-icons with fine-grained green particles.

import { FaGithub } from "react-icons/fa";
import { SvgParticle } from "@/components/svg-particle";

<SvgParticle
  svg={<FaGithub size={240} />}
  color="#86efac"
  particleGap={3}
  particleSize={2.8}
  repelRadius={45}
/>;

Last updated on Jun 19

Made with ❤️ by Pulkit &

© 2026 Pulkit. All rights reserved

Last updated: