Introduction
Google's upcoming transition from Manifest V2 to Manifest V3 for Chrome extensions marks a significant shift in browser extension capabilities. This change affects content blockers like uBlock Origin, which will need to be updated to work with the new architecture. In this tutorial, you'll learn how to migrate an existing Manifest V2 extension to Manifest V3 and understand the key differences in implementation.
Prerequisites
- Basic understanding of JavaScript and web development
- Chrome browser installed (version 120+ recommended)
- Node.js installed for development tools
- Text editor or IDE (VS Code recommended)
- Familiarity with Chrome Extension development concepts
Step-by-Step Instructions
1. Create a New Extension Directory
First, set up a new directory for your extension project. This will contain all the necessary files for your extension.
mkdir manifest-v3-extension
cd manifest-v3-extension
This creates a clean workspace for your new extension, separating it from any existing projects.
2. Create the Manifest File (Manifest V3)
Create a manifest.json file with the Manifest V3 structure. This is the core configuration file that tells Chrome how to load and run your extension.
{
"manifest_version": 3,
"name": "Content Blocker Demo",
"version": "1.0",
"description": "A demo extension demonstrating Manifest V3 capabilities",
"permissions": [
"activeTab",
"storage"
],
"content_scripts": [
{
"matches": [""],
"js": ["content.js"]
}
],
"background": {
"service_worker": "background.js"
}
}
Notice the key differences from Manifest V2: manifest_version is 3, background now uses service_worker instead of scripts, and permissions are more granular.
3. Implement Content Script
Create a content.js file that will run on web pages. This script demonstrates how content scripts work in Manifest V3.
// content.js
console.log('Content script loaded');
// Example: Modify page content
const body = document.body;
if (body) {
body.style.backgroundColor = 'lightblue';
}
// Listen for messages from background script
chrome.runtime.onMessage.addListener((request, sender, sendResponse) => {
if (request.action === "modifyPage") {
document.body.style.color = request.color;
sendResponse({status: "success"});
}
});
This script runs in the context of web pages and can modify DOM elements, but has more restrictions than Manifest V2.
4. Create Background Service Worker
Create a background.js file that will act as your extension's service worker. This runs in the background and handles extension events.
// background.js
console.log('Background service worker started');
// Listen for extension installation
chrome.runtime.onInstalled.addListener(() => {
console.log('Extension installed');
// Set up initial storage
chrome.storage.sync.set({blockAds: true});
});
// Listen for messages from content script
chrome.runtime.onMessage.addListener((request, sender, sendResponse) => {
if (request.action === "getSettings") {
chrome.storage.sync.get(["blockAds"], (result) => {
sendResponse({blockAds: result.blockAds});
});
return true; // Keep message channel open for async response
}
});
// Listen for tab updates
chrome.tabs.onUpdated.addListener((tabId, changeInfo, tab) => {
if (changeInfo.status === 'complete' && tab.url) {
console.log(`Tab loaded: ${tab.url}`);
}
});
Service workers in Manifest V3 have different lifecycle management and cannot use chrome.tabs.executeScript directly like V2.
5. Add a Popup Interface
Create a popup.html file to provide user interaction with your extension.
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<style>
body { width: 200px; padding: 10px; }
button { width: 100%; margin: 5px 0; }
</style>
</head>
<body>
<h3>Content Blocker</h3>
<button id="toggleBlocker">Toggle Blocking</button>
<div id="status">Status: Loading...</div>
<script src="popup.js"></script>
</body>
</html>
This popup allows users to interact with your extension's functionality without needing to open developer tools.
6. Implement Popup Logic
Create a popup.js file to handle popup interactions.
// popup.js
console.log('Popup script loaded');
// Get current tab
chrome.tabs.query({active: true, currentWindow: true}, (tabs) => {
if (tabs.length > 0) {
const currentTab = tabs[0];
console.log('Current tab:', currentTab.url);
}
});
// Toggle blocker functionality
const toggleButton = document.getElementById('toggleBlocker');
// Load current settings
chrome.storage.sync.get(['blockAds'], (result) => {
const statusDiv = document.getElementById('status');
statusDiv.textContent = `Status: ${result.blockAds ? 'Blocking Enabled' : 'Blocking Disabled'}`;
});
// Handle toggle button click
toggleButton.addEventListener('click', () => {
chrome.storage.sync.get(['blockAds'], (result) => {
const newSetting = !result.blockAds;
chrome.storage.sync.set({blockAds: newSetting}, () => {
const statusDiv = document.getElementById('status');
statusDiv.textContent = `Status: ${newSetting ? 'Blocking Enabled' : 'Blocking Disabled'}`;
// Send message to content script
chrome.tabs.query({active: true, currentWindow: true}, (tabs) => {
if (tabs.length > 0) {
chrome.tabs.sendMessage(tabs[0].id, {action: "updateSettings", blockAds: newSetting});
}
});
});
});
});
The popup now provides a user interface to control your extension's behavior and communicates with the content script.
7. Test Your Extension
Open Chrome and navigate to chrome://extensions. Enable "Developer mode" and click "Load unpacked" to select your extension directory.
After loading, you'll see your extension in the extensions list. Click the extension icon to test the popup functionality.
8. Handle Manifest V3 Limitations
Manifest V3 has several limitations compared to V2. For example, you cannot use chrome.tabs.executeScript directly. Instead, use:
// Instead of chrome.tabs.executeScript
chrome.scripting.executeScript({
target: {tabId: tabId},
func: () => {
// Your script here
}
});
This change requires you to use the scripting permission in your manifest.
Summary
This tutorial demonstrated how to migrate from Manifest V2 to Manifest V3 for Chrome extensions. Key changes include using service workers instead of background pages, updating permissions, and using new APIs like chrome.scripting. Understanding these differences is crucial for maintaining compatibility as Google phases out Manifest V2 support.
Remember to test your extensions thoroughly after migration, as some functionality may need to be re-implemented using new APIs. The transition to Manifest V3 represents a more secure and performant extension architecture, but requires careful attention to the updated APIs and permission models.



