registry:component
Ripple Button
v1.0.0ga
Button primitive with ripple click interaction.
Registry Endpoint
Open JSONhttps://registry.aavya.com/r/ui-ripple-button.json
npx shadcn@latest add https://registry.aavya.com/r/ui-ripple-button.json
Live Preview
Usage Snippet
import RippleButton from '@/components/ui/ripple-button.tsx'
export default function Example() {
return (
<div className="p-6">
<RippleButton />
</div>
)
}
Source Preview
'use client'
import * as React from 'react'
import { motion, type HTMLMotionProps, type Transition } from 'motion/react'
import type { VariantProps } from 'class-variance-authority'
import { cn } from '@/lib/utils'
import { buttonVariants } from '@/components/ui/button'
interface Ripple {
id: number
x: number
y: number
}
interface RippleButtonProps extends HTMLMotionProps<'button'>, VariantProps<typeof buttonVariants> {
children: React.ReactNode
scale?: number
transition?: Transition
}
function RippleButton({
ref,
children,
onClick,
className,
variant,
size,
scale = 10,
transition = { duration: 0.6, ease: 'easeOut' },
...props
}: RippleButtonProps) {
const [ripples, setRipples] = React.useState<Ripple[]>([])
const buttonRef = React.useRef<HTMLButtonElement>(null)
React.useImperativeHandle(ref, () => buttonRef.current as HTMLButtonElement)
const createRipple = React.useCallback((event: React.MouseEvent<HTMLButtonElement>) => {
const button = buttonRef.current
if (!button) return
const rect = button.getBoundingClientRect()
const x = event.clientX - rect.left
const y = event.clientY - rect.top
const newRipple: Ripple = {
id: Date.now(),
x,
y
}
setRipples(prev => [...prev, newRipple])
setTimeout(() => {
setRipples(prev => prev.filter(r => r.id !== newRipple.id))
}, 600