Tips & Tricks (अपडेट: 2/6/2026)

Claude Code से Clipboard API: copy button, permissions, fallback और tests

Claude Code से Clipboard API लागू करें: copy, paste, permissions, fallback, React और Playwright tests.

Claude Code से Clipboard API: copy button, permissions, fallback और tests

Clipboard API पहली नज़र में बहुत छोटी feature लगती है। एक button navigator.clipboard.writeText() call करता है और text copy हो जाता है। लेकिन production में यही code HTTP preview, iframe, delayed fetch, browser permissions, mobile Safari, copy failure feedback, pasted personal data और E2E tests के कारण टूट सकता है।

इस guide में हम Claude Code की मदद से React और TypeScript में copy और paste UX बनाएंगे जिसे सीधे project में रखा जा सके। Clipboard API का मतलब है browser की वह Web API जो operating system clipboard को read/write करती है। Async Clipboard Promise-based version है। Secure context यानी ऐसा environment जिसे browser सुरक्षित मानता है, जैसे HTTPS या localhost। Fallback यानी modern API न मिलने पर चलने वाला backup रास्ता।

साथ में पढ़ें: Claude Code accessibility, Claude Code Playwright testing और Claude Code form validation। Official references: MDN Clipboard API, MDN writeText, W3C Clipboard API and events, Playwright BrowserContext, WebKit Async Clipboard API और Claude Code docs

पहले acceptance criteria लिखें

Claude Code से सिर्फ “copy button बना दो” मत कहें। Browser edge cases और security rules पहले prompt में डालें।

Goal: Implement Clipboard API copy and paste UX in React.
Scope: edit only src/lib/clipboard.ts, src/components/CopyButton.tsx, and matching tests.
Requirements:
- Use navigator.clipboard.writeText in secure contexts.
- Keep the write call inside a user click handler.
- Provide a textarea fallback for unsupported or HTTP pages.
- Never read clipboard on page load.
- Show accessible copied/error feedback.
- Add Playwright tests for copy success and paste normalization.
Do not stage, commit, or edit unrelated files.

High-level flow यह है।

flowchart TD
  A["User copy पर click करता है"] --> B{"Async Clipboard available?"}
  B -->|yes| C["writeText"]
  B -->|no| D["textarea + execCommand fallback"]
  C --> E{"Success?"}
  D --> E
  E -->|yes| F["aria-live से copied announce करें"]
  E -->|no| G["Manual copy guidance दिखाएं"]
  H["User explicitly paste करता है"] --> I["readText या onPaste"]
  I --> J["Normalize, limit, validate"]

सबसे ज़रूरी privacy rule है: page load पर clipboard न पढ़ें। Clipboard में password, address, internal URL, customer data या private source code हो सकता है। Read action सिर्फ visible paste button या normal onPaste event से होना चाहिए।

Copy logic को utility में रखें

पहले React से अलग copyText function बनाएं। इससे fallback, error और tests एक जगह रहते हैं।

// src/lib/clipboard.ts
export type CopyResult =
  | { ok: true; method: "async-clipboard" | "textarea-fallback" }
  | { ok: false; method: "async-clipboard" | "textarea-fallback" | "unsupported"; error: string };

export async function copyText(text: string): Promise<CopyResult> {
  if (!text) {
    return { ok: false, method: "unsupported", error: "Copy text is empty." };
  }

  if (canUseAsyncClipboard()) {
    try {
      await navigator.clipboard.writeText(text);
      return { ok: true, method: "async-clipboard" };
    } catch (error) {
      const fallback = fallbackCopyText(text);
      if (fallback) return { ok: true, method: "textarea-fallback" };

      return {
        ok: false,
        method: "async-clipboard",
        error: error instanceof Error ? error.message : "Clipboard write was blocked.",
      };
    }
  }

  if (fallbackCopyText(text)) {
    return { ok: true, method: "textarea-fallback" };
  }

  return {
    ok: false,
    method: "unsupported",
    error: "Clipboard API is unavailable in this browser or context.",
  };
}

