Copy Code Button

Terminal-style snippet with an animated copy confirmation.

$ npm i motion lucide-react
$ npx create-next-app@latest my-app

Installation

  1. Install the dependencies

    $ npm i motion lucide-react
  2. Copy the source into components/ui/copy-code-button.tsx

    components/ui/copy-code-button.tsx
    "use client";
    
    import { AnimatePresence, motion } from "motion/react";
    import { Check, Copy } from "lucide-react";
    import { useState } from "react";
    
    export function CopyCodeButton({ text, label = "Copy" }: { text: string; label?: string }) {
      const [copied, setCopied] = useState(false);
    
      async function copy() {
        try {
          await navigator.clipboard.writeText(text);
          setCopied(true);
          setTimeout(() => setCopied(false), 1600);
        } catch {
          /* Clipboard blocked (e.g. insecure context) — leave state unchanged. */
        }
      }
    
      return (
        <button onClick={copy} aria-label={copied ? "Copied" : `${label} to clipboard`} className="relative inline-flex h-8 items-center gap-1.5 overflow-hidden rounded-lg border border-zinc-700 bg-zinc-800 px-2.5 text-xs font-medium text-zinc-200 transition-colors hover:bg-zinc-700">
          <AnimatePresence mode="wait" initial={false}>
            <motion.span key={copied ? "y" : "n"} initial={{ y: 12, opacity: 0 }} animate={{ y: 0, opacity: 1 }} exit={{ y: -12, opacity: 0 }} transition={{ duration: 0.15 }} className="flex items-center gap-1.5">
              {copied ? <Check className="size-3.5 text-emerald-400" /> : <Copy className="size-3.5" />}
              {copied ? "Copied!" : label}
            </motion.span>
          </AnimatePresence>
        </button>
      );
    }
    
    export function CodeSnippet({ code }: { code: string }) {
      return (
        <div className="flex items-center justify-between gap-4 rounded-xl bg-zinc-900 py-2 pl-4 pr-2 font-mono text-sm text-zinc-100">
          <code className="truncate">
            <span className="select-none text-zinc-500">$ </span>
            {code}
          </code>
          <CopyCodeButton text={code} />
        </div>
      );
    }
    
    export default function Demo() {
      return (
        <div className="w-full max-w-md space-y-3">
          <CodeSnippet code="npm i motion lucide-react" />
          <CodeSnippet code="npx create-next-app@latest my-app" />
        </div>
      );
    }
    
  3. Use it

    The file exports the component and a Demo showing typical usage. Import whichever you need.