Claude Skills Guide

Building a Chrome extension for counting words in essays is a practical project that combines JavaScript DOM manipulation, Chrome’s extension APIs, and real-time text analysis. This guide walks you through creating a fully functional word counter extension tailored for essay writing workflows.

Why Build a Custom Word Counter Extension

Pre-built word counters exist, but they often lack the specificity writers need. A custom extension can track:

For developers, this project demonstrates essential Chrome extension concepts including content scripts, popup UI, and message passing between components.

Project Structure

A Chrome extension requires a manifest file and your source files. Here’s the minimal structure:

word-counter-extension/
├── manifest.json
├── popup.html
├── popup.js
├── content.js
└── styles.css

Manifest Configuration

The manifest.json defines your extension’s capabilities:

{
  "manifest_version": 3,
  "name": "Essay Word Counter",
  "version": "1.0",
  "description": "Real-time word counter for essay writing",
  "permissions": ["activeTab", "scripting"],
  "action": {
    "default_popup": "popup.html",
    "default_icon": "icon.png"
  },
  "content_scripts": [{
    "matches": ["<all_urls>"],
    "js": ["content.js"]
  }]
}

Manifest V3 is the current standard, replacing the deprecated V2. Note the content_scripts array runs on every page matching the pattern, enabling real-time text analysis.

Content Script for Real-Time Counting

The content script injects into web pages and monitors text input:

// content.js
function countText() {
  const selection = window.getSelection().toString();
  const bodyText = document.body.innerText;
  
  // Count words (split by whitespace, filter empty)
  const words = bodyText.trim().split(/\s+/).filter(w => w.length > 0);
  const characters = bodyText.replace(/\s/g, '').length;
  const paragraphs = bodyText.split(/\n\n+/).filter(p => p.trim().length > 0);
  
  // Estimate reading time (average 200 words per minute)
  const readingTime = Math.ceil(words.length / 200);
  
  return {
    words: words.length,
    characters: characters,
    paragraphs: paragraphs.length,
    readingTime: readingTime
  };
}

// Listen for messages from popup
chrome.runtime.onMessage.addListener((request, sender, sendResponse) => {
  if (request.action === 'getCounts') {
    const counts = countText();
    sendResponse(counts);
  }
});

This script counts words by splitting on whitespace, filters out empty strings, and calculates reading time based on 200 words per minute—a standard reading pace.

The popup displays statistics when users click the extension icon:

<!-- popup.html -->
<!DOCTYPE html>
<html>
<head>
  <link rel="stylesheet" href="styles.css">
</head>
<body>
  <div class="stats-container">
    <h2>Essay Stats</h2>
    <div class="stat-item">
      <span class="label">Words:</span>
      <span id="word-count" class="value">-</span>
    </div>
    <div class="stat-item">
      <span class="label">Characters:</span>
      <span id="char-count" class="value">-</span>
    </div>
    <div class="stat-item">
      <span class="label">Paragraphs:</span>
      <span id="para-count" class="value">-</span>
    </div>
    <div class="stat-item">
      <span class="label">Reading Time:</span>
      <span id="read-time" class="value">-</span>
    </div>
  </div>
  <script src="popup.js"></script>
</body>
</html>

The popup script requests counts from the content script:

// popup.js
document.addEventListener('DOMContentLoaded', async () => {
  const [tab] = await chrome.tabs.query({ active: true, currentWindow: true });
  
  chrome.tabs.sendMessage(tab.id, { action: 'getCounts' }, (response) => {
    if (response) {
      document.getElementById('word-count').textContent = response.words;
      document.getElementById('char-count').textContent = response.characters;
      document.getElementById('para-count').textContent = response.paragraphs;
      document.getElementById('read-time').textContent = `${response.readingTime} min`;
    } else {
      document.getElementById('word-count').textContent = 'N/A';
    }
  });
});

The communication pattern here—sending a message from popup to content script—demonstrates inter-component messaging in Chrome extensions.

Styling the Popup

Basic styling improves usability:

/* styles.css */
body {
  width: 250px;
  font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;
  padding: 16px;
  margin: 0;
}

.stats-container {
  background: #f9f9f9;
  border-radius: 8px;
  padding: 12px;
}

h2 {
  margin: 0 0 12px;
  font-size: 16px;
  color: #333;
}

.stat-item {
  display: flex;
  justify-content: space-between;
  padding: 6px 0;
  border-bottom: 1px solid #eee;
}

.stat-item:last-child {
  border-bottom: none;
}

.label {
  color: #666;
  font-size: 13px;
}

.value {
  font-weight: 600;
  color: #222;
}

Loading and Testing

To test your extension:

  1. Open Chrome and navigate to chrome://extensions/
  2. Enable “Developer mode” in the top right
  3. Click “Load unpacked” and select your extension directory
  4. Visit any page with text and click your extension icon

The counts should appear instantly. If they show “N/A”, the content script may not have loaded—try refreshing the page.

Advanced Enhancements

Once the basics work, consider adding:

Common Pitfalls

Several issues commonly trip up developers:

Publishing Your Extension

To share your extension:

  1. Create a developer account at the Chrome Web Store
  2. Package your extension as a ZIP file
  3. Upload and provide store listing details
  4. Pay the one-time developer fee ($5)

Your extension can reach millions of users searching for writing tools.

Conclusion

Building a word counter Chrome extension teaches core extension development concepts while creating a genuinely useful tool for essay writers. The architecture—content scripts for page interaction, popup for display, and message passing between them—forms the foundation for more complex extensions.

The code above provides a working foundation. Customize the counting logic, enhance the UI, and adapt it for specific writing platforms to differentiate your extension in the Chrome Web Store.

Built by theluckystrike — More at zovo.one