Logo SmoothUI
ComponentsButton Copy

Button Copy

This component is an interactive button that visually changes state when clicked. The states are 'idle', 'loading', and 'success', represented by animated icons. When clicked, the button transitions from idle to loading and then to success, using smooth animations.

Code

Install with shadcn Beta

Terminal

npx shadcn@latest add "https://smoothui.dev/r/button-copy.json"

Manual install

Terminal

npm install motion lucide-react

ButtonCopy.tsx

"use client"

import { ReactNode, useCallback, useState } from "react"
import { Check, Copy, LoaderCircle } from "lucide-react"
import { AnimatePresence, motion } from "motion/react"

interface Button {
  idle: ReactNode
  loading: ReactNode
  success: ReactNode
}

const buttonCopy: Button = {
  idle: <Copy size={16} />,
  loading: <LoaderCircle size={16} className="animate-spin" />,
  success: <Check size={16} />,
}

export default function ButtonCopy() {
  const [buttonState, setButtonState] = useState<keyof Button>("idle")

  const handleClick = useCallback(() => {
    setButtonState("loading")
    setTimeout(() => {
      setButtonState("success")
    }, 1000)

    setTimeout(() => {
      setButtonState("idle")
    }, 3000)
  }, [])

  return (
    <div className="flex justify-center">
      <button
        className="border-light-200 bg-light-50 dark:border-dark-200 dark:bg-dark-50 relative w-auto cursor-pointer overflow-hidden rounded-full border p-3 disabled:opacity-50"
        disabled={buttonState !== "idle"}
        onClick={handleClick}
        aria-label={buttonState === "loading" ? "Copying..." : "Copy"}
      >
        <AnimatePresence mode="popLayout" initial={false}>
          <motion.span
            transition={{ type: "spring", duration: 0.3, bounce: 0 }}
            initial={{ opacity: 0, y: -25, filter: "blur(10px)" }}
            animate={{ opacity: 1, y: 0, filter: "blur(0px)" }}
            exit={{ opacity: 0, y: 25, filter: "blur(10px)" }}
            key={buttonState}
            className="flex w-full items-center justify-center"
          >
            {buttonCopy[buttonState]}
          </motion.span>
        </AnimatePresence>
      </button>
    </div>
  )
}