Skip to content

Entrypoints

WXT uses the files inside the entrypoints/ directory as inputs when bundling your extension. They can be HTML, JS, CSS, or any variant of those file types supported by Vite (Pug, TS, JSX, SCSS, etc).

Here's an example set of entrypoints:

html
📂 entrypoints/
   📂 popup/
      📄 index.html
      📄 main.ts
      📄 style.css
   📄 background.ts
   📄 content.ts

Listed vs Unlisted

For web extensions, there are two types of entrypoints:

  • Listed: Referenced in the manifest.json
  • Unlisted: Not referenced in the manifest.json

Throughout the rest of WXT's documentation, listed files are referred to by name. For example:

  • Popup
  • Options
  • Background
  • Content Scripts
  • Etc.

Some examples of "unlisted" entrypoints:

  • A welcome page shown when the extension is installed
  • JS files injected by content scripts into the page's main world

TIP

Regardless of whether a entrypoint is listed or unlisted, it will still be bundled into your extension and be available at runtime.

Adding Entrypoints

An entrypoint can be defined as a single file or directory with an index file inside it.

html
📂 entrypoints/
   📄 background.ts
html
📂 entrypoints/
   📂 background/
      📄 index.ts

The entrypoint's name dictates the type of entrypoint, listed vs unlisted. In this example, "background" is the name of the "Background" entrypoint.

Refer to the Entrypoint Types section for the full list of listed entrypoints and their filename patterns.

Defining Manifest Options

Most listed entrypoints have options that need to be added to the manifest.json. With WXT however, instead of defining the options in a separate file, you define these options inside the entrypoint file itself.

For example, here's how to define matches for content scripts:

ts
// entrypoints/content.ts
export default defineContentScript({
  matches: ['*://*.wxt.dev/*'],
  main() {
    // ...
  },
});

Refer to the Entrypoint Types sections for a list of options configurable inside each entrypoint, and how to define them.

When building your extension, WXT will look at the options defined in your entrypoints, and generate the manifest accordingly.

Entrypoint Types

Background

Chrome DocsFirefox Docs

For MV2, the background is added as a script to the background page. For MV3, the background becomes a service worker.

FilenameOutput Path
entrypoints/background.[jt]s/background.js
entrypoints/background/index.[jt]s/background.js
ts
export default defineBackground(() => {
  // Executed when background is loaded
});
ts
export default defineBackground({
  // Set manifest options
  persistent: undefined | true | false,
  type: undefined | 'module',

  // Set include/exclude if the background should be removed from some builds
  include: undefined | string[],
  exclude: undefined | string[],

  main() {
    // Executed when background is loaded, CANNOT BE ASYNC
  },
});

Bookmarks

Chrome DocsFirefox Docs

FilenameOutput Path
entrypoints/bookmarks.html/bookmarks.html
entrypoints/bookmarks/index.html/bookmarks.html
html
<!doctype html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Title</title>
    <!-- Set include/exclude if the page should be removed from some builds -->
    <meta name="manifest.include" content="['chrome', ...]" />
    <meta name="manifest.exclude" content="['chrome', ...]" />
  </head>
  <body>
    <!-- ... -->
  </body>
</html>

Content Scripts

Chrome DocsFirefox Docs

See Content Script UI for more info on creating UIs and including CSS in content scripts.

FilenameOutput Path
entrypoints/content.[jt]sx?/content-scripts/content.js
entrypoints/content/index.[jt]sx?/content-scripts/content.js
entrypoints/<name>.content.[jt]sx?/content-scripts/<name>.js
entrypoints/<name>.content/index.[jt]sx?/content-scripts/<name>.js
ts
export default defineContentScript({
  // Set manifest options
  matches: string[],
  excludeMatches: undefined | [],
  includeGlobs: undefined | [],
  excludeGlobs: undefined | [],
  allFrames: undefined | true | false,
  runAt: undefined | 'document_start' | 'document_end' | 'document_idle',
  matchAboutBlank: undefined | true | false,
  matchOriginAsFallback: undefined | true | false,
  world: undefined | 'ISOLATED' | 'MAIN',

  // Set include/exclude if the background should be removed from some builds
  include: undefined | string[],
  exclude: undefined | string[],

  // Configure how CSS is injected onto the page
  cssInjectionMode: undefined | "manifest" | "manual" | "ui",

  // Configure how/when content script will be registered
  registration: undefined | "manifest" | "runtime",

  main(ctx: ContentScriptContext) {
    // Executed when content script is loaded, can be async
  },
});

Devtools

Chrome DocsFirefox Docs

Follow the Devtools Example to add different panels and panes.

FilenameOutput Path
entrypoints/devtools.html/devtools.html
entrypoints/devtools/index.html/devtools.html
html
<!doctype html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <!-- Set include/exclude if the page should be removed from some builds -->
    <meta name="manifest.include" content="['chrome', ...]" />
    <meta name="manifest.exclude" content="['chrome', ...]" />
  </head>
  <body>
    <!-- ... -->
  </body>
</html>

History

Chrome DocsFirefox Docs

FilenameOutput Path
entrypoints/history.html/history.html
entrypoints/history/index.html/history.html
html
<!doctype html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Title</title>
    <!-- Set include/exclude if the page should be removed from some builds -->
    <meta name="manifest.include" content="['chrome', ...]" />
    <meta name="manifest.exclude" content="['chrome', ...]" />
  </head>
  <body>
    <!-- ... -->
  </body>
</html>

Newtab

Chrome DocsFirefox Docs

FilenameOutput Path
entrypoints/newtab.html/newtab.html
entrypoints/newtab/index.html/newtab.html
html
<!doctype html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Title</title>
    <!-- Set include/exclude if the page should be removed from some builds -->
    <meta name="manifest.include" content="['chrome', ...]" />
    <meta name="manifest.exclude" content="['chrome', ...]" />
  </head>
  <body>
    <!-- ... -->
  </body>
