How to Build a Chrome Extension That Uses the Claude API in 2026 (Step-by-Step) ⏱️ 12 min read

Building a Chrome extension that calls an AI API sounds complicated until you actually do it — the whole thing comes down to a manifest file, a few JavaScript files, and a fetch call. I built one last month that rewrites selected text on any webpage using Claude, and the setup took about 90 minutes start to finish. Here’s the complete walkthrough.

What You’re Building

A Chrome extension with a right-click context menu option: select any text on a webpage, right-click, choose “Rewrite with Claude,” and a popup shows the improved version you can copy. The same pattern applies to summarization, translation, tone adjustment, or any text transformation you want.

Prerequisites: a free Anthropic API account (you get $5 in free credits), basic JavaScript knowledge, and Chrome. No build tools, no Node.js required — this runs as plain JavaScript.

Setting Up the Manifest

Create a new folder called claude-rewriter. Inside it, create manifest.json:

{
  "manifest_version": 3,
  "name": "Claude Rewriter",
  "version": "1.0",
  "description": "Rewrite selected text using Claude AI",
  "permissions": [
    "contextMenus",
    "storage",
    "activeTab",
    "scripting"
  ],
  "host_permissions": [
    "https://api.anthropic.com/*"
  ],
  "background": {
    "service_worker": "background.js"
  },
  "action": {
    "default_popup": "popup.html",
    "default_icon": "icon.png"
  }
}

Manifest V3 (required since January 2023) uses a service worker for the background script instead of a persistent background page. This matters for how you handle API calls and message passing.

A quick note on the host_permissions entry: Chrome requires you to explicitly declare which external URLs your extension can fetch from. Without https://api.anthropic.com/*, every API call will be blocked by CORS policy.

The Background Service Worker

Create background.js. This handles the context menu and the API call:

// Create context menu item on install
chrome.runtime.onInstalled.addListener(() => {
  chrome.contextMenus.create({
    id: "rewrite-claude",
    title: "Rewrite with Claude",
    contexts: ["selection"]
  });
});

// Handle context menu click
chrome.contextMenus.onClicked.addListener(async (info, tab) => {
  if (info.menuItemId === "rewrite-claude") {
    const selectedText = info.selectionText;
    
    // Store selected text and open popup
    await chrome.storage.local.set({ 
      selectedText,
      status: "loading",
      result: "" 
    });
    
    // Open the popup
    await chrome.action.openPopup();
    
    // Call Claude API
    const apiKey = await getApiKey();
    if (!apiKey) {
      await chrome.storage.local.set({ status: "error", result: "No API key set. Click the extension icon to add one." });
      return;
    }
    
    try {
      const response = await fetch("https://api.anthropic.com/v1/messages", {
        method: "POST",
        headers: {
          "x-api-key": apiKey,
          "anthropic-version": "2023-06-01",
          "content-type": "application/json"
        },
        body: JSON.stringify({
          model: "claude-haiku-4-5",
          max_tokens: 1024,
          messages: [{
            role: "user",
            content: `Rewrite the following text to be clearer and more concise. Return only the rewritten text, no explanation:

${selectedText}`
          }]
        })
      });
      
      const data = await response.json();
      const rewritten = data.content[0].text;
      await chrome.storage.local.set({ status: "done", result: rewritten });
    } catch (err) {
      await chrome.storage.local.set({ status: "error", result: err.message });
    }
  }
});

async function getApiKey() {
  const result = await chrome.storage.local.get("apiKey");
  return result.apiKey || null;
}

Claude Haiku is the right model here — it’s fast (response in 1–2 seconds) and cheap ($0.80/million input tokens). For a text rewriter that users trigger on demand, this keeps costs negligible.

The Popup UI

Create popup.html:

<!DOCTYPE html>
<html>
<head>
  <style>
    body { width: 360px; padding: 16px; font-family: system-ui; }
    textarea { width: 100%; height: 120px; margin: 8px 0; box-sizing: border-box; }
    button { background: #7c3aed; color: white; border: none; padding: 8px 16px; border-radius: 4px; cursor: pointer; }
    #status { color: #666; font-size: 13px; margin: 4px 0; }
    #api-section { margin-top: 12px; border-top: 1px solid #eee; padding-top: 12px; }
    input { width: 100%; padding: 6px; box-sizing: border-box; margin: 4px 0; }
  </style>
</head>
<body>
  <h3 style="margin:0 0 8px">Claude Rewriter</h3>
  <div id="status">Select text on any page and right-click to rewrite.</div>
  <textarea id="result" placeholder="Rewritten text will appear here..." readonly></textarea>
  <button id="copy" onclick="copyResult()">Copy</button>
  <div id="api-section">
    <label style="font-size:13px">API Key:</label>
    <input type="password" id="api-key" placeholder="sk-ant-..." />
    <button onclick="saveKey()">Save Key</button>
  </div>
  <script src="popup.js"></script>
</body>
</html>

Create popup.js:

// Load saved API key
chrome.storage.local.get("apiKey", (data) => {
  if (data.apiKey) document.getElementById("api-key").value = data.apiKey;
});

// Poll for results
function pollStatus() {
  chrome.storage.local.get(["status", "result"], (data) => {
    if (data.status === "loading") {
      document.getElementById("status").textContent = "Rewriting...";
      setTimeout(pollStatus, 500);
    } else if (data.status === "done") {
      document.getElementById("status").textContent = "Done!";
      document.getElementById("result").value = data.result;
      chrome.storage.local.set({ status: "idle" });
    } else if (data.status === "error") {
      document.getElementById("status").textContent = "Error: " + data.result;
      chrome.storage.local.set({ status: "idle" });
    }
  });
}

pollStatus();

function copyResult() {
  const text = document.getElementById("result").value;
  if (text) navigator.clipboard.writeText(text);
}

function saveKey() {
  const key = document.getElementById("api-key").value.trim();
  if (key) {
    chrome.storage.local.set({ apiKey: key });
    document.getElementById("status").textContent = "API key saved!";
  }
}

Loading and Testing the Extension

Add a simple 48×48 PNG as icon.png (any image works for development). Then:

  1. Open Chrome and go to chrome://extensions
  2. Enable “Developer mode” (top right toggle)
  3. Click “Load unpacked” and select your claude-rewriter folder
  4. Click the extension icon, enter your Anthropic API key, click Save
  5. Go to any webpage, select some text, right-click → “Rewrite with Claude”

The popup opens, shows “Rewriting…”, and within 2–3 seconds displays the rewritten text ready to copy. The API key is stored locally in Chrome’s extension storage — it never leaves your browser.

Extending the Pattern

Swap out the prompt to change the behavior entirely: “Summarize this in 3 bullet points,” “Translate to Spanish,” “Make this more formal,” or “Explain this like I’m 12.” You can add multiple context menu items by calling chrome.contextMenus.create multiple times with different IDs and prompts.

Final Verdict

Chrome extensions and AI APIs are a genuinely powerful combination — you get a tool that works across every website without building a separate app. The pattern here (manifest V3 + service worker + storage for async result passing) applies to any API integration, not just Claude.

Fork this as your starting point, swap in the prompt you actually want, and you’ll have a working AI-powered browser tool in an afternoon. Once it’s working locally, the Chrome Web Store submission process takes about a week for review.

Similar Posts

Leave a Reply

Your email address will not be published. Required fields are marked *