title: Sparkles
description: A configurable sparkles component that can be used as a background or as a standalone component.
---

```vue
<template>
  <div
    class="flex h-[40rem] w-full flex-col items-center justify-center overflow-hidden rounded-md bg-white dark:bg-black"
  >
    <h1
      class="relative z-20 text-center text-3xl font-bold text-black lg:text-9xl md:text-7xl dark:text-white"
    >
      Inspira UI
    </h1>
    <div class="relative h-40 w-[40rem]">
      <div
        class="absolute inset-x-20 top-0 h-[2px] w-3/4 bg-gradient-to-r from-transparent via-indigo-500 to-transparent blur-sm"
      />
      <div
        class="absolute inset-x-20 top-0 h-px w-3/4 bg-gradient-to-r from-transparent via-indigo-500 to-transparent"
      />
      <div
        class="absolute inset-x-60 top-0 h-[5px] w-1/4 bg-gradient-to-r from-transparent via-sky-500 to-transparent blur-sm"
      />
      <div
        class="absolute inset-x-60 top-0 h-px w-1/4 bg-gradient-to-r from-transparent via-sky-500 to-transparent"
      />

      <Sparkles
        background="transparent"
        :min-size="0.4"
        :max-size="1.4"
        :particle-density="1200"
        class="size-full"
        :particle-color="particlesColor"
      />

      <div
        class="absolute inset-0 size-full bg-white [mask-image:radial-gradient(350px_200px_at_top,transparent_20%,white)] dark:bg-black"
      ></div>
    </div>
  </div>
</template>
<script setup lang="ts">
import { computed } from "vue";
import { useColorMode } from "@vueuse/core";

const particlesColor = computed(() => (useColorMode().value === "dark" ? "#FFFFFF" : "#000000"));
</script>

```

## Install using CLI

```vue
<InstallationCli component-id="sparkles" />
```

## Install Manually

Copy and paste the following code