function canUseAsyncClipboard(): boolean {
  return (
    typeof window !== "undefined" &&
    window.isSecureContext &&
    typeof navigator !== "undefined" &&
    Boolean(navigator.clipboard?.writeText)
  );
}

function fallbackCopyText(text: string): boolean {
  if (typeof document === "undefined") return false;

  const textarea = document.createElement("textarea");
  textarea.value = text;
  textarea.setAttribute("readonly", "");
  textarea.style.position = "fixed";
  textarea.style.top = "0";
  textarea.style.left = "-9999px";
  textarea.style.opacity = "0";

  const selection = document.getSelection();
  const selectedRange =
    selection && selection.rangeCount > 0 ? selection.getRangeAt(0) : null;

  document.body.appendChild(textarea);
  textarea.focus();
  textarea.select();

  try {
    return document.execCommand("copy");
  } catch {
    return false;
  } finally {
    document.body.removeChild(textarea);

    if (selection && selectedRange) {
      selection.removeAllRanges();
      selection.addRange(selectedRange);
    }
  }
}

document.execCommand("copy") पुरानी API है। इसे main path न बनाएं, लेकिन old browser, restricted WebView और HTTP preview में last fallback के रूप में useful है। यह भी fail कर सकता है, खासकर जब call user action के अंदर न हो।

React hook और accessible button

Hook copying, copied और failed states रखता है। Component role="status" और aria-live से result announce करता है।

// src/components/CopyButton.tsx
import { useCallback, useEffect, useRef, useState } from "react";
import { copyText, type CopyResult } from "../lib/clipboard";

type ClipboardStatus = "idle" | "copying" | "copied" | "failed";

export function useClipboard(resetAfter = 2000) {
  const [status, setStatus] = useState<ClipboardStatus>("idle");
  const [message, setMessage] = useState("");
  const timerRef = useRef<number | null>(null);

  useEffect(() => {
    return () => {
      if (timerRef.current) window.clearTimeout(timerRef.current);
    };
  }, []);

  const copy = useCallback(
    async (text: string): Promise<CopyResult> => {
      if (timerRef.current) window.clearTimeout(timerRef.current);

      setStatus("copying");
      setMessage("Copying...");

      const result = await copyText(text);

      if (result.ok) {
        setStatus("copied");
        setMessage("Copied to clipboard.");
      } else {
        setStatus("failed");
        setMessage("Copy failed. Select the text and copy it manually.");
      }

      timerRef.current = window.setTimeout(() => {
        setStatus("idle");
        setMessage("");
      }, resetAfter);

      return result;
    },
    [resetAfter],
  );

  return { copy, status, message };
}

type CopyButtonProps = {
  text: string;
  label?: string;
  copiedLabel?: string;
  className?: string;
};

export function CopyButton({
  text,
  label = "Copy",
  copiedLabel = "Copied",
  className = "",
}: CopyButtonProps) {
  const { copy, status, message } = useClipboard();
  const isCopying = status === "copying";

  return (
    <div className="inline-flex items-center gap-2">
      <button
        type="button"
        className={className}
        onClick={() => void copy(text)}
        disabled={isCopying}
        aria-label={status === "copied" ? copiedLabel : label}
      >
        {status === "copied" ? copiedLabel : label}
      </button>
      <span role="status" aria-live="polite" className="sr-only">
        {message}
      </span>
    </div>
  );
}

सिर्फ visual toast काफी नहीं है। Screen reader users को भी पता चलना चाहिए कि copy हुआ या fail हुआ। यह status region Playwright test के लिए भी stable assertion point देता है।

Code block copy UX

Documentation और blog में सबसे common use case code block copy है। ClaudeCodeLab की पहली implementation में Masa ने देखा कि “Copy” से “Copied” होते ही button width बढ़ती है और code block खिसकता है। Minimum width रखने से यह layout shift रुकता है।

// src/components/CodeBlockWithCopy.tsx
import { CopyButton } from "./CopyButton";

type CodeBlockWithCopyProps = {
  code: string;
  language?: string;
};