</html>

Options

Chrome DocsFirefox Docs

FilenameOutput Path
entrypoints/options.html/options.html
entrypoints/options/index.html/options.html
html
<!doctype html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta
      name="viewportYou are responsible for loading/running these scripts where needed. If necessary, don't forget to add the script and/or any related stylesheets to web_accessible_resources."
      content="width=device-width, initial-scale=1.0"
    />
    <title>Options Title</title>
    <meta name="manifest.open_in_tab" content="true|false" />
    <meta name="manifest.chrome_style" content="true|false" />
    <meta name="manifest.browser_style" content="true|false" />
    <!-- Set include/exclude if the page should be removed from some builds -->
    <meta name="manifest.include" content="['chrome', ...]" />
    <meta name="manifest.exclude" content="['chrome', ...]" />
  </head>
  <body>
    <!-- ... -->
  </body>
</html>

Chrome DocsFirefox Docs

FilenameOutput Path
entrypoints/popup.html/popup.html
entrypoints/popup/index.html/popup.html
html
<!doctype html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Default Popup Title</title>
    <meta
      name="manifest.default_icon"
      content="{
        16: '/icon-16.png',
        24: '/icon-24.png',
        ...
      }"
    />
    <meta name="manifest.type" content="page_action|browser_action" />
    <meta name="manifest.browser_style" content="true|false" />
    <!-- Set include/exclude if the page should be removed from some builds -->
    <meta name="manifest.include" content="['chrome', ...]" />
    <meta name="manifest.exclude" content="['chrome', ...]" />
  </head>
  <body>
    <!-- ... -->
  </body>
</html>

Sandbox

Chrome Docs

Chromium Only

Firefox does not support sandboxed pages.

FilenameOutput Path
entrypoints/sandbox.html/sandbox.html
entrypoints/sandbox/index.html/sandbox.html
entrypoints/<name>.sandbox.html/<name>.html
entrypoints/<name>.sandbox/index.html/<name>.html
html
<!doctype html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Title</title>
    <!-- Set include/exclude if the page should be removed from some builds -->
    <meta name="manifest.include" content="['chrome', ...]" />
    <meta name="manifest.exclude" content="['chrome', ...]" />
  </head>
  <body>
    <!-- ... -->
  </body>
</html>

Side Panel

Chrome DocsFirefox Docs

In Chrome, side panels use the side_panel API, while Firefox uses the sidebar_action API.

FilenameOutput Path
entrypoints/sidepanel.html/sidepanel.html
entrypoints/sidepanel/index.html/sidepanel.html
entrypoints/<name>.sidepanel.html/<name>.html`
entrypoints/<name>.sidepanel/index.html/<name>.html`
html
<!doctype html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Default Side Panel Title</title>
    <meta
      name="manifest.default_icon"
      content="{
        16: '/icon-16.png',
        24: '/icon-24.png',
        ...
      }"
    />
    <meta name="manifest.open_at_install" content="true|false" />
    <meta name="manifest.browser_style" content="true|false" />
    <!-- Set include/exclude if the page should be removed from some builds -->
    <meta name="manifest.include" content="['chrome', ...]" />
    <meta name="manifest.exclude" content="['chrome', ...]" />
  </head>
  <body>
    <!-- ... -->
  </body>
</html>

Unlisted CSS

Follow Vite's guide to setup your preprocessor of choice: https://vitejs.dev/guide/features.html#css-pre-processors

CSS entrypoints are always unlisted. To add CSS to a content script, see the Content Script docs.

FilenameOutput Path
entrypoints/<name>.(css|scss|sass|less|styl|stylus)/<name>.css
entrypoints/<name>/index.(css|scss|sass|less|styl|stylus)/<name>.css
entrypoints/content.(css|scss|sass|less|styl|stylus)/content-scripts/content.css
entrypoints/content/index.(css|scss|sass|less|styl|stylus)/content-scripts/content.css
entrypoints/<name>.content.(css|scss|sass|less|styl|stylus)/content-scripts/<name>.css
entrypoints/<name>.content/index.(css|scss|sass|less|styl|stylus)/content-scripts/<name>.css
css
body {
  /* ... */
}

Unlisted Pages

FilenameOutput Path
entrypoints/<name>.html/<name>.html
entrypoints/<name>/index.html/<name>.html
html
<!doctype html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Title</title>
    <!-- Set include/exclude if the page should be removed from some builds -->
    <meta name="manifest.include" content="['chrome', ...]" />
    <meta name="manifest.exclude" content="['chrome', ...]" />
  </head>
  <body>
    <!-- ... -->
  </body>
</html>

Pages are accessible at /<name>.html:

ts
const url = browser.runtime.getURL('/<name>.html');

console.log(url); // "chrome-extension://<id>/<name>.html"

Unlisted Scripts

FilenameOutput Path
entrypoints/<name>.[jt]sx?/<name>.js
entrypoints/<name>/index.[jt]sx?/<name>.js
ts
export default defineUnlistedScript(() => {
  // Executed when script is loaded
});
ts
export default defineUnlistedScript({
  // Set include/exclude if the script should be removed from some builds
  include: undefined | string[],
  exclude: undefined | string[],

  main() {
    // Executed when script is loaded
  },
});

Scripts are accessible from /<name>.js:

ts
const url = browser.runtime.getURL('/<name>.js');

console.log(url); // "chrome-extension://<id>/<name>.js"

You are responsible for loading/running these scripts where needed. If necessary, don't forget to add the script and/or any related stylesheets to web_accessible_resources.