```vue
<template>
  <div
    ref="containerRef"
    class="relative size-full overflow-hidden will-change-transform"
    :style="{ background }"
  >
    <canvas
      ref="canvasRef"
      class="absolute inset-0 size-full"
    />
  </div>
</template>

<script setup lang="ts">
import { useRafFn, templateRef } from "@vueuse/core";
import { ref, onMounted, onBeforeUnmount } from "vue";

interface Props {
  background?: string;
  particleColor?: string;
  minSize?: number;
  maxSize?: number;
  speed?: number;
  particleDensity?: number;
}

interface Particle {
  x: number;
  y: number;
  size: number;
  opacity: number;
  vx: number;
  vy: number;
  phase: number;
  phaseSpeed: number;
}

const props = withDefaults(defineProps<Props>(), {
  background: "#0d47a1",
  particleColor: "#ffffff",
  minSize: 1,
  maxSize: 3,
  speed: 4,
  particleDensity: 120,
});

const containerRef = templateRef<HTMLElement | null>("containerRef");
const canvasRef = templateRef<HTMLCanvasElement | null>("canvasRef");
const particles = ref<Particle[]>([]);
const ctx = ref<CanvasRenderingContext2D | null>(null);

// Adjust canvas size on mount and resize
function resizeCanvas() {
  if (!canvasRef.value || !containerRef.value) return;

  const dpr = window.devicePixelRatio || 1;
  const rect = containerRef.value.getBoundingClientRect();

  canvasRef.value.width = rect.width * dpr;
  canvasRef.value.height = rect.height * dpr;

  if (ctx.value) {
    ctx.value.scale(dpr, dpr);
  }
}

function generateParticles(): void {
  const newParticles: Particle[] = [];
  const count = props.particleDensity;

  for (let i = 0; i < count; i++) {
    const baseSpeed = 0.05;
    const speedVariance = Math.random() * 0.3 + 0.7;

    newParticles.push({
      x: Math.random() * 100,
      y: Math.random() * 100,
      size: Math.random() * (props.maxSize - props.minSize) + props.minSize,
      opacity: Math.random() * 0.5 + 0.3,
      vx: (Math.random() - 0.5) * baseSpeed * speedVariance * props.speed,
      vy: ((Math.random() - 0.5) * baseSpeed - baseSpeed * 0.3) * speedVariance * props.speed,
      phase: Math.random() * Math.PI * 2,
      phaseSpeed: 0.015,
    });
  }

  particles.value = newParticles;
}

function updateAndDrawParticles() {
  if (!ctx.value || !canvasRef.value) return;

  const canvas = canvasRef.value;
  ctx.value.clearRect(0, 0, canvas.width, canvas.height);

  particles.value = particles.value.map((particle) => {
    let newX = particle.x + particle.vx;
    let newY = particle.y + particle.vy;

    if (newX < -2) newX = 102;
    if (newX > 102) newX = -2;
    if (newY < -2) newY = 102;
    if (newY > 102) newY = -2;

    const newPhase = (particle.phase + particle.phaseSpeed) % (Math.PI * 2);
    const opacity = 0.3 + (Math.sin(newPhase) * 0.3 + 0.3);

    // Draw particle
    ctx.value!.beginPath();
    ctx.value!.arc(
      (newX * canvas.width) / 100,
      (newY * canvas.height) / 100,
      particle.size,
      0,
      Math.PI * 2,
    );
    ctx.value!.fillStyle = `${props.particleColor}${Math.floor(opacity * 255)
      .toString(16)
      .padStart(2, "0")}`;
    ctx.value!.fill();

    return {
      ...particle,
      x: newX,
      y: newY,
      phase: newPhase,
      opacity,
    };
  });
}

const { pause, resume } = useRafFn(updateAndDrawParticles, { immediate: false });

// Handle window resize
let resizeObserver: ResizeObserver | undefined;

onMounted(() => {
  if (!canvasRef.value) return;

  ctx.value = canvasRef.value.getContext("2d");
  resizeCanvas();
  generateParticles();

  // Set up resize observer
  resizeObserver = new ResizeObserver(resizeCanvas);
  if (containerRef.value) {
    resizeObserver.observe(containerRef.value);
  }

  resume();
});

onBeforeUnmount(() => {
  pause();
  if (resizeObserver && containerRef.value) {
    resizeObserver.unobserve(containerRef.value);
  }
});
</script>

```

## Examples

Sparkles Full Page

```vue
<template>
  <div
    class="relative flex h-[40rem] w-full flex-col items-center justify-center overflow-hidden rounded-md bg-white dark:bg-black"
  >
    <div class="absolute inset-0 h-screen w-full">
      <Sparkles
        background="transparent"
        :min-size="0.8"
        :max-size="2"
        :particle-density="800"
        class="size-full"
        :particle-color="particlesColor"
      />
    </div>
    <h1
      class="relative z-20 text-center text-3xl font-bold text-black lg:text-6xl md:text-7xl dark:text-white"
    >
      Build great products
    </h1>
  </div>
</template>
<script setup lang="ts">
import { computed } from "vue";
import { useColorMode } from "@vueuse/core";

const particlesColor = computed(() => (useColorMode().value === "dark" ? "#FFFFFF" : "#000000"));
</script>

```

## API

| Prop Name         | Type     | Default     | Description                                                                            |
| ----------------- | -------- | ----------- | -------------------------------------------------------------------------------------- |
| `background`      | `string` | `'#0d47a1'` | Background color of the container. Use 'transparent' to see through to parent element. |
| `particleColor`   | `string` | `'#ffffff'` | Color of the particles. Accepts any valid CSS color value.                             |
| `minSize`         | `number` | `1`         | Minimum size of particles in pixels.                                                   |
| `maxSize`         | `number` | `3`         | Maximum size of particles in pixels.                                                   |
| `speed`           | `number` | `4`         | Movement speed multiplier. Higher values create faster movement.                       |
| `particleDensity` | `number` | `120`       | Number of particles to render. Higher values create denser particle fields.            |

## Credits

- Credits to [M Atif](https://github.com/atif0075) for porting this component.

- Ported from [Aceternity UI's Sparkles](https://ui.aceternity.com/components/sparkles).