ブログやドキュメントをMarkdown(マークダウン)で書いているとき、
「コードスニペットをそのままコピーできたら便利なのに」
「読者がワンクリックでコードをコピーできるようにしたい」
と思ったことはありませんか?
Markdown自体にはコピーボタンをつける機能はありませんが、表示プラットフォームや追加ツールを使えば簡単に実現できます。
この記事では、Markdownにコピーボタンをつける方法を用途別にわかりやすく解説します。
Markdownとコピーボタンの基本知識

Markdownの制限事項
まず知っておいてほしいのは、Markdownはあくまで文章をマークアップ(構造化)するシンプルな記法なので、コピーボタンのようなJavaScriptによる動きのあるUIは標準ではつきません。
Markdownの基本的なコードブロック記法
```javascript
function hello() {
console.log("Hello, World!");
}
この記法だけでは、シンプルなコードブロックが表示されるだけで、コピー機能は付きません。
### コピーボタンが実現される仕組み
コピーボタンは以下の要素で実現されます:
1. **HTML構造**:ボタン要素とコードブロック
2. **CSS**:ボタンのデザインと配置
3. **JavaScript**:クリップボードへのコピー機能
4. **表示プラットフォーム**:これらを統合して提供
## プラットフォーム別のコピーボタン対応
### GitHub
#### 標準機能
GitHubのREADMEやWikiでは、通常のMarkdownコードブロックに自動的にコピーボタンは付きません。
#### Gist(推奨)
GitHubのGistを使用すると、右上に自動的にコピーボタンが表示されます。
**使用方法**:
1. [gist.github.com](https://gist.github.com) にアクセス
2. コードを入力
3. 公開設定を選択
4. 「Create public gist」をクリック
**Gistの埋め込み方法**:
```markdown
<script src="https://gist.github.com/username/gist-id.js"></script>
GitHub Pages
Jekyll(GitHub Pagesの標準)でコピーボタンを追加する方法:
_includes/copy-button.html
を作成:
<button class="copy-button" onclick="copyToClipboard('{{ include.target }}')">
コピー
</button>
- JavaScriptを追加:
function copyToClipboard(elementId) {
const element = document.getElementById(elementId);
const text = element.textContent;
navigator.clipboard.writeText(text);
}
GitLab
自動コピーボタン
GitLabでは、コードブロック(“`)に対して自動的にコピーボタンが表示されます。
特徴:
- 設定不要で自動的に表示
- 言語指定に関係なく表示
- モバイルでも動作
GitLab Pages
GitLab Pagesでも同様の機能が利用できます。
Bitbucket
標準機能
Bitbucketも GitLab と同様に、コードブロックの右上に自動的にコピーボタンが表示されます。
その他のプラットフォーム
Notion
- コードブロックに自動的にコピーボタンが付く
- 言語指定をするとシンタックスハイライトも適用
Discord
- コードブロックの右上にコピーボタンが自動表示
- ただし、Markdownファイルを直接アップロードする場合は対象外
静的サイトジェネレーター別の実装方法

Hugo
Prism.jsを使用した方法
1. 設定ファイル(config.yaml):
markup:
highlight:
noClasses: false
lineNos: true
2. テーマにPrism.jsを追加:
<!-- head.html -->
<link href="https://cdnjs.cloudflare.com/ajax/libs/prism/1.24.1/themes/prism.min.css" rel="stylesheet" />
<link href="https://cdnjs.cloudflare.com/ajax/libs/prism/1.24.1/plugins/toolbar/prism-toolbar.min.css" rel="stylesheet" />
3. JavaScriptファイルを追加:
<!-- footer.html -->
<script src="https://cdnjs.cloudflare.com/ajax/libs/prism/1.24.1/components/prism-core.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/prism/1.24.1/plugins/autoloader/prism-autoloader.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/prism/1.24.1/plugins/toolbar/prism-toolbar.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/prism/1.24.1/plugins/copy-to-clipboard/prism-copy-to-clipboard.min.js"></script>
カスタムコピーボタンの実装
1. ショートコードを作成(layouts/shortcodes/copycode.html):
<div class="code-container">
<pre><code class="language-{{ .Get 0 }}">{{ .Inner }}</code></pre>
<button class="copy-btn" onclick="copyCode(this)">コピー</button>
</div>
2. 使用方法:
{{< copycode "javascript" >}}
function hello() {
console.log("Hello, World!");
}
{{< /copycode >}}
Jekyll
jekyll-prism プラグインを使用
1. Gemfileに追加:
gem 'jekyll-prism'
2. _config.ymlに設定追加:
plugins:
- jekyll-prism
prism:
plugins:
- copy-to-clipboard
- toolbar
手動実装
1. _includes/copy-button.htmlを作成:
<div class="code-block-wrapper">
{{ content }}
<button class="copy-button" data-clipboard-target="#code-{{ include.id }}">
<i class="fas fa-copy"></i> コピー
</button>
</div>
2. clipboard.jsを導入:
<script src="https://cdnjs.cloudflare.com/ajax/libs/clipboard.js/2.0.8/clipboard.min.js"></script>
<script>
new ClipboardJS('.copy-button');
</script>
Docusaurus
標準機能
Docusaurusでは、デフォルトでコピーボタンが付きます。
docusaurus.config.js:
module.exports = {
themeConfig: {
prism: {
additionalLanguages: ['php', 'ruby', 'java'],
},
},
};
カスタマイズ方法
src/css/custom.css:
.theme-code-block .copy-button {
background-color: var(--ifm-color-primary);
border: none;
border-radius: 4px;
color: white;
padding: 4px 8px;
font-size: 12px;
}
Next.js
react-syntax-highlighterとの組み合わせ
1. 必要なパッケージをインストール:
npm install react-syntax-highlighter react-copy-to-clipboard
2. コンポーネント作成:
import { Prism as SyntaxHighlighter } from 'react-syntax-highlighter';
import { CopyToClipboard } from 'react-copy-to-clipboard';
import { useState } from 'react';
const CodeBlock = ({ language, children }) => {
const [copied, setCopied] = useState(false);
const handleCopy = () => {
setCopied(true);
setTimeout(() => setCopied(false), 2000);
};
return (
<div className="code-block-container">
<div className="code-header">
<span>{language}</span>
<CopyToClipboard text={children} onCopy={handleCopy}>
<button className="copy-button">
{copied ? 'コピー済み!' : 'コピー'}
</button>
</CopyToClipboard>
</div>
<SyntaxHighlighter language={language}>
{children}
</SyntaxHighlighter>
</div>
);
};
エディタとプレビューツール

Visual Studio Code
Markdown Preview Enhanced
インストール方法:
- VS Codeの拡張機能を開く
- “Markdown Preview Enhanced”を検索
- インストール
機能:
- コードブロック右上に自動的にコピーボタンが表示
- 様々なプログラミング言語に対応
- リアルタイムプレビュー
設定カスタマイズ
settings.json:
{
"markdown-preview-enhanced.codeBlockTheme": "github.css",
"markdown-preview-enhanced.previewTheme": "github-light.css"
}
Typora
標準機能
Typoraでは、プレビューモードでコードブロックにカーソルを合わせるとコピーボタンが表示されます。
Mark Text
特徴
- リアルタイム編集モード
- コードブロックのコピーボタン
- 軽量で高速
自作実装の方法
HTMLとJavaScriptを直接使用
基本的な実装
<div class="code-container">
<pre><code id="code-example">
npm install express
const app = require('express')();
app.listen(3000);
</code></pre>
<button onclick="copyToClipboard('code-example')" class="copy-btn">
コピー
</button>
</div>
<script>
function copyToClipboard(elementId) {
const element = document.getElementById(elementId);
const text = element.textContent;
navigator.clipboard.writeText(text).then(() => {
// コピー成功の通知
const button = event.target;
const originalText = button.textContent;
button.textContent = 'コピー完了!';
setTimeout(() => {
button.textContent = originalText;
}, 2000);
});
}
</script>
<style>
.code-container {
position: relative;
background-color: #f6f8fa;
border-radius: 6px;
padding: 16px;
margin: 16px 0;
}
.copy-btn {
position: absolute;
top: 8px;
right: 8px;
background: #0969da;
color: white;
border: none;
border-radius: 4px;
padding: 4px 8px;
font-size: 12px;
cursor: pointer;
}
.copy-btn:hover {
background: #0860ca;
}
</style>
より高度な実装
class CodeCopyButton {
constructor() {
this.init();
}
init() {
// すべてのコードブロックを検索
const codeBlocks = document.querySelectorAll('pre code');
codeBlocks.forEach((block, index) => {
this.addCopyButton(block, index);
});
}
addCopyButton(codeBlock, index) {
const container = codeBlock.parentElement;
container.style.position = 'relative';
const button = document.createElement('button');
button.className = 'copy-code-button';
button.textContent = 'コピー';
button.dataset.code = codeBlock.textContent;
button.addEventListener('click', (e) => {
this.copyCode(e.target);
});
container.appendChild(button);
}
async copyCode(button) {
try {
await navigator.clipboard.writeText(button.dataset.code);
this.showSuccess(button);
} catch (err) {
console.error('コピーに失敗しました:', err);
this.showError(button);
}
}
showSuccess(button) {
const originalText = button.textContent;
button.textContent = '✓ コピー完了';
button.classList.add('success');
setTimeout(() => {
button.textContent = originalText;
button.classList.remove('success');
}, 2000);
}
showError(button) {
const originalText = button.textContent;
button.textContent = '✗ エラー';
button.classList.add('error');
setTimeout(() => {
button.textContent = originalText;
button.classList.remove('error');
}, 2000);
}
}
// ページ読み込み後に初期化
document.addEventListener('DOMContentLoaded', () => {
new CodeCopyButton();
});
WordPress での実装
プラグインを使用
推奨プラグイン:
- Enlighter – Customizable Syntax Highlighter
- Code Block Pro
- Syntax Highlighter Evolved
手動実装
functions.php に追加:
function add_copy_button_script() {
wp_enqueue_script(
'copy-code-button',
get_template_directory_uri() . '/js/copy-button.js',
array(),
'1.0.0',
true
);
}
add_action('wp_enqueue_scripts', 'add_copy_button_script');
デザインとユーザビリティの考慮

ボタンデザインのベストプラクティス
視認性の確保
.copy-button {
/* 背景と十分なコントラストを確保 */
background-color: #0066cc;
color: white;
/* ホバー効果で操作可能であることを示す */
transition: background-color 0.2s ease;
}
.copy-button:hover {
background-color: #0052a3;
}
レスポンシブ対応
@media (max-width: 768px) {
.copy-button {
font-size: 14px;
padding: 6px 10px;
}
}
アクセシビリティの配慮
ARIA属性の追加
<button
class="copy-button"
aria-label="コードをクリップボードにコピー"
title="コピー">
📋
</button>
キーボード操作への対応
button.addEventListener('keydown', (e) => {
if (e.key === 'Enter' || e.key === ' ') {
e.preventDefault();
copyCode(e.target);
}
});
トラブルシューティング
よくある問題と解決方法
問題1:clipboard APIが動作しない
原因:HTTPSでない環境では clipboard API が制限される
解決方法:
function fallbackCopyTextToClipboard(text) {
const textArea = document.createElement("textarea");
textArea.value = text;
document.body.appendChild(textArea);
textArea.focus();
textArea.select();
try {
document.execCommand('copy');
} catch (err) {
console.error('Fallback: Could not copy text');
}
document.body.removeChild(textArea);
}
function copyToClipboard(text) {
if (!navigator.clipboard) {
fallbackCopyTextToClipboard(text);
return;
}
navigator.clipboard.writeText(text);
}
問題2:ボタンが表示されない
原因:CSSの優先度やJavaScriptの実行タイミング
解決方法:
// DOMContentLoadedで確実に実行
document.addEventListener('DOMContentLoaded', function() {
// 少し遅延させて確実に要素が存在してから実行
setTimeout(() => {
initCopyButtons();
}, 100);
});
問題3:コピーしたテキストに余計な文字が含まれる
原因:HTML要素や空白の処理
解決方法:
function getCleanText(element) {
// HTMLタグを除去し、余計な空白を整理
return element.textContent
.replace(/^\s+|\s+$/g, '') // 前後の空白を削除
.replace(/\n\s+/g, '\n'); // 行頭の余計なスペースを削除
}
まとめ
各方法の特徴比較
方法 | 難易度 | カスタマイズ性 | メンテナンス性 |
---|---|---|---|
プラットフォーム標準 | 低 | 低 | 高 |
静的サイトジェネレーター | 中 | 高 | 中 |
手動実装 | 高 | 最高 | 低 |
プラグイン使用 | 低 | 中 | 高 |
推奨アプローチ
初心者の場合
- GitLab/Bitbucket:設定不要で自動的にコピーボタンが付く
- Docusaurus:ドキュメントサイトなら最適
- VS Code + MPE:ローカルでの執筆・プレビューに便利
中級者の場合
- Hugo + Prism.js:高いカスタマイズ性
- Jekyll + プラグイン:GitHub Pagesとの相性が良い
- Next.js + カスタムコンポーネント:Reactベースの開発
上級者の場合
- 完全自作実装:要件に完全に合わせられる
- 既存ツールの拡張:独自機能の追加
- ライブラリ開発:再利用可能なソリューション
コメント