Sebastian Gomez
How to Debug and Use the Runtime API in Chrome Extensions
Chrome extensions offer a wide range of functionality through their runtime API, which makes communication between different components of an extension easy. In this post we'll dig into how to debug and use this API to improve how your extensions work.
Important (current as of 2026): This chapter uses Manifest V3 (MV3). Chrome began disabling Manifest V2 in June 2024, and the phase out is now complete on stable channels, MV2 extensions no longer run and are no longer accepted in the Chrome Web Store. If you're coming from an older tutorial with manifest_version: 2, this is the updated version.
Why JavaScript Matters in Extension Development
Before we dive into debugging techniques and the runtime API, it's crucial to highlight how important a solid JavaScript foundation is. If you don't yet feel comfortable with JavaScript, I recommend pausing your extension work and spending time learning and mastering the language. Once you have a good grasp of JavaScript, working with Chrome extensions will be much easier.
Initial Setup: the Manifest File
The manifest.json file is the heart of any Chrome extension. Make sure it's configured correctly to avoid errors.
Example of a basic Manifest V3 configuration:
{
"manifest_version": 3,
"name": "My Chrome Extension",
"version": "1.0",
"permissions": ["storage", "tabs"],
"host_permissions": ["https://*/*"],
"background": {
"service_worker": "background.js"
},
"action": {
"default_popup": "popup.html",
"default_icon": "icon.png"
},
"options_page": "popup.html"
}Notice the key changes from MV2:
"manifest_version"is now 3."browser_action"was renamed to `"action"`.- The
backgroundno longer usesscripts/persistent: in MV3 it's a service worker ("service_worker": "background.js"). If you use ES modules, add"type": "module". - Host patterns (
https://*/*) go in `host_permissions`, notpermissions.
Tip: Request only the hosts you actually need. Broad patterns like <all_urls> or https://*/* trigger stricter Chrome Web Store review. For the Pipedrive example below, "host_permissions": ["https://*.pipedrive.com/*"] would be enough.
Using the Runtime API
Chrome's runtime API is essential for handling events and messages inside your extension. These APIs are unchanged in MV3.
Setting up an installation event: a common case is opening the options page when the extension is installed (remember to declare it with options_page or options_ui in the manifest, as above).
chrome.runtime.onInstalled.addListener(() => {
chrome.runtime.openOptionsPage();
});Sending messages between components: you can send messages between popup.js and background.js (the service worker) to handle complex interactions.
Example in popup.js:
document.getElementById('button').addEventListener('click', () => {
chrome.runtime.sendMessage({ greeting: 'hello' }, (response) => {
console.log(response.farewell);
});
});Example in background.js:
chrome.runtime.onMessage.addListener((request, sender, sendResponse) => {
if (request.greeting === 'hello') {
sendResponse({ farewell: 'goodbye' });
}
});Debugging the Extension
Debugging Chrome extensions can be tricky, but here are some useful tips:
Using the browser console: open Chrome's developer tools (Ctrl+Shift+I, or Cmd+Option+I on Mac) and go to the Console tab to see errors and debug messages. You can use console.log in your scripts to print out important messages and values.
Debugging the service worker: in MV3, background.js runs as a service worker and can "go to sleep" when idle. Go to chrome://extensions/, enable Developer mode, and click the "service worker" link on your extension to open its dedicated console. If it isn't responding, reload the extension from that same page to restart it.
Debugging `popup.html`: right click the popup and choose Inspect to open developer tools specifically for the popup. Use the Network tab to monitor requests and make sure the responses are what you expect.
Practical Example: Pipedrive Integration
Let's say we want to integrate our extension with Pipedrive to show leads and deals.
Setting up the options page. In popup.html:
<!DOCTYPE html>
<html>
<head>
<title>Options</title>
</head>
<body>
<label>API subdomain:</label>
<input type="text" id="subdomain" />
<label>API token:</label>
<input type="password" id="apikey" />
<button id="save">Save</button>
<script src="popup.js"></script>
</body>
</html>In popup.js:
document.getElementById('save').addEventListener('click', () => {
const subdomain = document.getElementById('subdomain').value;
const apikey = document.getElementById('apikey').value;
chrome.storage.local.set({ subdomain, apikey }, () => {
console.log('Data saved');
});
});Showing leads and deals. In background.js:
chrome.runtime.onInstalled.addListener(async () => {
const items = await chrome.storage.local.get(['subdomain', 'apikey']);
// Send the token in a header (Authorization), never in the URL:
// that keeps it out of logs, history, and proxies.
const response = await fetch(
`https://${items.subdomain}.pipedrive.com/api/v2/deals`,
{
headers: {
'x-api-token': items.apikey,
},
},
);
const data = await response.json();
console.log(data);
});About Pipedrive authentication: The old ?api_token=... query string pattern is legacy and discouraged: a key in the URL ends up leaking into logs and browser history. Pipedrive now recommends OAuth 2.0 for public apps and its v2 API; send the token in a header and store it securely (like in chrome.storage.local), never embedded in the URL.
Conclusion
Debugging and using Chrome's runtime API is crucial for building efficient, functional extensions. Make sure you have a solid JavaScript foundation, configure your manifest.json correctly on Manifest V3, and take advantage of Chrome's developer tools, especially the service worker console, to debug and improve your extension. With these techniques you'll be able to build powerful extensions and solve problems effectively.
I hope this post was useful and inspires you to explore more of what Chrome's APIs make possible. If you have questions or need help, don't hesitate to leave a comment. See you next time!
Sebastian Gomez
Creador de contenido principalmente acerca de tecnología.