跳到主要內容
← 回到學習

學習 · 開發工具

讓你網頁的Code語法 Hightlight起來

目錄 · 4 個段落
  1. 01教學 (一般React環境)
  2. 02Next.js中如何使用
  3. 03新增複製按鈕功能 (新版做法)
  4. 04新增複製按鈕功能 (舊版作法)
TL;DR
Web 端 Syntax Highlighting 三主流:Prism.js(輕量、CSS-based)、Highlight.js(老牌、語言多)、Shiki(2026 推薦)(VS Code 同款、build time 渲染、零 runtime cost)。配 rehype-pretty-code 是 Next.js / MDX 黃金組合。

教學 (一般React環境)

要在 React 中呈現帶有語法高亮的代碼塊,您可以使用第三方庫,例如,Prism.js 或 Highlight.js。這些庫可以自動對代碼進行語法高亮,並呈現具有顏色標記的代碼塊。

以 Prism.js 為例,您需要在 React 項目中安裝 Prism.js 庫:

npm install prismjs
npm i @types/prismjs --save-dev

然後,您需要匯入 Prism.js 並選擇您想要的語言(例如,JavaScript):

import React from 'react';
import Prism from 'prismjs';
import 'prismjs/components/prism-javascript';
 
function ExampleCodeBlock() {
  return (
    <pre>
      <code className="language-javascript">
        {`
          // Example code block
          function example() {
            console.log("Hello, World!");
          }
        `}
      </code>
    </pre>
  );
}
 
export default ExampleCodeBlock;
javascript

這段代碼定義了一個名為 **ExampleCodeBlock** 的 React 組件,該組件將返回一個預先格式化的代碼塊,其中包含示例代碼,並將其類名設置為 **language-javascript**。這將告訴 Prism.js 該代碼是 JavaScript 代碼,因此需要進行語法高亮。

您還需要匯入 Prism.js 的 CSS 樣式以使代碼塊顯示有顏色標記的語法高亮:

import 'prismjs/themes/prism.css';
javascript

請注意,您需要確保將 CSS 文件路徑正確引入您的 React 項目,並且它必須可以在 Web 浏览器中訪問到,以便使用

useEffect(() => {
        Prism.highlightAll()
}, [])
typescript

Common CodeBlock 我寫好的

interface Props1 {
    children: React.ReactNode
}
 
export const CodeBlockTS: React.FC<Props1> = ({ children }) => (
    <pre>
        <code className="language-typescript" >
            {children}
        </code>
    </pre>
);
typescript

Next.js中如何使用

和前面的方法完全不同 (也許這個方法 前面也可以用這樣 但我還沒測試)

npm install prismjs
npm i @types/prismjs --save-dev
npm i react-syntax-highlighter
npm i @types/react-syntax-highlighter

然後打上這邊,之後就能夠使用了

import { Prism } from 'react-syntax-highlighter';
import { vscDarkPlus } from 'react-syntax-highlighter/dist/cjs/styles/prism';
 
<Prism language="javascript" style={vscDarkPlus}>
   {`/* Your Code Here */`}
</Prism >
typescript

新增複製按鈕功能 (新版做法)

What is the easiest or recommended method to add a copy code button? · mdx-js · Discussion #1948
Hi, I was wondering what is the easiest or recommended method to add a copy code button, like in the mdx docs page or like in the github markdown view. https://github.com/orgs/mdx-js/discussions/1948

先安裝clsx 為了讓css可以用複雜的邏輯

npm install clsx
javascript

接著創建一個複製的Commponent如下:記得搭配的是上面的Next.js中如何使用喔 (非Next.js中的第一版那個 最上面的我還沒測試)

import clsx from 'clsx'
import React from 'react'
 
interface ICopyToClipboard {
    children: React.ReactNode
}
 
