Copy Button

A button component to copy content to the clipboard.

ReactClientComponents

Preview

Preparing...

Installation

shadcn/ui

shadcn/ui Command
pnpm dlx shadcn@latest add https://code-blocks.pheralb.dev/r/copy-button.json

Manual

  1. Create a utility function to handle clipboard copying:
.ts
export const copyToClipboard = async (text: string) => {
  try {
    await navigator.clipboard.writeText(text);
    return true;
  } catch (err) {
    console.error("Failed to copy text: ", err);
    return false;
  }
};
  1. Create a src/components/code-block/copy-button.tsx file and add the following code:
.tsx
"use client";

import { useEffect, useState, type ComponentProps } from "react";

import { cn } from "@/utils/cn";
import { copyToClipboard } from "@/utils/copy";
import { CheckIcon, CopyIcon } from "lucide-react";

interface CopyButtonProps extends ComponentProps<"button"> {
  content: string;
  iconSize?: number;
}

const CopyButton = ({ content, iconSize = 14, className, ...props }: CopyButtonProps) => {
  const [isCopied, setIsCopied] = useState<boolean>(false);

  useEffect(() => {
    if (!isCopied) return;

    const timeout = setTimeout(() => {
      setIsCopied(false);
    }, 2000);
    return () => clearTimeout(timeout);
  }, [isCopied]);

  const handleCopy = async () => {
    await copyToClipboard(content);
    setIsCopied(true);
  };

  return (
    <button
      title="Copy to clipboard"
      className={cn(
        "cursor-pointer",
        "transition-colors duration-200 ease-in-out",
        "text-neutral-600 dark:text-neutral-400",
        "hover:text-neutral-950 hover:dark:text-neutral-50",
        className,
      )}
      onClick={handleCopy}
      {...props}
    >
      {isCopied ? (
        <CheckIcon
          size={iconSize}
          className="animate-in zoom-in-50 text-green-900 duration-200 dark:text-green-400"
        />
      ) : (
        <CopyIcon
          size={iconSize}
          className="animate-in zoom-in-50 duration-200"
        />
      )}
    </button>
  );
};

export { CopyButton };

Usage

.tsx
import { CopyButton } from "@/components/code-block/copy-button";

const Example = () => {
  return <CopyButton content="Text to be copied" />;
};

export default Example;

Or with <CodeBlock> component:

.tsx
import {
  CodeBlock,
  CodeBlockHeader,
  CodeBlockContent,
} from "@/components/code-block/code-block";
import { CopyButton } from "@/components/code-block/copy-button";

const Example = () => {
  return (
    <CodeBlock>
      <CodeBlockHeader>
        <CopyButton content={`console.log('Hello, World!');`} />
      </CodeBlockHeader>
      <CodeBlockContent>
        {/* Your syntax highlighted code goes here. */}
      </CodeBlockContent>
    </CodeBlock>
  );
};
export default Example;

Props

React Props

PropTypeRequired
contentstringYes
iconSizenumberNo
Included: DOMAttributes<HTMLDivElement>