Entry Points
Entry points are the core building blocks of a browser extension. They define different components like background, popup, or content scripts that make up your extension.
Configuring Entry Points
You can configure entry points through the manifest option. WebExtend will parse it to find the entry points used in the extension.
For example:
import { defineConfig } from 'web-extend';
export default defineConfig({
manifest: {
background: {
service_worker: './src/background.ts',
},
content_scripts: [
{
matches: ['https://www.google.com/*'],
js: ['./src/content.ts'],
},
],
action: {
default_popup: './src/popup.ts',
},
},
});The manifest option also can be a function that returns a manifest object.
import { defineConfig } from 'web-extend';
export default defineConfig({
manifest: ({ target, mode }) => {
return {
// ...
};
},
});File-based Entry Points
Besides the manifest option, WebExtend also makes it easy to manage these entry points through a file-based convention system.
Why File-based Entry Points?
File-based entry points reduce boilerplate code and make your extension more maintainable. Instead of managing complex manifest configurations, you can focus on writing the actual extension code.
All entry points are located in the entriesDir (default is src) directory. Each entry point can be a folder or a file.
- When the entry point is a file, only files ending with
.js|.jsx|.ts|.tsxwill be discovered. The build tool injects an HTML template for each entry point, if necessary, and generates the corresponding HTML file.
src/
├─ background.ts -> entry point
├─ popup.ts -> entry point
└─ content.ts -> entry pointWhen the entry point is a folder, and that folder contains a single entry point, the
index.jsfile within that folder will be discovered as the entry point.When the entry point is a folder, and that folder contains multiple entry points, all the direct
*.jsor*/index.jsfiles within that folder will be discovered as entry points. Only files incontents,pages,sandboxesandscriptingfolders will be discovered as multiple entry points currently.
src/
├─ content/
| ├─ lib.ts
| ├─ index.css
| └─ index.ts -> entry point
└─ contents/
├─ content-one.ts -> entry point
└─ content-two/
├─ index.css
└─ index.ts -> entry pointEntry Generator
WebExtend comes with a generator to help you create entry points automatically.
npx web-extend g [entry...]
# Or use the short form
npx we g [entry...]Entry Types
Background
The background script runs in the extension's background context. The background entry point will be reflected to the background.service_worker or background.scripts field in manifest.json.
| Entry Path | Output Path |
|---|---|
background.(js|ts) | background.js |
background/index.(js|ts) | background.js |
Generate the entry automatically:
npx web-extend g backgroundExample usage:
chrome.runtime.onInstalled.addListener(() => {
console.log('Extension installed');
});See with-background for an example.
Bookmarks
Chrome Docs, Firefox doesn't support bookmarks.
The bookmarks entry will be reflected to the chrome_url_overrides.bookmarks field in manifest.json.
| Entry Path | Output Path |
|---|---|
bookmarks.(js|jsx|ts|tsx) | bookmarks.html |
bookmarks/index.(js|jsx|ts|tsx) | bookmarks.html |
Generate the entry automatically.
npx web-extend g bookmarksContent Scripts
Content scripts are JavaScript files that run in the context of web pages. They can read and modify the content of web pages that your extension interacts with.
Best Practices
- Use content scripts sparingly and only on necessary pages
- Avoid heavy operations that might slow down page load
- Consider using the
run_atoption to control when your script runs - Use Shadow DOM when possible to avoid style conflicts
| Entry Path | Output Path |
|---|---|
content.(js|jsx|ts|tsx) | content.js |
content/index.(js|jsx|ts|tsx) | content.js |
contents/{name}.(js|jsx|ts|tsx) | contents/{name}.js |
contents/{name}/index.(js|jsx|ts|tsx) | contents/{name}.js |
Adding content scripts
Content entries will be reflected to the content_scripts[index].js field in manifest.json respectively.
Generate the entry automatically:
# for a single content script
npx web-extend g content
# for multiple content scripts
npx web-extend g contents/site-one contents/site-twoAdding CSS
Import CSS files directly in the content entry file, which will be reflected to the content_scripts[index].css field in manifest.json.
CSS Scope
Be careful with CSS selectors in content scripts. They can conflict with the webpage's existing styles. Consider using:
- Specific class names with a unique prefix
- Shadow DOM for complete style isolation
- CSS Modules for scoped styling
For example:
.my-content-container {
position: fixed;
bottom: 20px;
right: 20px;
display: flex;
align-items: end;
z-index: 1000;
}
.my-content {
color: #000;
background-color: #fff;
margin-right: 8px;
border: 1px solid #ccc;
border-radius: 6px;
text-align: center;
padding: 12px;
}import './index.css';
let root = document.getElementById('myContent');
if (!root) {
root = document.createElement('div');
root.id = 'myContent';
root.innerHTML = `<div class="my-content-container">
<div class="my-content">
<p>This is a content script.</p>
</div>
</div>`;
document.body.appendChild(root);
}Using Shadow DOM
Shadow DOM provides better encapsulation for both DOM and styles. Here's how to use it:
import inlineStyles from './index.css?inline';
let host = document.getElementById('myContentHost');
if (!host) {
host = document.createElement('div');
host.id = 'myContentHost';
const shadow = host.attachShadow({ mode: 'open' });
const sheet = new CSSStyleSheet();
sheet.replaceSync(inlineStyles);
shadow.adoptedStyleSheets = [sheet];
const root = document.createElement('div');
root.innerHTML = `<div class="my-content-container">
<div class="my-content">
<p>This is a content script.</p>
</div>
</div>`;
shadow.appendChild(root);
document.body.appendChild(host);
}Adding Configuration
Export an object named config in the entry to configure how and where your content script runs. This will be reflected to other fields in content_scripts[index].
Common configuration options include:
matches: URL patterns where the script should runexclude_matches: URL patterns to excluderun_at: When to inject the script (document_start,document_end, ordocument_idle)all_frames: Whether to run in all frames
For example:
export const config = {
matches: ['https://www.google.com/*'],
run_at: 'document_end',
all_frames: false,
};import type { ContentScriptConfig } from 'web-extend';
export const config: ContentScriptConfig = {
matches: ['https://www.google.com/*'],
run_at: 'document_end',
all_frames: false,
};See with-content and with-multi-contents for examples.
Devtools
The devtools entry allows you to extend the browser's developer tools with custom functionality. This is perfect for creating debugging tools, performance analyzers, or specialized inspectors.
The devtools entry will be reflected to the devtools_page field in manifest.json.
| Entry Path | Output Path |
|---|---|
devtools.(js|jsx|ts|tsx) | devtools.html |
devtools/index.(js|jsx|ts|tsx) | devtools.html |
Generate the entry automatically:
npx web-extend g devtoolsExample usage:
chrome.devtools.panels.create('My Panel', '', 'panel.html');Adding panels
The devtools page is composed of a single panel or multiple panels.
Generate the panel entry automatically:
# create a single panel
npx web-extend g panel
# create multiple panels
npx web-extend g panels/panel1,panels/panel2See with-devtools for an example.
History
Chrome Docs, Firefox doesn't support history.
The history entry will be reflected to the chrome_url_overrides.history field in manifest.json.
| Entry Path | Output Path |
|---|---|
history.(js|jsx|ts|tsx) | history.html |
history/index.(js|jsx|ts|tsx) | history.html |
Generate the entry automatically:
npx web-extend g historyIcons
Create the assets/icon-{size}.png files under the src directory as follows, which will be reflected to the icons and action.default_icons fields in manifest.json.
src/assets/
├─ icon-16.png
├─ icon-32.png
├─ icon-48.png
└─ icon-128.pngAlternatively, you can use WebExtend's generator to automatically create icons in different sizes. The generator will read a template icon file from the src/assets directory (named icon.png, icon-1024.png, icon-512.png or icon-128.png) and generate resized versions at 16px, 32px, 48px and 128px. You can also specify custom sizes using the --size option:
# generate icons in default sizes
npx web-extend g icons
# generate icons in custom sizes
npx web-extend g icons --size 16 32 48 128See with-icons for an example.
Newtab
The new tab page replaces the browser's default new tab page. The new tab entry point will be reflected to the chrome_url_overrides.newtab field in manifest.json.
| Entry Path | Output Path |
|---|---|
newtab.(js|jsx|ts|tsx) | newtab.html |
newtab/index.(js|jsx|ts|tsx) | newtab.html |
Generate the entry automatically:
npx web-extend g newtabOptions
The options page provides a way for users to customize your extension. The options entry point will be reflected to the options_ui.page field in manifest.json.
| Entry Path | Output Path |
|---|---|
options.(js|jsx|ts|tsx) | options.html |
options/index.(js|jsx|ts|tsx) | options.html |
Generate the entry automatically:
npx web-extend g optionsSee with-options for an example.
Pages
Pages are HTML documents that are not listed in manifest.json, but can be accessed by your extension. They are useful in certain situations, such as serving as a welcome page shown in a new tab upon installation.
| Entry Path | Output Path |
|---|---|
pages/{name}.(js|jsx|ts|tsx) | pages/{name}.html |
pages/{name}/index.(js|jsx|ts|tsx) | pages/{name}.html |
Generate the entry automatically:
npx web-extend g pages/welcomePopup
The popup is a temporary UI that appears when users click your extension's icon. The popup entry will be reflected to the action.default_popup field in manifest.json.
| Entry Path | Output Path |
|---|---|
popup.(js|jsx|ts|tsx) | popup.html |
popup/index.(js|jsx|ts|tsx) | popup.html |
Generate the entry automatically:
npx web-extend g popupHere's a basic popup example using React:
import { StrictMode } from 'react';
import { createRoot } from 'react-dom/client';
import App from './App';
const rootEl = document.getElementById('root');
if (rootEl) {
const root = createRoot(rootEl);
root.render(
<StrictMode>
<App />
</StrictMode>,
);
}See with-popup for an example.
Sandbox
Chrome Docs, Firefox doesn't support sandbox.
The sandbox entry will be reflected to the sandbox.pages field in manifest.json.
| Entry Path | Output Path |
|---|---|
sandbox.(js|jsx|ts|tsx) | sandbox.html |
sandbox/index.(js|jsx|ts|tsx) | sandbox.html |
sandboxes/{name}.(js|jsx|ts|tsx) | sandboxes/{name}.html |
sandboxes/{name}/index.(js|jsx|ts|tsx) | sandboxes/{name}.html |
Generate the entry automatically.
# create a single entry
npx web-extend g sandbox
# create multiple entries
npx web-extend g sandboxes/sandbox1 sandboxes/sandbox2To use the sandbox, you can embed it as an iframe inside an extension page or a content script.
document.querySelector("#root").innerHTML = `
<div class="content">
<!-- embed a single sandbox -->
<iframe id="sandboxFrame1" src="sandbox.html"></iframe>
<!-- embed multiple sandboxes -->
<iframe id="sandboxFrame1" src="sandboxes/sandbox1.html"></iframe>
</div>
`;
}See with-sandbox and with-multi-sandboxes for examples.
Scripting
The scripting API allows you to inject JavaScript and CSS into web pages programmatically. This is different from content scripts as it provides more flexibility in when and where to inject the code.
| Entry Path | Output Path |
|---|---|
scripting/{name}.(js|jsx|ts|tsx) | scripting/{name}.js |
scripting/{name}.(css|less|sass|scss) | scripting/{name}.css |
Example usage:
chrome.tabs.onActivated.addListener((e) => {
chrome.scripting.executeScript({
target: { tabId: e.tabId },
files: ['scripting/injected-script.js'],
});
chrome.scripting.insertCSS({
target: { tabId: e.tabId },
files: ['scripting/injected-style.css'],
});
});See with-scripting for an example.
Side Panel
The side panel entry point will be reflected to the side_panel.default_path or sidebar_action.default_panel field in manifest.json.
| Entry Path | Output Path |
|---|---|
sidepanel.(js|jsx|ts|tsx) | sidepanel.html |
sidepanel/index.(js|jsx|ts|tsx) | sidepanel.html |
Generate the entry automatically:
npx web-extend g sidepanelSee with-sidepanel for an example.