Copy button

Copy a value with immediate confirmation.

actionutility
Preview
Copy button preview

When to use

  • Next to API keys, install commands, links, or code snippets.
  • Icon-only copy controls in dense rows, with an accessible label.
  • Any copy action that should confirm Copied or Could not copy in place.

When not to use

  • Use split-button when copying has alternatives, such as Copy link and Copy as Markdown.
  • Use code-block for full code samples, which include their own copy control.

shadcn CLI

Also installs (1)

  • manicat-foundation
Manual install(+)
registry.jsonjson
{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "copy-button",
  "type": "registry:ui",
  "title": "Copy button",
  "description": "Copy a value with immediate confirmation.",
  "categories": [
    "actions"
  ],
  "dependencies": [
    "lucide-react",
    "motion"
  ],
  "registryDependencies": [
    "https://manicat.viraza.net/r/manicat-foundation.json"
  ],
  "files": [
    {
      "path": "registry/components/copy-button/copy-button.tsx",
      "type": "registry:component",
      "target": "@components/manicat/copy-button/copy-button.tsx"
    },
    {
      "path": "registry/components/copy-button/copy-button.module.css",
      "type": "registry:component",
      "target": "@components/manicat/copy-button/copy-button.module.css"
    },
    {
      "path": "lib/use-copy-feedback.ts",
      "type": "registry:lib",
      "target": "@components/manicat/lib/use-copy-feedback.ts"
    }
  ],
  "docs": "Docs and live preview: https://uiarc.dev/components/copy-button",
  "meta": {
    "tier": "free",
    "kind": "component",
    "docs": "https://manicat.viraza.net/components/copy-button",
    "markdown": "https://uiarc.dev/components/copy-button/markdown",
    "tags": [
      "action",
      "utility"
    ]
  }
}

Usage

Install the component, then copy this into your app. Change the content and the props to fit your screen.

example.tsxtsx
"use client";

import { CopyButton } from "@/components/manicat/copy-button/copy-button";

export function CopyButtonExample() {
  return <CopyButton value="npx shadcn@latest add https://manicat.dev/r/button.json" label="Copy install command" />;
}

Examples

Real screens rather than isolated props. Switch to Code to copy a whole example into your project.

Copy install command A copy button next to the command it copies.

Copy install command A copy button next to the command it copies. preview
npx shadcn@latest add https://manicat.dev/r/button.json

Keyboard interactions

KeysAction
Enter / SpaceCopies the value.

Accessibility

  • Native button named by label, including when iconOnly.
  • A polite role="status" region announces "<label>: Copied" or "<label>: Could not copy".
  • The label cell reserves the widest of its three states, so the layout never shifts.

Motion

  • Icons trade places through a blur on a slow, nearly critically damped spring, and the check draws its stroke in.
  • Only the changed letters of the label move; the same motion plays in reverse on reset.
  • Reduced motion swaps icon and text with an instant fade and skips the stroke draw.

Responsive behavior

  • The label reserves the width of its widest state, so the button never shifts its neighbours when it changes.
  • It sizes to max-content with max-width 100%, so it stays compact in narrow rows.

Performance

  • Only changed letters animate; the icon swap and stroke draw are a single short spring per copy.
  • Clipboard access is async and needs a secure context; failures show the Failed state rather than throwing.

Notes for AI

  • Use next to any copyable value: API keys, install commands, links, code. For a copy action that also has alternatives use split-button.
  • Named export only; there is no default export.
  • Clipboard state and reset timing come from lib/use-copy-feedback, so copy that file along with the component.