跳到主要內容
← 回到學習

學習 · 開發工具

複製你的hightlight 程式碼

目錄 · 3 個段落
  1. 01最簡實作
  2. 02整合到 ReactMarkdown / MDX
  3. 03進階:從 DOM 抓真實 code 文字

Copy 程式碼按鈕(Copy to Clipboard)

TL;DR
部落格 / 文件站常見的「Copy 按鈕」放在 code block 右上角。現代做法:用 navigator.clipboard.writeText()(原生 API,所有現代瀏覽器支援),配合 rehype-pretty-code 或自製 ReactMarkdown component。
主要參考
mdx-js Discussion #1948 — Copy code button 推薦做法

最簡實作

'use client';
import { useState } from 'react';
 
export function CopyButton({ text }: { text: string }) {
  const [copied, setCopied] = useState(false);
 
  const handleCopy = async () => {
    await navigator.clipboard.writeText(text);
    setCopied(true);
    setTimeout(() => setCopied(false), 2000);
  };
 
  return (
    <button onClick={handleCopy} aria-label="Copy code">
      {copied ? '✓ Copied!' : '📋 Copy'}
    </button>
  );
}
tsx

整合到 ReactMarkdown / MDX

<ReactMarkdown
  components={{
    pre: ({ children, ...props }) => {
      const text = typeof children === 'string' ? children : '';
      return (
        <div style={{ position: 'relative' }}>
          <CopyButton text={text} />
          <pre {...props}>{children}</pre>
        </div>
      );
    },
  }}
>
  {markdown}
</ReactMarkdown>
tsx

配 rehype-pretty-code 或 shiki 處理 syntax highlight,組合起來就是業界標準的 code block UI。


進階:從 DOM 抓真實 code 文字

'use client';
import { useRef } from 'react';
 
function CodeBlock({ children }: { children: React.ReactNode }) {
  const ref = useRef<HTMLPreElement>(null);
 
  const handleCopy = async () => {
    const code = ref.current?.textContent ?? '';
    await navigator.clipboard.writeText(code);
  };
 
  return (
    <div className="relative">
      <button onClick={handleCopy}>Copy</button>
      <pre ref={ref}>{children}</pre>
    </div>
  );
}
tsx

適合已經 highlight 過的 code(裡面有 <span> 等 HTML 結構),用 textContent 抓純文字。

目錄