Chrome Extension Copy as Markdown — Developer Guide

7 min read

Build a Copy-as-Markdown Chrome Extension

This tutorial guides you through building a Chrome extension that converts web content to Markdown format. The extension will copy selected HTML, links, and page titles as clean Markdown.

Prerequisites

Step 1: Create the Manifest

The manifest defines permissions and the extension’s configuration. For this extension, we need three key permissions:

{
  "manifest_version": 3,
  "name": "Copy as Markdown",
  "version": "1.0",
  "description": "Copy web content as clean Markdown",
  "permissions": ["activeTab", "contextMenus", "clipboardWrite"],
  "background": {
    "service_worker": "background.js"
  },
  "action": {
    "default_popup": "popup.html"
  }
}

Step 2: Set Up Context Menus

Create the background script to register context menu items for different copy scenarios. The extension supports three context menu types: selection text, links, and the page itself.

// background.js
chrome.runtime.onInstalled.addListener(() => {
  chrome.contextMenus.create({
    id: "copySelection",
    title: "Copy as Markdown",
    contexts: ["selection"]
  });
  
  chrome.contextMenus.create({
    id: "copyLink",
    title: "Copy Link as Markdown",
    contexts: ["link"]
  });
  
  chrome.contextMenus.create({
    id: "copyPage",
    title: "Copy Page as Markdown",
    contexts: ["page"]
  });
});

Step 3: Build the HTML-to-Markdown Converter

Create a utility function that converts HTML elements to Markdown. This converter handles headings (h1-h6), bold (strong, b), italic (em, i), links (a), lists (ul, ol), images (img), and code blocks (pre, code).

// converter.js
function htmlToMarkdown(html) {
  const temp = document.createElement('div');
  temp.innerHTML = html;
  
  // Handle headings
  temp.querySelectorAll('h1,h2,h3,h4,h5,h6').forEach(el => {
    const level = el.tagName[1];
    el.replaceWith('#'.repeat(level) + ' ' + el.textContent + '\n');
  });
  
  // Handle bold and italic
  el.replaceWith('**' + el.textContent + '**'); // bold
  el.replaceWith('*' + el.textContent + '*');   // italic
  
  // Handle links: <a href="url">text</a> -> [text](url)
  el.replaceWith('[' + el.textContent + '](' + el.href + ')');
  
  // Handle images: <img src="url" alt="text"> -> ![alt](url)
  el.replaceWith('![' + el.alt + '](' + el.src + ')');
  
  return temp.textContent;
}

Step 4: Handle Text Selection

When users select text on a page, retrieve the HTML content using window.getSelection(). Pass this to the converter to generate Markdown. This approach preserves formatting better than using textContent alone.

chrome.contextMenus.onClicked.addListener((info, tab) => {
  if (info.menuItemId === "copySelection") {
    chrome.scripting.executeScript({
      target: { tabId: tab.id },
      func: () => {
        const selection = window.getSelection();
        const html = selection.getRangeAt(0).cloneContents();
        return htmlToMarkdown(html.innerHTML);
      }
    }).then(results => {
      navigator.clipboard.writeText(results[0].result);
    });
  }
});

When right-clicking a link, extract the URL and text to format as [text](url). The context menu provides info.linkText and info.linkUrl for this purpose.

if (info.menuItemId === "copyLink") {
  const markdown = `[${info.linkText}](${info.linkUrl})`;
  navigator.clipboard.writeText(markdown);
}

Step 6: Copy Page Title and URL

For copying the entire page, use the tab’s title and URL. This creates a reference link in Markdown: [Page Title](https://example.com).

if (info.menuItemId === "copyPage") {
  const markdown = `[${tab.title}](${tab.url})`;
  navigator.clipboard.writeText(markdown);
}

Step 7: Add a Popup with Preview

Create popup.html to show a preview of the converted Markdown before copying. This gives users confidence that the conversion worked correctly.

<!-- popup.html -->
<!DOCTYPE html>
<html>
<head>
  <style>
    body { width: 300px; padding: 10px; font-family: sans-serif; }
    textarea { width: 100%; height: 200px; }
    button { margin-top: 10px; }
  </style>
</head>
<body>
  <h3>Copy as Markdown</h3>
  <textarea id="preview"></textarea>
  <button id="copyBtn">Copy to Clipboard</button>
  <script src="popup.js"></script>
</body>
</html>

Advanced Features

Table Conversion

Convert HTML tables to Markdown pipe tables with proper alignment:

function tableToMarkdown(table) {
  const rows = table.querySelectorAll('tr');
  return Array.from(rows, row => {
    const cells = row.querySelectorAll('th, td');
    return '| ' + Array.from(cells, c => c.textContent).join(' | ') + ' |';
  }).join('\n');
}

Markdown Flavor Options

Support different Markdown flavors (GFM, CommonMark) by adding an options page. GFM supports tables, task lists, and strikethrough, while CommonMark is more strict.

Copy Notification

Provide visual feedback when content is copied using the Chrome notifications API or a simple toast in the popup.

Conclusion

This extension demonstrates core Chrome extension concepts: context menus, clipboard access, and content script injection. With these foundations, you can extend functionality to support more Markdown features or integrate with note-taking apps. -e —


Turn Your Extension Into a Business

Ready to monetize? The Extension Monetization Playbook covers freemium models, Stripe integration, subscription architecture, and growth strategies for Chrome extension developers.


Part of the Chrome Extension Guide by theluckystrike. Built at zovo.one.

No previous article
No next article