shadimage
flowui
componentcopy-buttonsource: ok · hit

Copy Button

A copy button component with two types: normal and animated.

npx shadcn add "https://flowui-registry.vercel.app/r/copy-button.json"
free

No selection tags yet — empty means no evidence, not lack of support.

Dependencies

Bundle weight · medium
iconslucide-react
animationmotion

Registry dependencies

button

Files3 · source cached 10/14/2026, 2:30:02 PM

registry/flowui/components/copy-button/copy-button.tsx
"use client";

import { Button } from "@/components/ui/button";
import { cn } from "@/lib/utils";
import { Check, Copy } from "lucide-react";
import { useState } from "react";
import AnimationWrapper from "./animation-wrapper";

type AnimationWrapperProps = {
    children: React.ReactNode;
    mode: "copy" | "copied";
};

export type CopyButtonProps = {
    animated?: boolean;
    time?: number;
    iconType?: "default" | "rotated";
} & React.ComponentProps<typeof Button>;


export const CopyButton = ({
    animated = false,
    className,
    children,
    time = 3000,
    iconType = "default",
    ...props
}: CopyButtonProps) => {
    // Fixed Logic for any behavior
    const [mode, setMode] = useState<"copy" | "copied">("copy");

    const handleCopy = (e: React.MouseEvent<HTMLButtonElement>) => {
        if (props.onClick) {
            props.onClick(e);
        }

        // Change state to copied
        setMode("copied");

        // Change to the base state after user timer - default 3s
        const timer = setTimeout(() => {
            setMode("copy");
        }, time);

        // Clear the timer
        return () => clearTimeout(timer);
    }

    // Identify the icon to be used and create it's node

    const Icon = mode === "copy" ? Copy : Check;

    const iconNode = (
        <Icon
            className={cn(
                "",
                iconType === "rotated" && mode === "copy" && "rotate-90"
            )}
        />
    );

    const content = animated ? (
        <AnimationWrapper mode={mode}>
            {iconNode}
        </AnimationWrapper>
    ) : (
        iconNode
    )

    return (
        <Button
            {...props}
            onClick={handleCopy}
            className={cn(
                "relative size-9",
                className
            )}
        >
            {content}
        </Button>
    )
}
registry/flowui/components/copy-button/animated-copy-button.tsx
import { CopyButton, CopyButtonProps } from "./copy-button"

export const AnimatedCopyButton = (props: CopyButtonProps) => {
    return (
        <CopyButton
            {...props}
            animated={true}
        />
    )
}
registry/flowui/components/copy-button/animation-wrapper.tsx
import { motion, AnimatePresence } from "motion/react";

type AnimatedLogicProps = {
    children: React.ReactNode;
    mode: "copy" | "copied";
};

const AnimationWrapper = ({ children, mode }: AnimatedLogicProps) => {
    return (
        <AnimatePresence mode="wait">
            <motion.span
                key={mode}
                className="absolute"
                initial={{ scale: 0.6, opacity: 0 }}
                animate={{ scale: 1, opacity: 1 }}
                exit={{ scale: 0.6, opacity: 0 }}
            >
                {children}
            </motion.span>
        </AnimatePresence>
    )
}

export default AnimationWrapper;

More from flowui

Browse all components in this library →