Chrome Extension Content Script Injection — Best Practices
3 min readContent Script Injection Patterns
Content scripts run in the context of web pages. Understanding the different injection methods is essential for building effective Chrome extensions.
Overview
There are three primary ways to inject content scripts:
- Static Injection: Declared in manifest.json, runs automatically on matching URLs
- Programmatic Injection: Injected on demand via the Scripting API
- Dynamic Registration: Registered at runtime but runs automatically on matching pages
Static Injection (Manifest)
Declare content scripts in manifest.json:
{
"content_scripts": [{
"matches": ["https://*.example.com/*"],
"js": ["content.js"],
"css": ["content.css"],
"run_at": "document_idle"
}]
}
Best for always-on functionality on specific sites—runs automatically without user interaction.
Programmatic Injection
Use the Scripting API for on-demand injection:
chrome.scripting.executeScript({
target: { tabId: 123 },
files: ['content.js']
});
Requires "scripting" permission plus host permission or activeTab. Best for user-triggered actions.
Dynamic Registration
Register scripts at runtime:
chrome.scripting.registerContentScripts([{
id: 'my-script',
matches: ['https://*.example.com/*'],
js: ['content.js'],
persistAcrossSessions: true
}]);
Ideal for user-configurable site matching. Unregister with:
chrome.scripting.unregisterContentScripts(['my-script']);
run_at Timing
| Value | Description |
|---|---|
"document_start" |
Before DOM constructed |
"document_idle" |
After DOMContentLoaded (default, recommended) |
"document_end" |
After DOM, before subresources |
World Isolation
- ISOLATED (default): Separate JS context, shared DOM, cannot access page variables
- MAIN: Page’s JS context, can access page variables, security risk
{
"content_scripts": [{
"matches": ["https://*.example.com/*"],
"js": ["content.js"],
"world": "MAIN"
}]
}
CSS Injection
Static: add "css": ["styles.css"] to content_scripts. Programmatic:
chrome.scripting.insertCSS({ target: { tabId: 123 }, css: 'body { }' });
chrome.scripting.removeCSS({ target: { tabId: 123 }, css: 'body { }' });
Injection Guards
Prevent duplicate injections using DOM markers:
if (document.body.hasAttribute('data-extension-injected')) return;
document.body.setAttribute('data-extension-injected', 'true');
Or check registered scripts:
chrome.scripting.getRegisteredContentScripts({ ids: ['my-script'] });
Best Practices
- Use static injection for always-on features
- Use programmatic injection for user-triggered actions
- Use dynamic registration for user-configurable matching
- Prefer ISOLATED world for security
- Implement injection guards to prevent duplicates
Cross-References
- Content Script Patterns Guide
- Dynamic Content Scripts (MV3)
- Scripting API Reference
- Content Script Isolation -e —
Part of the Chrome Extension Guide by theluckystrike. Built at zovo.one.