Overlays & Feedback

Click Spark

A feedback effect that bursts sparks / particles at the click point.

Wrap it around any content and a radial burst of sparks is drawn on canvas at the cursor position on click, as lightweight interaction feedback.

Bring this into your project

Run this command in your project folder. The CLI adds the source and dependencies; then follow the usage example below.

npx webberui@latest add click-spark.en

New project or installation trouble? Start here →

Click Spark is a React click effect built with canvas 2D that bursts sparks from the cursor, with a Tailwind CSS wrapper and Motion for reduced-motion detection. The animation loop only runs while sparks are on screen and stops once they finish.

Playground

Tune the props live — the code snippet updates as you go, so you can dial in the look you want before copying it.

10
15
8
400
Active edited usageDefault props
<ClickSpark/>

The complete example includes the preview layout and data. Install the component first, then paste this file into your project.

When to use

  • Feedback on like, save or add-to-cart buttons
  • Gamified interfaces, raffles or check-in pages where each click should feel tactile
  • Portfolios or personal sites, wrapping a whole page section as a global click effect
  • Not a good fit: button feedback that needs success/failure states; use Confetti Feedback or Stateful Button instead

Installation

npx webberui@latest add https://webberui.com/r/click-spark.en.json

Or, once registries are configured in components.json, install it as @webberui/click-spark.

Usage

import { ClickSpark } from "@/components/ui/click-spark";

<ClickSpark sparkColor="#f59e0b" sparkCount={10}>
  <button className="rounded-md bg-black px-4 py-2 text-white">Click me</button>
</ClickSpark>

The spark layer is stacked on top of the content with pointer-events-none and never intercepts clicks; the container itself listens for onClick, so any interactive element it wraps keeps working as usual.

Examples

Apply it to a whole section

Wrap the outermost element of a section and clicking anywhere inside produces sparks; links and buttons inside keep working.

<ClickSpark sparkColor="#6366f1" className="min-h-screen">
  <main className="mx-auto max-w-3xl px-6 py-16">
    <h1 className="text-3xl font-semibold">Portfolio</h1>
    {/* page content */}
  </main>
</ClickSpark>

A like button with a bigger burst

Raise sparkCount, sparkRadius and extraScale for a wider burst, and stretch duration to 600ms.

<ClickSpark
  sparkColor="#f43f5e"
  sparkCount={12}
  sparkSize={14}
  sparkRadius={28}
  extraScale={1.3}
  duration={600}
  easing="ease-in-out"
  lineWidth={3}
  className="inline-block p-6"
>
  <button type="button" className="rounded-full bg-rose-50 px-4 py-2 text-sm font-medium text-rose-600">
    Like 128
  </button>
</ClickSpark>

The p-6 on the wrapper gives the sparks room to fly out instead of being clipped at the container edge.

Follow the text colour

Without sparkColor, sparks use the container's text colour, so set one in className.

<ClickSpark className="text-emerald-600 dark:text-emerald-400">
  <button type="button" className="rounded-md border px-4 py-2">Check in</button>
</ClickSpark>

Props

PropTypeDefaultDescription
sparkColorstringcurrentColorSpark color; follows the text color when unspecified
sparkSizenumber10Length of each spark line segment (px)
sparkRadiusnumber15Maximum radius the sparks fly out to (px)
sparkCountnumber8Number of sparks per click burst
durationnumber400Duration of a single burst (milliseconds)
easing"linear" | "ease-in" | "ease-out" | "ease-in-out""ease-out"Easing curve of the radius advance
extraScalenumber1Extra scale factor applied in the tail of the burst
lineWidthnumber2Spark line segment width (px)

Accessibility

  • When the user has "reduce motion" enabled at the system level, clicks no longer burst sparks
  • The sparks are drawn on canvas and marked aria-hidden — purely visual feedback that does not affect what assistive technology announces
  • The component calls cancelAnimationFrame on unmount to clean up the animation loop

FAQ

Does it work with the Next.js App Router?

Yes. The file already starts with "use client", so import it from a Server Component page as usual.

Why are sparks cut off at the edges?

Sparks are drawn on a canvas the same size as the container, so anything outside it is not shown. Add padding to the container or wrap a larger area.

Why doesn't the spark colour change with the theme?

Without sparkColor, the component reads the container's text colour once on mount (and when sparkColor changes); switching themes does not trigger a re-read. If you need theme-aware sparks, pass sparkColor based on the current theme.

Does it keep a requestAnimationFrame loop running?

No. The loop only runs while sparks exist and stops when they have all finished; it is also cancelled with cancelAnimationFrame on unmount.

Does it respect reduced motion?

Yes. With "reduce motion" on, clicks produce no sparks, and the wrapped content stays clickable as usual.