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 · mediumiconslucide-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;