export const CopyToClipboard = ({ children }: ICopyToClipboard) => {
    const textInput = React.useRef<HTMLDivElement>(null)
    const [hovered, setHovered] = React.useState(false)
    const [copied, setCopied] = React.useState(false)
 
    const onEnter = () => {
        setHovered(true)
    }
 
    const onExit = () => {
        setHovered(false)
        setCopied(false)
    }
 
    const onCopy = () => {
        setCopied(true)
        if (textInput.current !== null && textInput.current.textContent !== null)
            navigator.clipboard.writeText(textInput.current.textContent)
        setTimeout(() => {
            setCopied(false)
        }, 2000)
    }
 
    return (
        <div
            ref={textInput}
            onMouseEnter={onEnter}
            onMouseLeave={onExit}
            className="relative code-block"
        >
            {hovered && (
                <button
                    aria-label="Copy code"
                    type="button"
                    className={clsx(
                        'absolute right-2 top-2 w-8 h-8 p-1 rounded border-2 bg-gray-700 dark:bg-gray-800',
                        {
                            'focus:outline-none focus:border-green-400 border-green-400': copied,
                            'hover:border-gray-300': !copied,
                        }
                    )}
                    onClick={onCopy}
                >
                    <svg
                        xmlns="http://www.w3.org/2000/svg"
                        viewBox="0 0 24 24"
                        stroke="currentColor"
                        fill="none"
                        className={copied ? 'text-green-400' : 'text-gray-300'}
                    >
                        {copied ? (
                            <>
                                <path
                                    strokeLinecap="round"
                                    strokeLinejoin="round"
                                    strokeWidth={2}
                                    d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2m-6 9l2 2 4-4"
                                />
                            </>
                        ) : (
                            <>
                                <path
                                    strokeLinecap="round"
                                    strokeLinejoin="round"
                                    strokeWidth={2}
                                    d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2"
                                />
                            </>
                        )}
                    </svg>
                </button>
            )}
            {children}
        </div>
    )
}
json

我們只需要將程式碼如下 包起來就殼已了

<CopyToClipboard>
	<Prism language="javascript" style={vscDarkPlus}>
                        {`service firebase.storage {
    match /b/{bucket}/o {
        match /{allPaths=**} {
            // 只允許已經通過身份驗證的用戶進行讀寫
            allow read, write: if request.auth.uid != null;
        }
    }
}`}
	</Prism>
</CopyToClipboard>
javascript

最後我們優化一下

我們直接把Prism元件放到CopyToCLipboard去,這樣使用就更方便。

import clsx from 'clsx'
import React from 'react'
import { Prism } from 'react-syntax-highlighter';
import { vscDarkPlus } from 'react-syntax-highlighter/dist/cjs/styles/prism';
 
interface ICopyToClipboard {
    children: string
}
 
export const CopyToClipboard = ({ children }: ICopyToClipboard) => {
    const textInput = React.useRef<HTMLDivElement>(null)
    const [hovered, setHovered] = React.useState(false)
    const [copied, setCopied] = React.useState(false)
 
    const onEnter = () => {
        setHovered(true)
    }
 
    const onExit = () => {
        setHovered(false)
        setCopied(false)
    }
 
    const onCopy = () => {
        setCopied(true)
        if (textInput.current !== null && textInput.current.textContent !== null)
            navigator.clipboard.writeText(textInput.current.textContent)
        setTimeout(() => {
            setCopied(false)
        }, 2000)
    }
 
    return (
        <div
            ref={textInput}
            onMouseEnter={onEnter}
            onMouseLeave={onExit}
            className="relative code-block"
        >
            {hovered && (
                <button
                    aria-label="Copy code"
                    type="button"
                    className={clsx(
                        'absolute right-2 top-2 w-8 h-8 p-1 rounded border-2 bg-gray-700 dark:bg-gray-800',
                        {
                            'focus:outline-none focus:border-green-400 border-green-400': copied,
                            'hover:border-gray-300': !copied,
                        }
                    )}
                    onClick={onCopy}
                >
                    <svg
                        xmlns="http://www.w3.org/2000/svg"
                        viewBox="0 0 24 24"
                        stroke="currentColor"
                        fill="none"
                        className={copied ? 'text-green-400' : 'text-gray-300'}
                    >
                        {copied ? (
                            <>
                                <path
                                    strokeLinecap="round"
                                    strokeLinejoin="round"
                                    strokeWidth={2}
                                    d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2m-6 9l2 2 4-4"
                                />
                            </>
                        ) : (
                            <>
                                <path
                                    strokeLinecap="round"
                                    strokeLinejoin="round"
                                    strokeWidth={2}
                                    d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2"
                                />
                            </>
                        )}
                    </svg>
                </button>
            )}
            <Prism language="jsx" style={vscDarkPlus}>
                {children}
            </Prism>
        </div>
    )
}
javascript

就只需要使用CopyToClipboard然後寫上code就完成了

<CopyToClipboard>
                    {`service firebase.storage {
    match /b/{bucket}/o {
        match /{allPaths=**} {
            // 只允許已經通過身份驗證的用戶進行讀寫
            allow read, write: if request.auth.uid != null;
        }
    }
}`}
</CopyToClipboard>
javascript

新增複製按鈕功能 (舊版作法)

npm i react-copy-to-clipboard
npm i @types/react-copy-to-clipboard

之後就可以使用啦

import React from 'react';
import { Prism as SyntaxHighlighter } from 'react-syntax-highlighter';
import { CopyToClipboard } from 'react-copy-to-clipboard';
 
<CopyToClipboard text={`your code`}>
          <button>Copy</button>
</CopyToClipboard>
typescript

目錄