コードブロック(Code Block)
シンタックスハイライト付きコードブロックの実装を4つのアプローチで比較。SSR対応・バンドルサイズ・ハイライト品質の違いを確認できます。
1. Shiki
Next.js App Routerに最適なサーバーサイドのシンタックスハイライター。 VS Code と同等のハイライト品質を持ち、クライアントバンドルに影響を与えない。
✓ SSRで動作✓ ゼロクライアントバンドル✓ VS Code相当の品質
tsx
import { useState } from 'react';
interface CounterProps {
initialCount?: number;
}
export function Counter({ initialCount = 0 }: CounterProps) {
const [count, setCount] = useState(initialCount);
return (
<div>
<p>Count: {count}</p>
<button onClick={() => setCount(count + 1)}>Increment</button>
</div>
);
}特徴: サーバーサイドで実行するため、クライアントにJavaScriptを送らない。 テーマはVS Codeのテーマをそのまま使用でき、200以上のテーマと100以上の言語に対応。 Next.js App RouterのServer Componentとの相性が抜群。
tsx
// page.tsx(Server Component)
import { codeToHtml } from 'shiki';
import CodeBlockDisplay from './CodeBlockDisplay';
const code = `import { useState } from 'react';
export function Counter() {
const [count, setCount] = useState(0);
return (
<div>
<p>Count: {count}</p>
<button onClick={() => setCount(count + 1)}>Increment</button>
</div>
);
}`;
export default async function Page() {
const html = await codeToHtml(code, {
lang: 'tsx',
theme: 'github-dark',
});
return <CodeBlockDisplay html={html} code={code} />;
}
// CodeBlockDisplay.tsx('use client' でコピーボタンを追加)
'use client';
import { useState } from 'react';
export default function CodeBlockDisplay({
html,
code,
}: {
html: string;
code: string;
}) {
const [copied, setCopied] = useState(false);
return (
<div className="rounded-lg overflow-hidden">
<div className="flex items-center justify-between bg-zinc-800 px-4 py-2">
<span className="text-xs text-zinc-400">tsx</span>
<button
onClick={async () => {
await navigator.clipboard.writeText(code);
setCopied(true);
setTimeout(() => setCopied(false), 2000);
}}
className="text-xs px-2 py-1 rounded border border-zinc-600 text-zinc-300"
>
{copied ? 'コピーしました ✓' : 'コピー'}
</button>
</div>
<div dangerouslySetInnerHTML={{ __html: html }} />
</div>
);
}🤖 AIプロンプトテンプレート
Next.js App Routerで、Shikiを使ったシンタックスハイライト付きコードブロックを実装してください。 - 使用ライブラリ: shiki の codeToHtml - サーバーコンポーネントとして実装し、クライアントバンドルに影響を与えない - テーマ: github-dark - 言語: tsx - コピーボタンを追加する(コピーボタン部分のみ 'use client')
⚠️ このプロンプトはあくまでたたき台です。AIの回答はモデルやバージョン、会話の文脈によって毎回異なります。意図通りに動かない場合は、条件を追記・修正してお使いください。
2. Prism.js
クライアントサイドで動作する老舗のシンタックスハイライターライブラリ。 豊富なテーマとプラグインエコシステムを持ち、幅広い言語に対応する。
✓ 豊富なテーマ✓ プラグイン対応✓ 実績豊富
tsx
import { useState } from 'react';
interface CounterProps {
initialCount?: number;
}
export function Counter({ initialCount = 0 }: CounterProps) {
const [count, setCount] = useState(initialCount);
return (
<div>
<p>Count: {count}</p>
<button onClick={() => setCount(count + 1)}>Increment</button>
</div>
);
}特徴: クライアントサイドで
useEffectを使ってハイライトを適用する。 行番号表示・行のハイライトなど豊富なプラグインが揃っており、シンプルな静的サイトや React以外のプロジェクトにも使いやすい。tsx
'use client';
import { useEffect, useRef } from 'react';
import Prism from 'prismjs';
import 'prismjs/components/prism-typescript';
import 'prismjs/components/prism-jsx';
import 'prismjs/components/prism-tsx';
import 'prismjs/themes/prism-tomorrow.css';
const code = `import { useState } from 'react';
interface CounterProps {
initialCount?: number;
}
export function Counter({ initialCount = 0 }: CounterProps) {
const [count, setCount] = useState(initialCount);
return (
<div>
<p>Count: {count}</p>
<button onClick={() => setCount(count + 1)}>Increment</button>
</div>
);
}`;
export function CodeBlock() {
const codeRef = useRef<HTMLElement>(null);
useEffect(() => {
if (codeRef.current) {
Prism.highlightElement(codeRef.current);
}
}, []);
return (
<pre className="language-tsx">
<code ref={codeRef} className="language-tsx">
{code}
</code>
</pre>
);
}🤖 AIプロンプトテンプレート
Reactで、Prism.jsを使ったシンタックスハイライト付きコードブロックを実装してください。 - 使用ライブラリ: prismjs - 'use client' コンポーネントとして実装 - テーマ: prism-tomorrow(またはダーク系テーマ) - 言語: tsx - useEffect でハイライトを適用する - コピーボタンを追加する
⚠️ このプロンプトはあくまでたたき台です。AIの回答はモデルやバージョン、会話の文脈によって毎回異なります。意図通りに動かない場合は、条件を追記・修正してお使いください。
3. highlight.js
言語自動検出に対応した汎用のシンタックスハイライターライブラリ。 非Reactのプロジェクトにも使いやすく、ユーザーが任意のコードを貼り付けて表示するような用途に最適。
✓ 言語自動検出✓ 豊富な言語対応✓ フレームワーク非依存
tsx
import { useState } from 'react';
interface CounterProps {
initialCount?: number;
}
export function Counter({ initialCount = 0 }: CounterProps) {
const [count, setCount] = useState(initialCount);
return (
<div>
<p>Count: {count}</p>
<button onClick={() => setCount(count + 1)}>Increment</button>
</div>
);
}特徴: 言語を指定しなくても自動で言語を検出してハイライトできる。 ユーザーが任意のコードを貼り付けて表示するCMS・ブログ・Wikiなどの用途に向いている。 コアとなる実装がシンプルで、React・Vue・Angularなどフレームワーク非依存で利用可能。
tsx
'use client';
import { useEffect, useRef } from 'react';
import hljs from 'highlight.js/lib/core';
import typescript from 'highlight.js/lib/languages/typescript';
import xml from 'highlight.js/lib/languages/xml';
import 'highlight.js/styles/github-dark.css';
hljs.registerLanguage('typescript', typescript);
hljs.registerLanguage('xml', xml);
const code = `import { useState } from 'react';
interface CounterProps {
initialCount?: number;
}
export function Counter({ initialCount = 0 }: CounterProps) {
const [count, setCount] = useState(initialCount);
return (
<div>
<p>Count: {count}</p>
<button onClick={() => setCount(count + 1)}>Increment</button>
</div>
);
}`;
export function CodeBlock() {
const codeRef = useRef<HTMLElement>(null);
useEffect(() => {
if (codeRef.current) {
hljs.highlightElement(codeRef.current);
}
}, []);
return (
<pre>
<code ref={codeRef} className="language-typescript">
{code}
</code>
</pre>
);
}🤖 AIプロンプトテンプレート
Reactで、highlight.jsを使ったシンタックスハイライト付きコードブロックを実装してください。 - 使用ライブラリ: highlight.js - 'use client' コンポーネントとして実装 - テーマ: github-dark - 言語: typescript(自動検出も可) - useEffect でハイライトを適用する - コピーボタンを追加する
⚠️ このプロンプトはあくまでたたき台です。AIの回答はモデルやバージョン、会話の文脈によって毎回異なります。意図通りに動かない場合は、条件を追記・修正してお使いください。
4. プレーン HTML
追加ライブラリなしで実装するシンプルなコードブロック。<pre><code>とTailwind CSSのみを使用。
✓ 依存ゼロ✓ 最軽量✓ どんな環境でも動作
tsx
import { useState } from 'react';
interface CounterProps {
initialCount?: number;
}
export function Counter({ initialCount = 0 }: CounterProps) {
const [count, setCount] = useState(initialCount);
return (
<div>
<p>Count: {count}</p>
<button onClick={() => setCount(count + 1)}>Increment</button>
</div>
);
}特徴: シンタックスハイライトは行わず、等幅フォントとダーク背景のみでコードを表示する。 コマンド例・ファイルパス・設定値など、ハイライト不要な短いコードブロックに十分。 追加依存ゼロのため、どんな環境でもそのまま使える。
tsx
import { useState } from 'react';
const code = `import { useState } from 'react';
interface CounterProps {
initialCount?: number;
}
export function Counter({ initialCount = 0 }: CounterProps) {
const [count, setCount] = useState(initialCount);
return (
<div>
<p>Count: {count}</p>
<button onClick={() => setCount(count + 1)}>Increment</button>
</div>
);
}`;
export function PlainCodeBlock() {
const [copied, setCopied] = useState(false);
return (
<div className="rounded-lg overflow-hidden border border-zinc-700">
<div className="flex items-center justify-between bg-zinc-800 px-4 py-2">
<span className="text-xs text-zinc-400 font-mono">tsx</span>
<button
onClick={async () => {
await navigator.clipboard.writeText(code);
setCopied(true);
setTimeout(() => setCopied(false), 2000);
}}
className="text-xs px-2 py-1 rounded border border-zinc-600 text-zinc-300 hover:bg-zinc-700"
>
{copied ? 'コピーしました ✓' : 'コピー'}
</button>
</div>
<pre className="bg-zinc-900 p-4 overflow-x-auto m-0">
<code className="text-zinc-100 text-sm font-mono whitespace-pre">{code}</code>
</pre>
</div>
);
}🤖 AIプロンプトテンプレート
Reactで、ライブラリなしのシンタックスハイライト付きコードブロックを実装してください。 - 追加ライブラリなし、<pre><code> のみ使用 - Tailwind CSS でスタイリング(ダーク背景、等幅フォント) - コピーボタンを追加する - ハイライトは不要(モノクロで可)
⚠️ このプロンプトはあくまでたたき台です。AIの回答はモデルやバージョン、会話の文脈によって毎回異なります。意図通りに動かない場合は、条件を追記・修正してお使いください。
ライブラリ比較表
| 項目 | Shiki | Prism.js | highlight.js | プレーン HTML |
|---|---|---|---|---|
| 動作環境 | サーバー(SSR) | クライアント | クライアント | どちらでも |
| バンドルサイズ | ゼロ | 中 | 中 | ゼロ |
| ハイライト品質 | VS Code相当 | 良好 | 良好 | なし |
| 言語自動検出 | ❌ | ❌ | ✅ | ❌ |
| テーマ数 | 多(VS Codeテーマ対応) | 多 | 多 | 自前 |
| Next.js との相性 | ◎ App Router最適 | ○ | ○ | ◎ |
| 導入の手軽さ | 中 | 易 | 易 | 最易 |
選択のポイント
- •Shiki — Next.js App Router との相性が最も良く、サーバーサイドでハイライト済みHTMLを生成するためクライアントのバンドルサイズに影響しない。VS Codeと同等のハイライト品質が必要な場合に最適。
- •Prism.js — クライアントサイドで動作する老舗ライブラリ。テーマが豊富でドキュメントも充実しており、React以外のプロジェクトにも使いやすい。
- •highlight.js — 言語自動検出が特徴。ユーザーが任意のコードを貼り付けて表示するような用途(言語が不定)に向いている。
- •プレーン HTML — 追加依存ゼロ。コマンド例やファイルパスなどハイライト不要な短いコードブロックに十分。