export function CodeBlockWithCopy({ code, language = "text" }: CodeBlockWithCopyProps) {
  return (
    <figure className="relative my-6 overflow-hidden rounded-md border border-slate-700 bg-slate-950">
      <figcaption className="flex min-h-10 items-center justify-between border-b border-slate-800 px-3 text-xs text-slate-300">
        <span>{language}</span>
        <CopyButton
          text={code}
          label="Copy code"
          copiedLabel="Copied"
          className="min-w-24 rounded bg-slate-800 px-3 py-1.5 text-xs font-medium text-white hover:bg-slate-700 focus:outline-none focus:ring-2 focus:ring-cyan-400 disabled:opacity-60"
        />
      </figcaption>
      <pre tabIndex={0} className="overflow-x-auto p-4 text-sm leading-6">
        <code>{code}</code>
      </pre>
    </figure>
  );
}

यही button CLI command, invite link, coupon, support ticket ID या generated prompt copy कर सकता है। Claude Code से reusable component और usage examples मांगें, सिर्फ code block-specific solution नहीं।

Paste को sensitive input मानें

Forms में browser का normal onPaste event पहले इस्तेमाल करें। navigator.clipboard.readText() केवल visible paste button के पीछे रखें।

// src/components/PasteImportBox.tsx
import { useState } from "react";

export function normalizePastedText(input: string): string {
  return input
    .replace(/\r\n?/g, "\n")
    .replace(/\u0000/g, "")
    .slice(0, 10_000);
}

export function PasteImportBox() {
  const [value, setValue] = useState("");
  const [message, setMessage] = useState("");

  async function pasteFromClipboard() {
    if (!navigator.clipboard?.readText || !window.isSecureContext) {
      setMessage("Use your browser paste shortcut instead.");
      return;
    }

    try {
      const text = await navigator.clipboard.readText();
      setValue(normalizePastedText(text));
      setMessage("Pasted from clipboard.");
    } catch {
      setMessage("Paste was blocked. Use Ctrl+V or Cmd+V in the text area.");
    }
  }

  return (
    <section aria-labelledby="paste-import-title">
      <h2 id="paste-import-title">Import prompt</h2>
      <button type="button" onClick={pasteFromClipboard}>
        Paste from clipboard
      </button>
      <textarea
        value={value}
        onChange={(event) => setValue(event.currentTarget.value)}
        onPaste={(event) => {
          const text = event.clipboardData.getData("text/plain");
          if (!text) return;

          event.preventDefault();
          setValue(normalizePastedText(text));
          setMessage("Pasted text was normalized.");
        }}
        aria-describedby="paste-import-help"
      />
      <p id="paste-import-help" role="status" aria-live="polite">
        {message}
      </p>
    </section>
  );
}

Pasted content पर भरोसा न करें। Length limit, line ending normalization, control character removal और format validation करें। HTML accept करते हैं तो render से पहले sanitize करें और clipboard HTML को सीधे dangerouslySetInnerHTML में न डालें।

Practical use cases और failure modes

Use caseImplementation detailCommon failure
Docs में code copyसिर्फ code string copy करें और success announce करेंButton label layout shift करता है
Admin में order ID copyसिर्फ ID copy करें, पूरी row नहींCustomer name या address clipboard में चला जाता है
Support logs pasteNormalize, size cap, secrets scanToken या cookie बिना limit के store हो जाता है
Invite link shareTime-limited URL copy करें और expiry दिखाएंExpired link support ticket बनाता है

ClaudeCodeLab products में भी यही बात लागू होती है। Free PDF commands, workshop setup steps और consulting diagnostic prompts तभी useful होते हैं जब reader exact text copy कर सके। लेकिन license key, buyer email और private customer data को एक convenient copy button में नहीं रखना चाहिए।

HTTP, iframe और mobile Safari caveats

Modern Async Clipboard secure context मांगती है। HTTPS और localhost ठीक हो सकते हैं; http://192.168.x.x preview में navigator.clipboard बिल्कुल न मिले। पहले fallback try करें, फिर manual copy guidance दें।

iframe में Chromium browsers को Permissions Policy या allow attribute चाहिए हो सकता है।

<iframe
  src="https://docs.example.com/embed"
  allow="clipboard-read; clipboard-write"
  title="Documentation preview"
