複製你的hightlight 程式碼
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>
);
}
整合到 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>
配 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>
);
}
適合已經 highlight 過的 code(裡面有 <span> 等 HTML 結構),用 textContent 抓純文字。