Learn how to build powerful Chrome extensions with this comprehensive guide covering practical implementation and best practices. This guide provides step-by-step instructions for creating professional-grade extensions. This tutorial walks you through creating a Chrome extension that enhances GitHub’s UI with productivity features.
github-enhancer/
├── manifest.json
├── content.js
├── popup/popup.html, popup.js
├── utils/api.js
├── utils/dom.js
Configure the manifest with content scripts matching GitHub URLs:
{
"manifest_version": 3,
"name": "GitHub Enhancer",
"permissions": ["storage"],
"host_permissions": ["https://github.com/", "https://api.github.com/"],
"action": { "default_popup": "popup/popup.html" },
"content_scripts": [{
"matches": ["https://github.com/*"],
"js": ["content.js"],
"run_at": "document_idle"
}]
}
Identify which GitHub page type the user is viewing using URL patterns:
const PageType = {
REPO: /github\.com\/[^/]+\/[^/]+$/,
FILE: /github\.com\/[^/]+\/[^/]+\/blob\//,
PR: /github\.com\/[^/]+\/[^/]+\/pull\//,
ISSUES: /github\.com\/[^/]+\/[^/]+\/issues\//
};
function detectPageType(url) {
if (PageType.PR.test(url)) return 'pr';
if (PageType.FILE.test(url)) return 'file';
if (PageType.ISSUES.test(url)) return 'issues';
if (PageType.REPO.test(url)) return 'repo';
return null;
}
Reference: URL Matching Patterns
Build a file tree sidebar using GitHub API or DOM scraping:
async function buildFileTreeSidebar() {
const repo = extractRepoInfo();
const response = await fetch(`https://api.github.com/repos/${repo}/git/trees/main?recursive=1`);
const data = await response.json();
const tree = document.createElement('div');
tree.className = 'gh-enhancer-file-tree';
tree.innerHTML = renderFileTree(data.tree);
document.querySelector('.Layout-sidebar').prepend(tree);
}
Add a copy button to file headers:
function addCopyButton() {
const header = document.querySelector('.file-header');
if (!header) return;
const btn = document.createElement('button');
btn.className = 'btn btn-sm gh-enhancer-copy';
btn.textContent = 'Copy';
btn.onclick = async () => {
const content = await fetchRawFileContent();
await navigator.clipboard.writeText(content);
btn.textContent = 'Copied!';
};
header.appendChild(btn);
}
Fetch and display repository size in the header:
async function displayRepoSize() {
const repo = extractRepoInfo();
const response = await fetch(`https://api.github.com/repos/${repo}`);
const data = await response.json();
const sizeKB = data.size * 1024;
const sizeDisplay = formatSize(sizeKB);
const header = document.querySelector('.repo-header');
const sizeEl = document.createElement('span');
sizeEl.className = 'gh-enhancer-repo-size';
sizeEl.textContent = `Size: ${sizeDisplay}`;
header.appendChild(sizeEl);
}
Add word-level diff highlighting in PR files:
function enhanceDiffView() {
document.querySelectorAll('.diff-table tbody tr').forEach(row => {
const additions = row.querySelector('.blob-code-addition');
const deletions = row.querySelector('.blob-code-deletion');
if (additions) additions.classList.add('gh-enhancer-diff-add');
if (deletions) deletions.classList.add('gh-enhancer-diff-del');
});
}
Implement keyboard shortcuts for common GitHub actions:
document.addEventListener('keydown', (e) => {
if (e.altKey && e.key === 'n') {
window.location.href += '/new';
}
if (e.altKey && e.key === 'i') {
window.location.href += '/issues/new';
}
});
Reference: Keyboard Shortcuts API
Create a settings popup to toggle features:
<!-- popup/popup.html -->
<!DOCTYPE html>
<html><body>
<h3>GitHub Enhancer Settings</h3>
<label><input type="checkbox" id="fileTree" checked> File Tree</label>
<label><input type="checkbox" id="copyButton" checked> Copy Button</label>
<label><input type="checkbox" id="repoSize" checked> Repo Size</label>
<label><input type="checkbox" id="enhancedDiff" checked> Enhanced Diff</label>
<script src="popup.js"></script>
</body></html>
// popup/popup.js
document.querySelectorAll('input').forEach(input => {
input.addEventListener('change', (e) => {
chrome.storage.sync.set({ [e.target.id]: e.target.checked });
});
});
GitHub uses Turbo/PJAX for navigation. Use MutationObserver to detect page changes:
const observer = new MutationObserver((mutations) => {
const pageType = detectPageType(window.location.href);
handlePageType(pageType);
});
observer.observe(document.body, {
childList: true,
subtree: true
});
Reference: DOM Observer Patterns, Dynamic Content Injection
Handle rate limits by caching responses and using auth tokens:
const cache = new Map();
async function fetchWithCache(url) {
if (cache.has(url)) return cache.get(url);
const response = await fetch(url);
if (response.status === 403) {
console.warn('API rate limit reached');
return null;
}
const data = await response.json();
cache.set(url, data);
return data;
}
Reference: Content Script Patterns
This tutorial covered building a GitHub UI enhancer with:
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.