></iframe>

Safari और iOS WebKit में clipboard call को click या tap से closely tied रखें। writeText से पहले fetch, timer, animation या route change wait न करें। Value पहले prepare करें, handler के अंदर तुरंत copy करें, फिर UI update करें। Critical flow को real iOS device पर test करें।

Playwright tests

Permissions उसी origin को दें जिसे page visit कर रहा है। Playwright docs बताती हैं कि permissions support browser और version के अनुसार बदल सकता है। Practical setup में Chromium actual clipboard content verify कर सकता है, और WebKit visible UI feedback verify कर सकता है।

// tests/clipboard.spec.ts
import { expect, test } from "@playwright/test";

const baseURL = "http://127.0.0.1:4173";

test.describe("clipboard UX", () => {
  test.beforeEach(async ({ context }) => {
    await context.grantPermissions(["clipboard-read", "clipboard-write"], {
      origin: baseURL,
    });
  });

  test("copies a code block", async ({ page }) => {
    await page.goto(`${baseURL}/docs/install`);

    await page.getByRole("button", { name: /copy code/i }).first().click();

    await expect(page.getByRole("status")).toContainText(/copied/i);
    await expect
      .poll(() => page.evaluate(() => navigator.clipboard.readText()))
      .toContain("npm");
  });

  test("normalizes pasted text", async ({ page }) => {
    await page.goto(`${baseURL}/support/import`);
    await page.evaluate(() => navigator.clipboard.writeText("line1\r\nline2\u0000"));

    await page.getByRole("button", { name: /paste from clipboard/i }).click();

    await expect(page.getByRole("textbox")).toHaveValue("line1\nline2");
  });
});

CI fail हो तो origin पहले जांचें। http://localhost:4173 और http://127.0.0.1:4173 अलग origins हैं। फिर browser permissions और translated button labels से role query टूट रही है या नहीं देखें।

Accessibility checklist

  • Clickable div नहीं, real button इस्तेमाल करें।
  • Success और failure को role="status" और aria-live="polite" से announce करें।
  • Labels specific रखें: “Copy code”, “Copy invite link”, “Copy order ID”।
  • Keyboard focus visible रखें।
  • Layout shift रोकने के लिए minimum width दें।
  • Failure पर next action बताएं।
  • Success को केवल color से न दिखाएं।
  • Clipboard read सिर्फ explicit paste action के बाद करें।

Claude Code review prompt

Implementation के बाद generic review न मांगें। Narrow prompt दें।

Review only clipboard-related changes.
Check:
1. Clipboard read is never triggered on page load.
2. writeText is called from a user action.
3. HTTP or unsupported browser fallback is handled.
4. copied/error feedback is accessible.
5. pasted text is normalized and size-limited.
6. Playwright tests grant permissions for the correct origin.
Return findings with file and line references.

ClaudeCodeLab training में ऐसी छोटी Web API features useful हैं, क्योंकि इनमें specification reading, implementation, fallback design, browser testing, accessibility review और documentation एक साथ आते हैं। Templates और practical guides के लिए ClaudeCodeLab products देखें। Team adoption और review workflow के लिए Claude Code training देखें।

Practical result

Masa के code-block copy UI में पहला issue visual था: copy के बाद button चौड़ा हुआ और code block shift हुआ। दूसरा issue mobile HTTP preview पर आया, जहां async clipboard path available नहीं था। Stable version ने copy logic को utility में निकाला, textarea fallback और manual-copy guidance जोड़ी, button width fix की और Playwright को exact origin के लिए permissions दीं। Clipboard API छोटी feature है, लेकिन production-ready तभी होती है जब permissions, privacy, accessibility और tests साथ design किए जाएं।

#Claude Code #Clipboard API #React #Playwright #Accessibility
मुफ़्त

मुफ़्त PDF: Claude Code cheatsheet

Email डालें और commands, review habits तथा safe workflow वाली एक-page PDF पाएँ.

हम आपका data सुरक्षित रखते हैं और spam नहीं भेजते.

Masa

लेखक के बारे में

Masa

Claude Code workflow और team adoption पर काम करने वाला engineer.