Text with four-point stars that twinkle around it in theme colors, re-rolling position each time, with an optional gradient fill.
Right-to-left: Sparkles are placed by random percentages from the left edge and centred with a negative margin; decoration with no reading direction.
Meet the new Pro plan
import { SparklesText } from "@/components/ballmac/sparkles-text"
export default function SparklesTextDemo() {
return (
<h2 className="text-center text-4xl font-bold tracking-tight text-foreground sm:text-5xl">
Meet the new <SparklesText count={10}>Pro plan</SparklesText>
</h2>
)
}Installation
$ pnpm dlx shadcn@latest add @ballmac/sparkles-textInstall the dependencies.
$ pnpm add motion@^12Copy the source into your project.
components/ballmac/sparkles-text.tsx// Ballmac UI: Sparkles Text. https://ui.ballmac.com/components/sparkles-text "use client" import * as React from "react" import { motion, useReducedMotion } from "motion/react" import { cn } from "@/lib/utils" const TONES = ["var(--chart-1)", "var(--chart-2)", "var(--chart-3)", "var(--chart-4)", "var(--chart-5)"] type Spark = { id: number; x: number; y: number; size: number; tone: string; duration: number; delay: number } function makeSpark(id: number): Spark { return { id, x: Math.random() * 110 - 5, y: Math.random() * 120 - 10, size: 12 + Math.random() * 14, tone: TONES[Math.floor(Math.random() * TONES.length)]!, duration: 1.4 + Math.random() * 1.2, delay: Math.random() * 1.2, } } function Star({ spark, onDone }: { spark: Spark; onDone: (id: number) => void }) { return ( <motion.svg aria-hidden="true" viewBox="0 0 24 24" className="pointer-events-none absolute" style={{ left: `${spark.x}%`, top: `${spark.y}%`, width: spark.size, height: spark.size, marginLeft: -spark.size / 2, marginTop: -spark.size / 2, color: spark.tone }} initial={{ scale: 0, rotate: 0, opacity: 0 }} animate={{ scale: [0, 1, 0], rotate: [0, 90, 180], opacity: [0, 1, 0] }} transition={{ duration: spark.duration, delay: spark.delay, ease: "easeInOut" }} onAnimationComplete={() => onDone(spark.id)} > <path d="M12 0c.6 6.4 5 11.4 12 12-7 .6-11.4 5.6-12 12-.6-6.4-5-11.4-12-12 7-.6 11.4-5.6 12-12Z" fill="currentColor" /> </motion.svg> ) } type SparklesTextProps = Omit<React.ComponentProps<"span">, "children"> & { /** The text to decorate. */ children: React.ReactNode /** How many sparkles twinkle at once. */ count?: number /** Show a soft gradient fill on the text itself. */ gradient?: boolean } function SparklesText({ children, count = 8, gradient = false, className, ...props }: SparklesTextProps) { const reduce = useReducedMotion() const [sparks, setSparks] = React.useState<Spark[]>([]) const next = React.useRef(0) // Random positions can only be picked after mount, or the server and browser would disagree. React.useEffect(() => { if (reduce) return setSparks([]) setSparks(Array.from({ length: count }, () => makeSpark(next.current++))) }, [count, reduce]) const replace = React.useCallback((id: number) => { setSparks((all) => all.map((s) => (s.id === id ? makeSpark(next.current++) : s))) }, []) return ( <span data-slot="sparkles-text" className={cn("relative inline-block", className)} {...props}> <span className={cn(gradient && "bg-[linear-gradient(90deg,var(--foreground),color-mix(in_oklab,var(--chart-4)_60%,var(--foreground)),var(--foreground))] bg-clip-text text-transparent")} > {children} </span> {sparks.map((s) => ( <Star key={s.id} spark={s} onDone={replace} /> ))} </span> ) } export { SparklesText, type SparklesTextProps }Update the import paths to match your project setup.
Usage
import { SparklesText } from "@/components/ballmac/sparkles-text"The full example is in the Code tab above.
Examples
Gradient fill
You unlocked 3 new badges this week.
import { SparklesText } from "@/components/ballmac/sparkles-text"
export default function SparklesTextGradient() {
return (
<div className="grid justify-items-center gap-3">
<SparklesText gradient count={14} className="text-5xl font-extrabold tracking-tight">
Level up
</SparklesText>
<p className="text-sm text-muted-foreground">You unlocked 3 new badges this week.</p>
</div>
)
}API reference
| Prop | Type | Default |
|---|---|---|
children*The text to decorate. | React.ReactNode | — |
countHow many sparkles twinkle at once. | number | 8 |
gradientShow a soft gradient fill on the text itself. | boolean | false |
Also accepts the standard attributes of its root element.
Accessibility
| Key | Action |
|---|---|
| Screen readers | Sparkles are aria-hidden and pointer-events-none; the text is untouched |
| Reduced motion | No sparkles are drawn |
Use with AI
<SparklesText count gradient>Text</SparklesText>. Sparkles are decorative and positioned after mount, so the server markup stays identical. With the shadcn MCP server set up (guide), ask your agent:
Add the Ballmac UI Sparkles Text (@ballmac/sparkles-text) to this project with the shadcn MCP, then use it where it fits.
Use it for
- One standout word in a marketing headline
- Celebratory states such as a new plan or achievement
Not for
- Dense UI
- More than one or two per screen
Registry JSON: https://ui.ballmac.com/r/sparkles-text.json
Credits
Free to use in personal and commercial projects.
- npm
- motion@^12
- Registry
- shadcn/utils
Pairs well with
Gradient Text
Text filled with a gradient of theme colors that slowly drifts, or a shiny variant that sweeps a glint of light across it on a pause. Static under reduced motion; plain text in forced colors.
Confetti
Confetti in your theme colors: fireConfetti(), a useConfetti() hook that fires from an element, and a ConfettiButton. Presets for burst, side cannons, stars and fireworks; nothing fires under reduced motion.
Shimmer Text
Text with a band of light sweeping across it, from muted to full foreground color. Suited to AI "thinking" and loading status lines; static under reduced motion.
Highlighter
Hand-drawn marks around inline text: highlight, underline, box, circle, strike-through and brackets, drawn in when the phrase scrolls into view.