diff --git a/README.md b/README.md
index 3808327c..295b4d77 100644
--- a/README.md
+++ b/README.md
@@ -70,7 +70,7 @@ Or see the [installation guide](https://wxt.dev/guide/installation.html) to get
- 🦾 Auto-imports
- 🤖 Automated publishing
- 🎨 Frontend framework agnostic: works with Vue, React, Svelte, etc
-- 📦 Modular architecture with [WXT modules](https://wxt.dev/guide/go-further/reusable-modules.html#overview)
+- 📦 [Module system](https://wxt.dev/guide/essentials/wxt-modules.html#overview) for reusing code between extensions
- 🖍️ Quickly bootstrap a new project
- 📏 Bundle analysis
- ⬇️ Download and bundle remote URL imports
diff --git a/docs/.old/development.md b/docs/.old/development.md
deleted file mode 100644
index 50098add..00000000
--- a/docs/.old/development.md
+++ /dev/null
@@ -1,116 +0,0 @@
-# Development
-
-WXT's main goal is providing the best DX it possibly can. When running your extension in dev mode, each part of your extension is reloaded separately when possible.
-
-| | HMR | Reloaded individually | Reload extension | Restart browser |
-| ------------------- | :-: | :-------------------: | :--------------: | :----------------------------------------------------: |
-| HTML File | | ✅ |
-| HTML Dependency | ✅ |
-| MV3 Content Script | | ✅ |
-| MV2 Content Script | | | ✅ |
-| Background | | | ✅ |
-| manifest.json | | | | 🟡 See [#16](https://github.com/wxt-dev/wxt/issues/16) |
-| `wxt.config.ts` | | | | 🟡 See [#10](https://github.com/wxt-dev/wxt/issues/10) |
-| `web-ext.config.ts` | | | | 🟡 See [#10](https://github.com/wxt-dev/wxt/issues/10) |
-
-## Dev Mode vs Production Builds
-
-There are some notable differences between the development and production versions of an extension. During development:
-
-1. **Content scripts are not listed in the `manifest.json`** when targeting MV3. Instead, the [`scripting`](https://developer.chrome.com/docs/extensions/reference/api/scripting) permission is used to register content scripts at runtime so they can be reloaded individually.
-
- To get the list of content scripts during development, run the following in the background's console:
-
- ```ts
- await chrome.scripting.getRegisteredContentScripts();
- ```
-
-2. **The CSP is modified to allow loading scripts from the dev server**. Make sure you're using Chrome v110 or above for HMR to work.
-
-3. If you don't include a background script/service worker, one will be created to perform various tasks in dev mode, mostly related to reloading different parts of the extension on change.
-
-For production builds, none of the above modifications will be applied, and you're extension/manifest will only include what you have defined.
-
-## Configure Browser Startup
-
-WXT uses [`web-ext` by Mozilla](https://github.com/mozilla/web-ext) to automatically open a browser with the extension installed. You can configure the runner's behavior via the [`runner`](/api/reference/wxt/interfaces/InlineConfig#runner) option, or in a separate gitignored file, `web-ext.config.ts`.
-
-:::code-group
-
-```ts [wxt.config.ts]
-import { defineConfig } from 'wxt';
-
-export default defineConfig({
- runner: {
- // Runner config
- },
-});
-```
-
-```ts [web-ext.config.ts]
-import { defineRunnerConfig } from 'wxt';
-
-export default defineRunnerConfig({
- // Runner config
-});
-```
-
-:::
-
-### Browser Binaries
-
-`web-ext`'s browser discovery is very limited. By default, it only guesses at where Chrome and Firefox are installed. If you've customized your install locations, you may need to tell `web-ext` where the binaries/executables are located using the [`binaries` option](/api/reference/wxt/interfaces/ExtensionRunnerConfig#binaries). For other Chromium based browsers, like Edge or Opera, you'll need to explicitly list them in the `binaries` option as well, otherwise they will open in Chrome by default.
-
-```ts
-// ~/web-ext.config.ts
-import { defineRunnerConfig } from 'wxt';
-
-export default defineRunnerConfig({
- binaries: {
- chrome: '/path/to/chrome-beta', // Use Chrome Beta instead of regular Chrome
- firefox: 'firefoxdeveloperedition', // Use Firefox Developer Edition instead of regular Firefox
- edge: '/path/to/edge', // Open MS Edge when running "wxt -b edge"
- },
-});
-```
-
-:::tip
-When configuring browser binaries, it's helpful to put them in `~/web-ext.config.ts` instead of the project directory's `web-ext.config.ts` file. When placed in your home directory (`~/`), this config will be used by all WXT projects, so you only need to configure the binaries once.
-:::
-
-### Other options
-
-You can customize other options as well, like startup URLs, profiles, or additional command line arguments:
-
-```ts
-// web-ext.config.ts
-import { defineRunnerConfig } from 'wxt';
-
-export default defineRunnerConfig({
- startUrls: ['https://google.com', 'https://duckduckgo.com'],
- chromiumProfile: '/path/to/profile/to/use',
- chromiumArgs: ['--window-size=400,300'],
-});
-```
-
-For a full list of options, see the [API Reference](/api/reference/wxt/interfaces/ExtensionRunnerConfig).
-
-## Reload the Extension
-
-Normally, to manually reload an extension, you have to visit `chrome://extensions` and click the reload button for your extension.
-
-When running `wxt` command to start the dev server, WXT adds a keyboard shortcut `Alt+R`, that reloads the extension when pressed, without visiting `chrome://extensions`. This can also be customized or disabled:
-
-```ts [wxt.config.ts]
-import { defineConfig } from 'wxt';
-
-export default defineConfig({
- dev: {
- reloadCommand: 'Alt+T', // false, to disable
- },
-});
-```
-
-:::info
-This shortcut is only available during development, and is not be added to your extension when running `wxt build` or `wxt-zip`.
-:::
diff --git a/docs/.vitepress/components/EntrypointPatterns.vue b/docs/.vitepress/components/EntrypointPatterns.vue
index 0600a066..8f73d7c2 100644
--- a/docs/.vitepress/components/EntrypointPatterns.vue
+++ b/docs/.vitepress/components/EntrypointPatterns.vue
@@ -8,14 +8,14 @@ const props = defineProps<{
-
Input Pattern
+
Filename
Output Path
-
+
entrypoints/{{ pattern[0] }}
@@ -31,7 +31,7 @@ const props = defineProps<{
/>
-
+
/{{ pattern[1] }}
diff --git a/docs/.vitepress/config.ts b/docs/.vitepress/config.ts
index 33a97536..87f398dc 100644
--- a/docs/.vitepress/config.ts
+++ b/docs/.vitepress/config.ts
@@ -8,6 +8,9 @@ import {
prepareTypedocSidebar,
} from './utils/menus';
import { meta, script } from './utils/head';
+import { version as wxtVersion } from '../../packages/wxt/package.json';
+import { version as i18nVersion } from '../../packages/i18n/package.json';
+import { version as autoIconsVersion } from '../../packages/auto-icons/package.json';
const title = 'Next-gen Web Extension Framework';
const titleSuffix = ' – WXT';
@@ -64,111 +67,93 @@ export default defineConfig({
],
nav: [
- navItem('Get Started', '/get-started/introduction'),
- navItem('Guide', '/guide/key-concepts/manifest'),
- navItem('API', '/api/reference/wxt'),
+ navItem('Guide', '/guide/installation'),
navItem('Examples', '/examples'),
+ navItem('API', '/api/reference/wxt'),
+ navItem(`v${wxtVersion}`, [
+ navItem('wxt', [
+ navItem(`v${wxtVersion}`, '/'),
+ navItem(
+ `Changelog`,
+ 'https://github.com/wxt-dev/wxt/blob/main/packages/wxt/CHANGELOG.md',
+ ),
+ ]),
+ navItem('Other Packages', [
+ navItem(`wxt/storage — ${wxtVersion}`, '/storage'),
+ navItem(`@wxt-dev/auto-icons — ${autoIconsVersion}`, '/auto-icons'),
+ navItem(`@wxt-dev/i18n — ${i18nVersion}`, '/i18n'),
+ ]),
+ ]),
],
sidebar: {
- '/get-started/': menuRoot([
- menuGroup('Get Started', '/get-started/', [
- menuItem('Introduction', 'introduction'),
- menuItem('Installation', 'installation'),
- menuItem('Configuration', 'configuration'),
- menuItem('Entrypoints', 'entrypoints'),
- menuItem('Assets', 'assets'),
- menuItem('Publishing', 'publishing'),
- menuItem('Migrate to WXT', 'migrate-to-wxt'),
- menuItem('Compare', 'compare'),
- ]),
- ]),
'/guide/': menuRoot([
- menuGroup('Key Concepts', '/guide/key-concepts/', [
- menuItem('Manifest', 'manifest'),
- menuItem('Auto-imports', 'auto-imports'),
- menuItem('Web Extension Polyfill', 'web-extension-polyfill'),
- menuItem('Frontend Frameworks', 'frontend-frameworks'),
- menuItem('Content Script UI', 'content-script-ui'),
- ]),
- menuGroup('Directory Structure', '/guide/directory-structure/', [
- // Folders
- menuItem('.output/', 'output'),
- menuItem('.wxt/', 'wxt'),
- menuItem('assets/', 'assets'),
- menuItem('components/', 'components'),
- menuItem('composables/', 'composables'),
- menuGroup('entrypoints/', '/guide/directory-structure/entrypoints/', [
- menuItem('background', 'background.md'),
- menuItem('bookmarks', 'bookmarks.md'),
- menuItem('*.content.ts', 'content-scripts.md'),
- menuItem('*.css', 'css.md'),
- menuItem('devtools', 'devtools.md'),
- menuItem('history', 'history.md'),
- menuItem('newtab', 'newtab.md'),
- menuItem('options', 'options.md'),
- menuItem('popup', 'popup.md'),
- menuItem('sandbox', 'sandbox.md'),
- menuItem('sidepanel', 'sidepanel.md'),
- menuItem('*.html', 'unlisted-pages.md'),
- menuItem('*.ts', 'unlisted-scripts.md'),
- ]),
- menuItem('hooks/', 'hooks'),
- menuItem('public/', 'public/', [
- menuItem('_locales/', 'public/locales'),
- ]),
- menuItem('utils/', 'utils'),
-
- // Files
- menuItem('.env', 'env'),
- menuItem('app.config.ts', 'app-config'),
- menuItem('package.json', 'package'),
- menuItem('tsconfig.json', 'tsconfig'),
- menuItem('web-ext.config.ts', 'web-ext-config'),
- menuItem('wxt.config.ts', 'wxt-config'),
- ]),
- menuGroup('Extension APIs', '/guide/extension-apis/', [
- menuItem('Storage', 'storage'),
- menuItem('Messaging', 'messaging'),
- menuItem('I18n', 'i18n'),
- menuItem('Scripting', 'scripting'),
- menuItem('Others', 'others'),
- ]),
- menuGroup('Go Further', '/guide/go-further/', [
- menuItem('Testing', 'testing'),
- menuItem('ES Modules', 'es-modules'),
- menuItem('Debugging', 'debugging'),
- menuItem('Handling Updates', 'handling-updates'),
- menuItem('Vite', 'vite'),
- menuItem('Custom Events', 'custom-events'),
- menuItem('Reusable Modules', 'reusable-modules'),
- menuItem('Remote Code', 'remote-code'),
- menuItem('Entrypoint Loaders', 'entrypoint-loaders'),
- menuItem('How WXT Works', 'how-wxt-works'),
- ]),
- menuGroup('Upgrade Guide', '/guide/upgrade-guide/', [
- menuItem('wxt', 'wxt'),
- ]),
- menuGroup('@wxt-dev/i18n', '/guide/i18n/', [
+ menuGroup('Get Started', '/guide/', [
menuItem('Introduction', 'introduction.md'),
menuItem('Installation', 'installation.md'),
- menuItem('Messages File Format', 'messages-file-format.md'),
- menuItem('Build Integrations', 'build-integrations.md'),
- menuItem('Editor Support', 'editor-support.md'),
+ ]),
+ menuGroup('Essentials', '/guide/essentials/', [
+ menuItem('Project Structure', 'project-structure.md'),
+ menuItem('Entrypoints', 'entrypoints.md'),
+ menuGroup(
+ 'Configuration',
+ '/guide/essentials/config/',
+ [
+ menuItem('Manifest', 'manifest.md'),
+ menuItem('Browser Startup', 'browser-startup.md'),
+ menuItem('Auto-imports', 'auto-imports.md'),
+ menuItem('Environment Variables', 'environment-variables.md'),
+ menuItem('Runtime Config', 'runtime.md'),
+ menuItem('Vite', 'vite.md'),
+ menuItem('Build Mode', 'build-mode.md'),
+ menuItem('TypeScript', 'typescript.md'),
+ menuItem('Hooks', 'hooks.md'),
+ menuItem('Entrypoint Loaders', 'entrypoint-loaders.md'),
+ ],
+ true,
+ ),
+ menuItem('Extension APIs', 'extension-apis.md'),
+ menuItem('Assets', 'assets.md'),
+ menuItem('Target Different Browsers', 'target-different-browsers.md'),
+ menuItem('Content Scripts', 'content-scripts.md'),
+ menuItem('Storage', 'storage.md'),
+ menuItem('Messaging', 'messaging.md'),
+ menuItem('I18n', 'i18n.md'),
+ menuItem('Scripting', 'scripting.md'),
+ menuItem('WXT Modules', 'wxt-modules.md'),
+ menuItem('Frontend Frameworks', 'frontend-frameworks.md'),
+ menuItem('ES Modules', 'es-modules.md'),
+ menuItem('Remote Code', 'remote-code.md'),
+ menuItem('Unit Testing', 'unit-testing.md'),
+ menuItem('E2E Testing', 'e2e-testing.md'),
+ menuItem('Publishing', 'publishing.md'),
+ menuItem('Testing Updates', 'testing-updates.md'),
+ ]),
+ menuGroup('Resources', '/guide/resources/', [
+ menuItem('Compare', 'compare.md'),
+ menuItem('FAQ', 'faq.md'),
+ menuItem('Upgrading WXT', 'upgrading.md'),
+ menuItem('Migrate to WXT', 'migrate.md'),
+ menuItem('How WXT Works', 'how-wxt-works.md'),
]),
]),
'/api/': menuRoot([
- menuGroup('CLI', '/api/cli/', [
- menuItem('wxt', 'wxt.md'),
- menuItem('wxt build', 'wxt-build.md'),
- menuItem('wxt zip', 'wxt-zip.md'),
- menuItem('wxt prepare', 'wxt-prepare.md'),
- menuItem('wxt clean', 'wxt-clean.md'),
- menuItem('wxt init', 'wxt-init.md'),
- menuItem('wxt submit', 'wxt-submit.md'),
- menuItem('wxt submit init', 'wxt-submit-init.md'),
- ]),
- menuGroup('API Reference', prepareTypedocSidebar(typedocSidebar)),
+ menuGroup(
+ 'CLI Reference',
+ '/api/cli/',
+ [
+ menuItem('wxt', 'wxt.md'),
+ menuItem('wxt build', 'wxt-build.md'),
+ menuItem('wxt zip', 'wxt-zip.md'),
+ menuItem('wxt prepare', 'wxt-prepare.md'),
+ menuItem('wxt clean', 'wxt-clean.md'),
+ menuItem('wxt init', 'wxt-init.md'),
+ menuItem('wxt submit', 'wxt-submit.md'),
+ menuItem('wxt submit init', 'wxt-submit-init.md'),
+ ],
+ true,
+ ),
+ menuGroup('API Reference', prepareTypedocSidebar(typedocSidebar), true),
]),
},
},
diff --git a/docs/.vitepress/theme/custom.css b/docs/.vitepress/theme/custom.css
index 1d249f5a..a7aa5980 100644
--- a/docs/.vitepress/theme/custom.css
+++ b/docs/.vitepress/theme/custom.css
@@ -85,23 +85,6 @@ body {
user-select: none;
}
-.VPSidebarItem.level-0.collapsible {
- padding-bottom: 0.5rem;
-}
-
-.VPSidebarItem.level-0.collapsible .items {
- /*border-left: 1px solid var(--vp-c-divider);*/
-}
-
-.VPSidebarItem.level-1 {
- padding-left: 0.75rem;
-}
-
-.VPSidebar .group + .group {
- border-top: none;
- padding-top: 0;
-}
-
.VPSidebarItem .badge {
display: inline-block;
min-width: 1.6em;
diff --git a/docs/.vitepress/utils/menus.ts b/docs/.vitepress/utils/menus.ts
index aad00c56..8395cabb 100644
--- a/docs/.vitepress/utils/menus.ts
+++ b/docs/.vitepress/utils/menus.ts
@@ -2,9 +2,20 @@ import { DefaultTheme } from 'vitepress';
type SidebarItem = DefaultTheme.SidebarItem;
type NavItem = DefaultTheme.NavItem;
+type NavItemWithLink = DefaultTheme.NavItemWithLink;
+type NavItemWithChildren = DefaultTheme.NavItemWithChildren;
+type NavItemChildren = DefaultTheme.NavItemChildren;
-export function navItem(text: string, link: string): NavItem {
- return { text, link };
+export function navItem(text: string): NavItemChildren;
+export function navItem(text: string, link: string): NavItemChildren;
+export function navItem(text: string, items: any[]): NavItemWithChildren;
+export function navItem(text: string, arg2?: unknown): any {
+ if (typeof arg2 === 'string') {
+ return { text, link: arg2 };
+ } else if (Array.isArray(arg2)) {
+ return { text, items: arg2 };
+ }
+ return { text };
}
export function menuRoot(items: SidebarItem[]) {
@@ -14,31 +25,39 @@ export function menuRoot(items: SidebarItem[]) {
});
}
-export function menuGroup(text: string, items: SidebarItem[]): SidebarItem;
+export function menuGroup(
+ text: string,
+ items: SidebarItem[],
+ collapsable?: boolean,
+): SidebarItem;
export function menuGroup(
text: string,
base: string,
items: SidebarItem[],
+ collapsable?: boolean,
): SidebarItem;
export function menuGroup(
text: string,
a: string | SidebarItem[],
- b?: SidebarItem[],
+ b?: SidebarItem[] | boolean,
+ c?: boolean,
): SidebarItem {
- const collapsed = true;
- if (typeof a === 'string') {
+ if (typeof a === 'string' && Array.isArray(b)) {
return {
text,
base: a,
items: b,
- collapsed,
+ collapsed: c,
};
}
- return {
- text,
- items: a,
- collapsed,
- };
+ if (typeof a !== 'string' && !Array.isArray(b))
+ return {
+ text,
+ items: a,
+ collapsed: b,
+ };
+
+ throw Error('Unknown overload');
}
export function menuItems(items: SidebarItem[]) {
diff --git a/docs/assets/init-demo.gif b/docs/assets/init-demo.gif
index 474db34c..353c8b73 100644
Binary files a/docs/assets/init-demo.gif and b/docs/assets/init-demo.gif differ
diff --git a/docs/auto-icons.md b/docs/auto-icons.md
new file mode 100644
index 00000000..474e842c
--- /dev/null
+++ b/docs/auto-icons.md
@@ -0,0 +1 @@
+
diff --git a/docs/get-started/assets.md b/docs/get-started/assets.md
deleted file mode 100644
index f8ef639a..00000000
--- a/docs/get-started/assets.md
+++ /dev/null
@@ -1,33 +0,0 @@
-# Assets
-
-WXT has two directories for storing assets like CSS, images, or fonts.
-
-- [public directory](/guide/directory-structure/public/): Store files that will be copied into the output directory as-is
-- [assets directory](/guide/directory-structure/assets): Store files that will be processed by Vite during the build process
-
-To learn more about how to use assets at runtime from either of these directories, visit their guides linked above.
-
-## Public Directory
-
-Place static files like the extension icon or `_locales/` directory here. These files will be copied over to the output directory without being transformed by Vite.
-
-```
-
-└─ public/
- ├─ icon-16.png
- ├─ icon-32.png
- ├─ icon-48.png
- ├─ icon-96.png
- └─ icon-128.png
-```
-
-## Assets Directory
-
-Files in the assets directory will be processed by Vite. They are imported in your source code, and will be transformed or renamed in the output directory.
-
-```
-
-└─ assets/
- ├─ tailwind.css
- └─ illustration.svg
-```
diff --git a/docs/get-started/compare.md b/docs/get-started/compare.md
deleted file mode 100644
index 37e9e9ea..00000000
--- a/docs/get-started/compare.md
+++ /dev/null
@@ -1,47 +0,0 @@
-# Compare
-
-Lets compare the features of WXT vs [Plasmo](https://docs.plasmo.com/framework) (another framework) and [CRXJS](https://crxjs.dev/vite-plugin) (a bundler plugin).
-
-## Overview
-
-| Features | WXT | Plasmo | CRXJS |
-| ---------------------------------------------------- | :--------------: | :-------------: | :--------------: |
-| Supports all browsers | ✅ | ✅ | 🟡 10 |
-| MV2 Support | ✅ | ✅ | 🟡 1 |
-| MV3 Support | ✅ | ✅ | 🟡 1 |
-| Create Extension ZIPs | ✅ | ✅ | ❌ |
-| Create Firefox Sources ZIP | ✅ | ❌ | ❌ |
-| First-class TypeScript support | ✅ | ✅ | ✅ |
-| Entrypoint discovery | ✅ 2 | ✅ 2 | ❌ |
-| Inline entrypoint config | ✅ | ✅ | ❌ 9 |
-| Auto-imports | ✅ | ❌ | ❌ |
-| Supports all frontend frameworks | ✅ | 🟡 3 | ✅ |
-| Framework specific entrypoints (like `Popup.tsx`) | 🟡 4 | ✅ 5 | ❌ |
-| Automated publishing | ✅ | ✅ | ❌ |
-| Remote Code Bundling (Google Analytics) | ✅ | ✅ | ❌ |
-| Dev Mode | | |
-| `.env` Files | ✅ | ✅ | ✅ |
-| Opens browser with extension installed | ✅ | ❌ | ❌ |
-| HMR for UIs | ✅ | 🟡 6 | ✅ |
-| Reload HTML Files on Change | ✅ | 🟡 7 | ✅ |
-| Reload Content Scripts on Change | ✅ | 🟡 7 | ✅ |
-| Reload Background on Change | 🟡 7 | 🟡 7 | 🟡 7 |
-| Respects Content Script `run_at` | ✅ | ✅ | ❌ 8 |
-| Built-in Utils | | | |
-| Storage | ✅ | ✅ | ❌ 11 |
-| Messaging | ❌ 11 | ✅ | ❌ 11 |
-| Content Script UI | ✅ | ✅ | ❌ 11 |
-
-
- 1: Either MV2 or MV3, not both.
- 2: File based.
- 3: Only React, Vue, and Svelte.
- 4: .html.ts.tsx.
- 5: .html.ts.tsx. .vue.svelte.
- 6: React only.
- 7: Reloads entire extension.
- 8: ESM-style loaders run asynchronously.
- 9: Entrypoint options all configured in `manifest.json`.
- 10: As of v2.0.0-beta.23, but v2 stable hasn't been released yet.
- 11: There is no built-in wrapper around this API. However, you can still access the standard APIs via chrome/browser globals or use any 3rd party NPM package.
-
diff --git a/docs/get-started/configuration.md b/docs/get-started/configuration.md
deleted file mode 100644
index 04cb62ef..00000000
--- a/docs/get-started/configuration.md
+++ /dev/null
@@ -1,57 +0,0 @@
-# Configuration
-
-By default, WXT provides sensible configuration for bundling web extensions with Vite.
-
-## Config File
-
-To configure WXT, create a `wxt.config.ts` file in your project root. It should have the following contents:
-
-```ts
-import { defineConfig } from 'wxt';
-
-export default defineConfig({
- // My WXT config
-});
-```
-
-:::info
-For more information on configuring WXT via the `wxt.config.ts` file, read the dedicated [`wxt.config.ts` guide](/guide/directory-structure/wxt-config).
-:::
-
-## Manifest.json
-
-WXT generates your extension's `manifest.json` based on the project structure. To add additional properties, like permissions, use the [`manifest` property](/api/reference/wxt/interfaces/InlineConfig#manifest).
-
-```ts
-import { defineConfig } from 'wxt';
-
-export default defineConfig({
- manifest: {
- permissions: ['storage'],
- },
-});
-```
-
-:::info
-For more information on configuring the manifest, read the dedicated [Manifest guide](/guide/key-concepts/manifest).
-:::
-
-## Environment
-
-WXT can read `.env` files, and variables are accessible via `import.meta.env.*`.
-
-:::code-group
-
-```sh [.env]
-VITE_OAUTH_CLIENT_ID=abc123
-```
-
-```ts [JS]
-import.meta.env.VITE_OAUTH_CLIENT_ID;
-```
-
-:::
-
-:::info
-For more information on using .env files, read the dedicated [`.env` guide](/guide/directory-structure/env).
-:::
diff --git a/docs/get-started/entrypoints.md b/docs/get-started/entrypoints.md
deleted file mode 100644
index 65b7bf08..00000000
--- a/docs/get-started/entrypoints.md
+++ /dev/null
@@ -1,60 +0,0 @@
-# Entrypoints
-
-An "entrypoint" is any HTML, JS, or CSS file that needs to be bundled and included with your extension, which will be loaded and executed by the browser.
-
-## Defining Entrypoints
-
-In WXT, you create an entrypoint by adding a file to the `entrypoints/` directory.
-
-```
-
-└─ entrypoints/
- ├─ background.ts
- ├─ content.ts
- ├─ injected.ts
- └─ popup.html
-```
-
-Some entrypoint filesname patterns are reserved by WXT and effect how the manifest is generated.
-
-- `popup` adds an `action` to the manifest
-- `background` adds a background script/service worker
-- `*.content.ts` adds a content script
-- ...
-
-> For a full list of recognized filenames, see the the [Entrypoints Directory guide](/guide/directory-structure/entrypoints/background).
-
-Any other files, whether JS, CSS, or HTML, is considered "unlisted". Unlisted files, like `injected.ts` from above, are just bundled to the output directory and not added to the manifest. You can still access or load them at runtime.
-
-## Entrypoint Options
-
-Most entrypoints allow customizing their options in the file you define them in. This differs from regular web extension development, where all options are placed in the `manifest.json`.
-
-WXT looks for custom options in the entrypoint, and adds them to the manifest when generated.
-
-In HTML files, options are listed as `meta` tags:
-
-```html
-
-
-
-
-
-
-```
-
-In TS files, options are apart of the file's default export:
-
-```ts
-export default defineContentScript({
- matches: ['*://*.google.com/*'],
- runAt: 'document_start',
- main() {
- // ...
- },
-});
-```
-
-:::info
-All options for each entrypoint type is listed in the [entrypoints directory docs](/guide/directory-structure/entrypoints/background).
-:::
diff --git a/docs/get-started/installation.md b/docs/get-started/installation.md
deleted file mode 100644
index 7bf4765c..00000000
--- a/docs/get-started/installation.md
+++ /dev/null
@@ -1,120 +0,0 @@
-# Installation
-
-Bootstrap a new project, start from scratch, or [migrate an existing project](/get-started/migrate-to-wxt).
-
-## Bootstrap Project
-
-:::code-group
-
-```sh [pnpm]
-pnpm dlx wxt@latest init
-```
-
-```sh [npm]
-npx wxt@latest init
-```
-
-```sh [bun]
-# The "wxt init" command currently fails when ran with bunx.
-# Use NPX as a workaround, and select "bun" as your package
-# manager. To stay up to date with this issue, follow
-# https://github.com/wxt-dev/wxt/issues/707
-#
-# bunx wxt@latest init
-
-npx wxt@latest init
-```
-
-### Demo
-
-
-
-:::
-
-There are several starting templates available.
-
-| TypeScript |
-| ------------------------------------------------------------------------------------------------------------------------------------------------------- |
-| [`vanilla`](https://github.com/wxt-dev/wxt/tree/main/templates/vanilla) |
-| [`vue`](https://github.com/wxt-dev/wxt/tree/main/templates/vue) |
-| [`react`](https://github.com/wxt-dev/wxt/tree/main/templates/react) |
-| [`svelte`](https://github.com/wxt-dev/wxt/tree/main/templates/svelte) |
-| [`solid`](https://github.com/wxt-dev/wxt/tree/main/templates/solid) |
-
-:::info
-All templates default to TypeScript. Rename the file extensions to `.js` to use JavaScript instead.
-:::
-
-## From Scratch
-
-Initialize a project and install `wxt`:
-
-:::code-group
-
-```sh [pnpm]
-pnpm init
-pnpm add -D wxt
-```
-
-```sh [npm]
-npm init
-npm i --save-dev wxt
-```
-
-```sh [yarn]
-yarn init
-yarn add --dev wxt
-```
-
-```sh [bun]
-bun init
-bun add --dev wxt
-```
-
-:::
-
-Add your first entrypoint:
-
-```ts
-// entrypoints/background.ts
-export default defineBackground(() => {
- console.log(`Hello from ${browser.runtime.id}!`);
-});
-```
-
-And add scripts to your `package.json`:
-
-```json
-{
- "scripts": {
- "dev": "wxt", // [!code ++]
- "dev:firefox": "wxt --browser firefox", // [!code ++]
- "build": "wxt build", // [!code ++]
- "build:firefox": "wxt build --browser firefox", // [!code ++]
- "zip": "wxt zip", // [!code ++]
- "zip:firefox": "wxt zip --browser firefox", // [!code ++]
- "postinstall": "wxt prepare" // [!code ++]
- }
-}
-```
-
-## Development
-
-Once the project is setup, you can start the development server using the `dev` script.
-
-```sh
-pnpm dev
-```
-
-:::tip 🎉 Well done!
-The dev command will build the extension for development, open the browser, and reload the different parts of the extension when you save changes.
-:::
-
-## Next Steps
-
-You're ready to build your web extension!
-
-- Read the rest of the "Get Started" pages for a high-overview of what WXT can do
-- Read the [Guide](/guide/key-concepts/manifest) to learn in-depth about each feature WXT supports
-- [Configure WXT](./configuration) by creating a `wxt.config.ts` file
-- Checkout [example projects](https://github.com/wxt-dev/examples) to see how to perform common tasks with WXT
diff --git a/docs/get-started/introduction.md b/docs/get-started/introduction.md
deleted file mode 100644
index 1a66b8c6..00000000
--- a/docs/get-started/introduction.md
+++ /dev/null
@@ -1,47 +0,0 @@
----
-head:
- - - link
- - rel: canonical
- href: https://wxt.dev
----
-
-# Introduction
-
-## Overview
-
-WXT is a free and open source framework for building web extensions in a conventional, intuitive, and safe way **_for all browsers_**.
-
-WXT is based on [Nuxt](https://nuxt.com), and aims to provide the same great DX with TypeScript, auto-imports, and an opinionated project structure.
-
-
-
-## Conventions
-
-WXT is an opinionated framework. This helps keep projects consistent and easy to pick up.
-
-- **Generated manifest**: Based on your project's file structure
-- **Entrypoint configuration**: Configure entrypoints from the same file they're declare in
-- **Type-safety is a priority**: Out-of-the-box TypeScript support with improved browser API typing
-- **Simple output file structure**: Output file paths minimize the path at runtime
-
-## Development
-
-WXT's dev server supports modern features like HMR to provide a lighting fast dev mode.
-
-When changes can't be hot-reloaded, like content scripts or background scripts, they're reloaded individually to prevent reloading the entire extension and slowing down your development cycle.
-
-## Production-ready
-
-Production builds are optimized for store review, changing as few files as possible between builds.
-
-In addition, WXT fully supports Firefox's source code requirements when using a bundler. It will automatically create and upload a ZIP file of your source code.
-
-:::info
-See [Publishing](./publishing) for more info around production builds.
-:::
-
-## New to Extension Development?
-
-Most of these docs assume you have a basic understanding of how to write a chrome or web extension.
-
-If you've never written a web extension before or need a refresher, follow Google's ["Hello, World!" tutorial](https://developer.chrome.com/docs/extensions/get-started/tutorial/hello-world) to understand the basics.
diff --git a/docs/guide/directory-structure/app-config.md b/docs/guide/directory-structure/app-config.md
deleted file mode 100644
index e5c2582c..00000000
--- a/docs/guide/directory-structure/app-config.md
+++ /dev/null
@@ -1,61 +0,0 @@
-# `/app.config.ts`
-
-:::warning Nuxt Users
-If you're familiar with Nuxt, this file is meant to be a direct equivalent to Nuxt's `app.config.ts` file.
-
-However, some of Nuxt's features, like overriding the app config based on a `.env` file or automatically generating the config's types, are not implemented. They are planned, just not implemented yet. Feel free to open a PR!
-:::
-
-## Overview
-
-Define runtime configuration in a single place.
-
-```ts
-// /app.config.ts
-import { defineAppConfig } from 'wxt/sandbox';
-
-// Define types for your config
-declare module 'wxt/sandbox' {
- export interface WxtAppConfig {
- theme?: 'light' | 'dark';
- }
-}
-
-export default defineAppConfig({
- theme: 'dark',
-});
-```
-
-Then access the config in your extension by calling `useAppConfig`:
-
-```ts
-console.log(useAppConfig()); // { theme: "dark" }
-```
-
-## Environment Variables
-
-If you have a `.env` file, you can access any variables defined in it here. You can convert them to better types (like booleans), add types for them, or leave them as is.
-
-```txt
-# .env
-VITE_BUG_REPORTING_DISABLED=true
-VITE_API_KEY=...
-```
-
-```ts
-// /app.config.ts
-
-declare module 'wxt/sandbox' {
- export interface WxtAppConfig {
- bugReportingDisabled: boolean;
- apiKey?: string;
- }
-}
-
-export default defineAppConfig({
- bugReportingDisabled: import.meta.env.VITE_BUG_REPORTING_DISABLED === 'true',
- apiKey: import.meta.env.VITE_API_KEY,
-});
-```
-
-> You don't have to do this, you can use `import.meta.env.VITE_*` anywhere in your runtime code, but putting them here consolidates them to one place and defines what variables are expected.
diff --git a/docs/guide/directory-structure/assets.md b/docs/guide/directory-structure/assets.md
deleted file mode 100644
index f28bac1a..00000000
--- a/docs/guide/directory-structure/assets.md
+++ /dev/null
@@ -1,42 +0,0 @@
-# `/assets`
-
-Files in the assets directory will be processed by Vite. They are imported in your source code, and will be transformed or renamed in the output directory.
-
-```
-
-└─ assets/
- ├─ style.css
- └─ illustration.svg
-```
-
-### Example
-
-:::code-group
-
-```html [popup.html]
-
-
-
-
-
-
-
-
-
-
-```
-
-```ts [content.ts]
-import '~/assets/style.css';
-import illustration from '~/assets/style.svg';
-
-defineContentScript({
- main() {
- const image = document.createElement('img');
- image.src = illustration;
- document.body.append(image);
- },
-});
-```
-
-:::
diff --git a/docs/guide/directory-structure/components.md b/docs/guide/directory-structure/components.md
deleted file mode 100644
index 0bb2f0b2..00000000
--- a/docs/guide/directory-structure/components.md
+++ /dev/null
@@ -1,5 +0,0 @@
-# `/components`
-
-:::warning 🚧 Under construction
-These docs will be coming soon!
-:::
diff --git a/docs/guide/directory-structure/composables.md b/docs/guide/directory-structure/composables.md
deleted file mode 100644
index c1ada3ec..00000000
--- a/docs/guide/directory-structure/composables.md
+++ /dev/null
@@ -1,5 +0,0 @@
-# `/composables`
-
-:::warning 🚧 Under construction
-These docs will be coming soon!
-:::
diff --git a/docs/guide/directory-structure/entrypoints/background.md b/docs/guide/directory-structure/entrypoints/background.md
deleted file mode 100644
index b5df3dd9..00000000
--- a/docs/guide/directory-structure/entrypoints/background.md
+++ /dev/null
@@ -1,47 +0,0 @@
-# Background
-
-[Chrome Docs](https://developer.chrome.com/docs/extensions/mv3/manifest/background/) • [Firefox Docs](https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/WebExtensions/manifest.json/background)
-
-For MV2, the background is added as a script to the background page. For MV3, the background becomes a service worker.
-
-## Filenames
-
-
-
-## Definition
-
-:::warning
-The main function of the background **_CANNOT BE ASYNC_**. Event listeners must be added synchronously on background startup. If your main function returns a promise, WXT will log an error.
-:::
-
-```ts
-export default defineBackground(() => {
- // Executed when background is loaded
-});
-```
-
-or
-
-```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[],
-
- // Executed when background is loaded
- main() {
- // ...
- },
-});
-```
-
-> All manifest options default to `undefined`.
diff --git a/docs/guide/directory-structure/entrypoints/bookmarks.md b/docs/guide/directory-structure/entrypoints/bookmarks.md
deleted file mode 100644
index 10434f3c..00000000
--- a/docs/guide/directory-structure/entrypoints/bookmarks.md
+++ /dev/null
@@ -1,31 +0,0 @@
-# Bookmarks
-
-[Chrome Docs](https://developer.chrome.com/docs/extensions/mv3/override/) • [Firefox Docs](https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/WebExtensions/manifest.json/chrome_url_overrides)
-
-## Filenames
-
-
-
-## Definition
-
-```html
-
-
-
-
-
- Title
-
-
-
-
-
-
-
-
-```
diff --git a/docs/guide/directory-structure/entrypoints/content-scripts.md b/docs/guide/directory-structure/entrypoints/content-scripts.md
deleted file mode 100644
index 5ad58331..00000000
--- a/docs/guide/directory-structure/entrypoints/content-scripts.md
+++ /dev/null
@@ -1,107 +0,0 @@
-# Content Scripts
-
-[Chrome Docs](https://developer.chrome.com/docs/extensions/mv3/content_scripts/) • [Firefox Docs](https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/WebExtensions/Content_scripts)
-
-When creating content script entrypoints, they are automatically included in the `manifest.json` along with any CSS files they import.
-
-## Filenames
-
-
-
-## Definition
-
-```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
- },
-});
-```
-
-> All manifest options default to `undefined`.
-
-When defining multiple content scripts, content script entrypoints that have the same set of options will be merged into a single `content_script` item in the manifest.
-
-## CSS
-
-To include CSS with your content script, import the CSS file at the top of your entrypoint.
-
-```
-
-/
-└─ entrypoints/
- └─ overlay.content/
- ├─ index.ts
- └─ style.css
-```
-
-```ts
-// entrypoints/overlay.content/index.ts
-import './style.css';
-
-export default defineContentScript({
- matches: ['*://google.com/*', '*://duckduckgo.com/*'],
-
- main(ctx) {
- // ...
- },
-});
-```
-
-Any styles imported in your content script will be added to that content script's `css` array in your `manifest.json`:
-
-```json
-// .output/chrome-mv3/manifest.json
-{
- "content_scripts": [
- {
- "matches": ["*://google.com/*", "*://duckduckgo.com/*"],
- "js": ["content-scripts/overlay.js"],
- "css": ["content-scripts/overlay.css"]
- }
- ]
-}
-```
-
-To disable this behavior, set `cssInjectionMode` to `"manual"` or `"ui"`.
-
-```ts
-export default defineContentScript({
- matches: ['*://google.com/*', '*://duckduckgo.com/*'],
- cssInjectionMode: 'manual',
-
- main(ctx) {
- // ...
- },
-});
-```
-
-See [Content Script UI](/guide/key-concepts/content-script-ui) for more info on creating UIs and including CSS in content scripts.
diff --git a/docs/guide/directory-structure/entrypoints/css.md b/docs/guide/directory-structure/entrypoints/css.md
deleted file mode 100644
index 0e38c46c..00000000
--- a/docs/guide/directory-structure/entrypoints/css.md
+++ /dev/null
@@ -1,44 +0,0 @@
-# CSS
-
-WXT can build CSS entrypoints individually. CSS entrypoints are always unlisted.
-
-See [Content Script CSS](/guide/directory-structure/entrypoints/content-scripts#css) documentation for the recommended approach to include CSS with a content script.
-
-:::info
-If the recommended approach doesn't work for your use case, you can use any of the filename patterns below to build the styles separate from the JS and use the [`transformManifest` hook](/api/reference/wxt/interfaces/InlineConfig#transformmanifest) to manually add your CSS file to the manifest.
-:::
-
-## Filenames
-
-
-
-## Definition
-
-```css
-body {
- /* Plain CSS file */
-}
-```
-
-Follow Vite's guide to setup a preprocessor: https://vitejs.dev/guide/features.html#css-pre-processors
-
-```sh
-pnpm i sass
-```
-
-```scss
-body {
- h1 {
- /* ...*/
- }
-}
-```
diff --git a/docs/guide/directory-structure/entrypoints/devtools.md b/docs/guide/directory-structure/entrypoints/devtools.md
deleted file mode 100644
index da3cda04..00000000
--- a/docs/guide/directory-structure/entrypoints/devtools.md
+++ /dev/null
@@ -1,40 +0,0 @@
-# Devtools
-
-[Chrome Docs](https://developer.chrome.com/docs/extensions/mv3/devtools/) • [Firefox Docs](https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/WebExtensions/manifest.json/devtools_page)
-
-## Filenames
-
-
-
-## Definition
-
-```html
-
-
-
-
-
-
-
-
-
-
-
-
-
-```
-
-## Adding UI Elements
-
-Chrome extensions allow you to add panels and side panes to the devtools window.
-
-
-
-See the WXT's examples for a full walkthrough of extending the devtools window:
-
-- [Devtools Setup](https://github.com/wxt-dev/wxt-examples/tree/main/examples/vanilla-devtools#readme)
diff --git a/docs/guide/directory-structure/entrypoints/history.md b/docs/guide/directory-structure/entrypoints/history.md
deleted file mode 100644
index 9fc11a1b..00000000
--- a/docs/guide/directory-structure/entrypoints/history.md
+++ /dev/null
@@ -1,31 +0,0 @@
-# History
-
-[Chrome Docs](https://developer.chrome.com/docs/extensions/mv3/override/) • [Firefox Docs](https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/WebExtensions/manifest.json/chrome_url_overrides)
-
-## Filenames
-
-
-
-## Definition
-
-```html
-
-
-
-
-
- Title
-
-
-
-
-
-
-
-
-```
diff --git a/docs/guide/directory-structure/entrypoints/newtab.md b/docs/guide/directory-structure/entrypoints/newtab.md
deleted file mode 100644
index e15dcb2b..00000000
--- a/docs/guide/directory-structure/entrypoints/newtab.md
+++ /dev/null
@@ -1,31 +0,0 @@
-# Newtab
-
-[Chrome Docs](https://developer.chrome.com/docs/extensions/mv3/override/) • [Firefox Docs](https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/WebExtensions/manifest.json/chrome_url_overrides)
-
-## Filenames
-
-
-
-## Definition
-
-```html
-
-
-
-
-
- Title
-
-
-
-
-
-
-
-
-```
diff --git a/docs/guide/directory-structure/entrypoints/options.md b/docs/guide/directory-structure/entrypoints/options.md
deleted file mode 100644
index f4af0271..00000000
--- a/docs/guide/directory-structure/entrypoints/options.md
+++ /dev/null
@@ -1,36 +0,0 @@
-# Options
-
-[Chrome Docs](https://developer.chrome.com/docs/extensions/mv3/options/) • [Firefox Docs](https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/WebExtensions/manifest.json/options_ui)
-
-## Filenames
-
-
-
-## Definition
-
-```html
-
-
-
-
-
- Options Title
-
-
-
-
-
-
-
-
-
-
-
-```
-
-> All manifest options default to `undefined` when the `meta` tag is not present.
diff --git a/docs/guide/directory-structure/entrypoints/popup.md b/docs/guide/directory-structure/entrypoints/popup.md
deleted file mode 100644
index 536742e4..00000000
--- a/docs/guide/directory-structure/entrypoints/popup.md
+++ /dev/null
@@ -1,43 +0,0 @@
-# Popup
-
-[Chrome Docs](https://developer.chrome.com/docs/extensions/reference/action/) • [Firefox Docs](https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/WebExtensions/manifest.json/action)
-
-## Filenames
-
-
-
-## Definition
-
-```html
-
-
-
-
-
- Default Popup Title
-
-
-
-
-
-
-
-
-
-
-
-```
-
-> All manifest options default to `undefined` when the `meta` tag is not present.
diff --git a/docs/guide/directory-structure/entrypoints/sandbox.md b/docs/guide/directory-structure/entrypoints/sandbox.md
deleted file mode 100644
index 886c64dc..00000000
--- a/docs/guide/directory-structure/entrypoints/sandbox.md
+++ /dev/null
@@ -1,37 +0,0 @@
-# Sandbox
-
-[Chrome Docs](https://developer.chrome.com/docs/extensions/mv3/manifest/sandbox/)
-
-:::tip Chromium Only
-Firefox does not support sandboxed pages.
-:::
-
-## Filenames
-
-
-
-## Definition
-
-```html
-
-
-
-
-
- Title
-
-
-
-
-
-
-
-
-```
diff --git a/docs/guide/directory-structure/entrypoints/sidepanel.md b/docs/guide/directory-structure/entrypoints/sidepanel.md
deleted file mode 100644
index 8e61a07c..00000000
--- a/docs/guide/directory-structure/entrypoints/sidepanel.md
+++ /dev/null
@@ -1,49 +0,0 @@
-# Side Panel
-
-[Chrome Docs](https://developer.chrome.com/docs/extensions/reference/sidePanel/) • [Firefox Docs](https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/WebExtensions/user_interface/Sidebars)
-
-In Chrome, side panels use the "side_panel" API, while Firefox uses the "sidebar_action" API.
-
-:::warning
-Chrome added support for sidepanels in Manifest V3, they are not available in Manifest V2.
-:::
-
-## Filenames
-
-
-
-## Definition
-
-```html
-
-
-
-
-
- Default Side Panel Title
-
-
-
-
-
-
-
-
-
-
-
-```
diff --git a/docs/guide/directory-structure/entrypoints/unlisted-pages.md b/docs/guide/directory-structure/entrypoints/unlisted-pages.md
deleted file mode 100644
index c9812e99..00000000
--- a/docs/guide/directory-structure/entrypoints/unlisted-pages.md
+++ /dev/null
@@ -1,49 +0,0 @@
-# Unlisted Pages
-
-HTML pages that are bundled and shipped with the extension, but are not included in the manifest.
-
-If you plan on using the page in an iframe, don't forget to add the page to [`web_accessible_resources`](https://developer.chrome.com/docs/extensions/reference/manifest/web-accessible-resources).
-
-### Examples
-
-- Onboarding
-- Dashboard
-- FAQ
-- Help
-- Changelog
-
-## Filenames
-
-
-
-Pages are accessible at `'/.html'`:
-
-```ts
-const url = browser.runtime.getURL('/.html', '');
-
-console.log(url); // "chrome-extension:///.html"
-```
-
-## Definition
-
-```html
-
-
-
-
-
- Title
-
-
-
-
-
-
-
-
-```
diff --git a/docs/guide/directory-structure/entrypoints/unlisted-scripts.md b/docs/guide/directory-structure/entrypoints/unlisted-scripts.md
deleted file mode 100644
index 89a2da53..00000000
--- a/docs/guide/directory-structure/entrypoints/unlisted-scripts.md
+++ /dev/null
@@ -1,37 +0,0 @@
-# Unlisted Scripts
-
-TypeScript files that are bundled and shipped with the extension, but are not included in the manifest.
-
-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`](https://developer.chrome.com/docs/extensions/reference/manifest/web-accessible-resources).
-
-## Filenames
-
-
-
-## Definition
-
-```ts
-export default defineUnlistedScript(() => {
- // Executed when script is loaded
-});
-```
-
-or
-
-```ts
-export default defineUnlistedScript({
- // Set include/exclude if the script should be removed from some builds
- include: undefined | string[],
- exclude: undefined | string[],
-
- // Executed when script is loaded
- main() {
- // ...
- },
-});
-```
diff --git a/docs/guide/directory-structure/env.md b/docs/guide/directory-structure/env.md
deleted file mode 100644
index 6a17fbce..00000000
--- a/docs/guide/directory-structure/env.md
+++ /dev/null
@@ -1,5 +0,0 @@
-# `/.env`
-
-:::warning 🚧 Under construction
-These docs will be coming soon!
-:::
diff --git a/docs/guide/directory-structure/hooks.md b/docs/guide/directory-structure/hooks.md
deleted file mode 100644
index e6e19836..00000000
--- a/docs/guide/directory-structure/hooks.md
+++ /dev/null
@@ -1,5 +0,0 @@
-# `/hooks`
-
-:::warning 🚧 Under construction
-These docs will be coming soon!
-:::
diff --git a/docs/guide/directory-structure/output.md b/docs/guide/directory-structure/output.md
deleted file mode 100644
index bb5ef417..00000000
--- a/docs/guide/directory-structure/output.md
+++ /dev/null
@@ -1,5 +0,0 @@
-# `/.output`
-
-:::warning 🚧 Under construction
-These docs will be coming soon!
-:::
diff --git a/docs/guide/directory-structure/package.md b/docs/guide/directory-structure/package.md
deleted file mode 100644
index 79f23b57..00000000
--- a/docs/guide/directory-structure/package.md
+++ /dev/null
@@ -1,5 +0,0 @@
-# `/package.json`
-
-:::warning 🚧 Under construction
-These docs will be coming soon!
-:::
diff --git a/docs/guide/directory-structure/public/index.md b/docs/guide/directory-structure/public/index.md
deleted file mode 100644
index 765e1a44..00000000
--- a/docs/guide/directory-structure/public/index.md
+++ /dev/null
@@ -1,35 +0,0 @@
-# `public/`
-
-Place static files like the extension icon or `_locales/` directory here. These files will be copied over to the output directory without being transformed by Vite.
-
-```
-
-└─ public/
- ├─ icon-16.png
- ├─ icon-32.png
- ├─ icon-48.png
- ├─ icon-96.png
- └─ icon-128.png
-```
-
-### Example
-
-You can reference these files by using absolute paths in HTML files or `browser.runtime.getURL` in content scripts.
-
-:::code-group
-
-```html [popup.html]
-
-```
-
-```ts [content.ts]
-defineContentScript({
- main() {
- const image = document.createElement('img');
- image.src = browser.runtime.getURL('/icon-128.png');
- document.body.append(image);
- },
-});
-```
-
-:::
diff --git a/docs/guide/directory-structure/public/locales.md b/docs/guide/directory-structure/public/locales.md
deleted file mode 100644
index 36a57303..00000000
--- a/docs/guide/directory-structure/public/locales.md
+++ /dev/null
@@ -1,5 +0,0 @@
-# `public/_locales/`
-
-:::warning 🚧 Under construction
-These docs will be coming soon!
-:::
diff --git a/docs/guide/directory-structure/tsconfig.md b/docs/guide/directory-structure/tsconfig.md
deleted file mode 100644
index 81220e42..00000000
--- a/docs/guide/directory-structure/tsconfig.md
+++ /dev/null
@@ -1,5 +0,0 @@
-# `/tsconfig.json`
-
-:::warning 🚧 Under construction
-These docs will be coming soon!
-:::
diff --git a/docs/guide/directory-structure/utils.md b/docs/guide/directory-structure/utils.md
deleted file mode 100644
index 54cd86e5..00000000
--- a/docs/guide/directory-structure/utils.md
+++ /dev/null
@@ -1,5 +0,0 @@
-# `/utils`
-
-:::warning 🚧 Under construction
-These docs will be coming soon!
-:::
diff --git a/docs/guide/directory-structure/web-ext-config.md b/docs/guide/directory-structure/web-ext-config.md
deleted file mode 100644
index 88b33744..00000000
--- a/docs/guide/directory-structure/web-ext-config.md
+++ /dev/null
@@ -1,65 +0,0 @@
-# `web-ext.config.ts`
-
-This file lets you configure the browser startup when running `wxt dev`.
-
-```ts
-import { defineRunnerConfig } from 'wxt';
-
-export default defineRunnerConfig({
- startUrls: ['https://google.com', 'https://youtube.com'],
-});
-```
-
-There are three places you can customize the runner:
-
-- `/wxt.config.ts` - Use the `runner` option. Changes here will be committed and shared with everyone developing the project.
-- `/web-ext.config.ts` - A gitignored file for you to customize the startup behavior to your liking without effecting others
-- `$HOME/web-ext.config.ts` - Stores system-wide config effecting all projects running on your machine.
-
-See below examples on how to accomplish common configuration:
-
-[[toc]]
-
-## Configuring Binaries
-
-`web-ext`'s browser discovery is very limited. By default, it only guesses at where Chrome and Firefox are installed. If you've customized your install locations, you may need to tell `web-ext` where the binaries/executables are located using the [`binaries` option](/api/reference/wxt/interfaces/ExtensionRunnerConfig#binaries). For other Chromium based browsers, like Edge or Opera, you'll need to explicitly list them in the `binaries` option as well, otherwise they will open in Chrome by default.
-
-```ts
-import { defineRunnerConfig } from 'wxt';
-
-export default defineRunnerConfig({
- binaries: {
- chrome: '/path/to/chrome-beta', // Use Chrome Beta instead of regular Chrome
- firefox: 'firefoxdeveloperedition', // Use Firefox Developer Edition instead of regular Firefox
- edge: '/path/to/edge', // Open MS Edge when running "wxt -b edge"
- },
-});
-```
-
-## Disable Opening Browser
-
-Disabling the browser can be useful if it's difficult to develop your extension with fresh profiles. Maybe you need to sign into a website to see a content script run, and a fresh profile isn't helpful because it doesn't save your login info.
-
-To disable opening the extension automatically in a new window, just disable the runner:
-
-```ts
-export default defineRunnerConfig({
- disabled: true,
-});
-```
-
-## Profile Customization
-
-Another option, instead of disabling the runner, to stay logged into websites is to use a custom profile.
-
-`web-ext` comes with some built-in ways of using an existing profile, but it's not really using the same profile. It copies the profile to a temp directory, and uses that.
-
-Instead, I've found it's better to pass Chrome's `--user-data-dir` argument. This let's you use a fresh profile initially, and customize it to your liking. You can install devtool extensions, set custom flags, and log into websites. Next time you run the extension in dev mode, all that will be remembered!
-
-```ts
-export default defineRunnerConfig({
- chromiumArgs: ['--user-data-dir=./chrome-data'],
-});
-```
-
-> This only works for Chrome. You'll have to use `firefoxProfile` option instead, which has the same limitations mentioned above, where you won't be signed into websites automatically.
diff --git a/docs/guide/directory-structure/wxt-config.md b/docs/guide/directory-structure/wxt-config.md
deleted file mode 100644
index 30c0e831..00000000
--- a/docs/guide/directory-structure/wxt-config.md
+++ /dev/null
@@ -1,5 +0,0 @@
-# `/wxt.config.ts`
-
-:::warning 🚧 Under construction
-These docs will be coming soon!
-:::
diff --git a/docs/guide/directory-structure/wxt.md b/docs/guide/directory-structure/wxt.md
deleted file mode 100644
index 557ce148..00000000
--- a/docs/guide/directory-structure/wxt.md
+++ /dev/null
@@ -1,5 +0,0 @@
-# `.wxt/`
-
-:::warning 🚧 Under construction
-These docs will be coming soon!
-:::
diff --git a/docs/guide/essentials/assets.md b/docs/guide/essentials/assets.md
new file mode 100644
index 00000000..29638fd1
--- /dev/null
+++ b/docs/guide/essentials/assets.md
@@ -0,0 +1,82 @@
+# Assets
+
+## `/assets` Directory
+
+Any assets imported or referenced inside the `/assets/` directory will be processed by WXT's bundler.
+
+Here's how you access them:
+
+:::code-group
+
+```ts [JS]
+import imageUrl from '~/assets/image.png';
+
+const img = document.createElement('img');
+img.src = imageUrl;
+```
+
+```html [HTML]
+
+```
+
+```css [CSS]
+.bg-image {
+ background-image: url(~/assets/image.png);
+}
+```
+
+:::
+
+## `/public` Directory
+
+Files inside `/public/` are copied into the output folder as-is, without being processed by WXT's bundler.
+
+Here's how you access them:
+
+:::code-group
+
+```ts [JS]
+import imageUrl from '/image.png';
+
+const img = document.createElement('img');
+img.src = imageUrl;
+```
+
+```html [HTML]
+
+```
+
+```css [CSS]
+.bg-image {
+ background-image: url(/image.png);
+}
+```
+
+:::
+
+## Inside Content Scripts
+
+Assets inside content scripts are a little different. By default, when you import an asset, it returns just the path to the asset. This is because Vite assumes you're loading assets from the same hostname.
+
+But, inside content scripts, the hostname is whatever the tab is set to. So if you try to fetch the asset, manually or as an ``'s `src`, it will be loaded from the tab's website, not your extension.
+
+To fix this, you need to convert the image to a full URL using `browser.runtime.getURL`:
+
+```ts
+// entrypoints/content.ts
+import iconUrl from '/icon/128.png';
+
+export default defineContentScript({
+ matches: ['*://*.google.com/*'],
+ main() {
+ console.log(iconUrl); // "/icon/128.png"
+ console.log(browser.runtime.getURL(iconUrl)); // "chrome-extension:///icon/128.png"
+ },
+});
+```
+
+## WASM
+
+:::warning 🚧 Under construction
+These docs will be coming soon!
+:::
diff --git a/docs/guide/essentials/config/auto-imports.md b/docs/guide/essentials/config/auto-imports.md
new file mode 100644
index 00000000..92aca6aa
--- /dev/null
+++ b/docs/guide/essentials/config/auto-imports.md
@@ -0,0 +1,112 @@
+# Auto-imports
+
+WXT uses [`unimport`](https://www.npmjs.com/package/unimport), the same tool as Nuxt, to setup auto-imports.
+
+```ts
+export default defineConfig({
+ // See https://www.npmjs.com/package/unimport#configurations
+ imports: {
+ // ...
+ },
+});
+```
+
+By default, WXT automatically setups up auto-imports for all of it's own APIs:
+
+- [`browser`](/api/reference/wxt/browser/variables/browser) from `wxt/browser`
+- [`defineContentScript`](/api/reference/wxt/sandbox/functions/defineContentScript) from `wxt/sandbox`
+- [`defineBackground`](/api/reference/wxt/sandbox/functions/defineBackground) from `wxt/sandbox`
+- [`defineUnlistedScript`](/api/reference/wxt/sandbox/functions/defineUnlistedScript) from `wxt/sandbox`
+- [`createIntegratedUi`](/api/reference/wxt/client/functions/createIntegratedUi) from `wxt/client`
+- [`createShadowRootUi`](/api/reference/wxt/client/functions/createShadowRootUi) from `wxt/client`
+- [`createIframeUi`](/api/reference/wxt/client/functions/createIframeUi) from `wxt/client`
+- [`fakeBrowser`](/api/reference/wxt/testing/variables/fakeBrowser) from `wxt/testing`
+- And more!
+
+WXT also adds some project directories as auto-import sources automatically:
+
+- `/components/*`
+- `/composables/*`
+- `/hooks/*`
+- `/utils/*`
+
+All named and default exports from files in these directories are available everywhere else in your project without having to import them.
+
+## TypeScript
+
+For TypeScript and your editor to recognize auto-imported variables, you need to run the [`wxt prepare` command](/api/cli/wxt-prepare).
+
+Add this command to your `postinstall` script so your editor has everything it needs to report type errors after installing dependencies:
+
+```jsonc
+// package.json
+{
+ "scripts": {
+ "postinstall": "wxt prepare", // [!code ++]
+ },
+}
+```
+
+## ESLint
+
+ESLint doesn't know about the auto-imported variables unless they are explicitly defined in the ESLint's `globals`. By default, WXT will generate the config if it detects ESLint is installed in your project. If the config isn't generated automatically, you can manually tell WXT to generate it.
+
+:::code-group
+
+```ts [ESLint 9]
+export default defineConfig({
+ imports: {
+ eslintrc: {
+ enabled: 9,
+ },
+ },
+});
+```
+
+```ts [ESLint 8]
+export default defineConfig({
+ imports: {
+ eslintrc: {
+ enabled: 8,
+ },
+ },
+});
+```
+
+:::
+
+Then in your ESLint config, import and use the generated file:
+
+:::code-group
+
+```js [ESLint 9]
+// eslint.config.mjs
+import autoImports from './.wxt/eslint-auto-imports.mjs';
+
+export default [
+ autoImports,
+ {
+ // The rest of your config...
+ },
+];
+```
+
+```js [ESLint 8]
+// .eslintrc.mjs
+export default {
+ extends: ['./.wxt/eslintrc-auto-import.json'],
+ // The rest of your config...
+};
+```
+
+:::
+
+## Disabling Auto-imports
+
+Not all developers like auto-imports. To disable them, set `imports` to `false`.
+
+```ts
+export default defineConfig({
+ imports: false, // [!code ++]
+});
+```
diff --git a/docs/guide/essentials/config/browser-startup.md b/docs/guide/essentials/config/browser-startup.md
new file mode 100644
index 00000000..676d5e22
--- /dev/null
+++ b/docs/guide/essentials/config/browser-startup.md
@@ -0,0 +1,85 @@
+---
+outline: deep
+---
+
+# Browser Startup
+
+> See the [API Reference](/api/reference/wxt/interfaces/ExtensionRunnerConfig) for a full list of config.
+
+During development WXT uses [`web-ext` by Mozilla](https://www.npmjs.com/package/web-ext) to automatically open a browser window with your extension installed.
+
+## Config Files
+
+You can configure browser startup in 3 places:
+
+1. `/web-ext.config.ts`: Ignored from version control, this file lets you configure your own options for a specific project without affecting other developers
+
+ ```ts
+ import { defineRunnerConfig } from 'wxt';
+
+ export default defineRunnerConfig({
+ // ...
+ });
+ ```
+
+2. `/wxt.config.ts`: Via the [`runner` config](/api/reference/wxt/interfaces/InlineConfig#runner), included in version control
+3. `$HOME/web-ext.config.ts`: Provide default values for all WXT projects on your computer
+
+## Recipes
+
+### Set Browser Binaries
+
+To set or customize the browser opened during development:
+
+```ts
+export default defineRunnerConfig({
+ binaries: {
+ chrome: '/path/to/chrome-beta', // Use Chrome Beta instead of regular Chrome
+ firefox: 'firefoxdeveloperedition', // Use Firefox Developer Edition instead of regular Firefox
+ edge: '/path/to/edge', // Open MS Edge when running "wxt -b edge"
+ },
+});
+```
+
+### Persist Data
+
+By default, to keep from modifying your browser's existing profiles, `web-ext` creates a brand new profile every time you run the `dev` script.
+
+Right now, Chromium based browsers are the only browsers that support overriding this behavior and persisting data when running the `dev` script multiple times.
+
+To persist data, set the `--user-data-dir` flag:
+
+:::code-group
+
+```ts [Mac/Linux]
+export default defineRunnerConfig({
+ chromiumArgs: ['--user-data-dir=./.wxt/chrome-data'],
+});
+```
+
+```ts [Windows]
+import { resolve } from 'node:path';
+
+export default defineRunnerConfig({
+ // On Windows, the path must be absolute
+ chromiumArgs: [`--user-data-dir="${resolve(".wxt/chrome-data")}"`
+});
+```
+
+:::
+
+Now, next time you run the `dev` script, a persistent profile will be created in `.wxt/chrome-data/{profile-name}`. With a persistent profile, you can install devtools extensions to help with development, allow the browser to remember logins, etc, without worrying about the profile being reset the next time you run the `dev` script.
+
+:::tip
+You can use any directory you'd like for `--user-data-dir`, the examples above create a persistent profile for each WXT project. To create a profile for all WXT projects, you can put the `chrome-data` directory inside you're user's home directory.
+:::
+
+### Disable Opening Browser
+
+If you prefer to load the extension into your browser manually, you can disable the auto-open behavior:
+
+```ts
+export default defineRunnerConfig({
+ disabled: true,
+});
+```
diff --git a/docs/guide/essentials/config/build-mode.md b/docs/guide/essentials/config/build-mode.md
new file mode 100644
index 00000000..850f499f
--- /dev/null
+++ b/docs/guide/essentials/config/build-mode.md
@@ -0,0 +1,29 @@
+# Build Modes
+
+Because WXT is powered by Vite, it supports [modes](https://vite.dev/guide/env-and-mode.html#modes) in the same way.
+
+When running any dev or build commands, pass the `--mode` flag:
+
+```sh
+wxt --mode production
+wxt build --mode development
+wxt zip --mode testing
+```
+
+By default, `--mode` is `development` for the dev command and `production` for all other commands (build, zip, etc).
+
+## Get Mode at Runtime
+
+You can access the current mode in your extension using `import.meta.env.MODE`:
+
+```ts
+switch (import.meta.env.MODE) {
+ case 'development': // ...
+ case 'production': // ...
+
+ // Custom modes specified with --mode
+ case 'testing': // ...
+ case 'staging': // ...
+ // ...
+}
+```
diff --git a/docs/guide/essentials/config/entrypoint-loaders.md b/docs/guide/essentials/config/entrypoint-loaders.md
new file mode 100644
index 00000000..a61708b4
--- /dev/null
+++ b/docs/guide/essentials/config/entrypoint-loaders.md
@@ -0,0 +1,79 @@
+# Entrypoint Loaders
+
+To generate the manifest and other files at build-time, WXT must import each entrypoint to get their options, like content script `matches`. For HTML files, this is easy. For JS/TS entrypoints, the process is more complicated.
+
+When loading your JS/TS entrypoints, they are imported into a NodeJS environment, not the `browser` environment that they normally run in. This can lead to issues commonly seen when running browser-only code in a NodeJS environment, like missing global variables.
+
+WXT does several pre-processing steps to try and prevent errors during this process:
+
+1. Use `linkedom` to make a small set of browser globals (`window`, `document`, etc) available.
+2. Use `@webext-core/fake-browser` to create a fake version of the `chrome` and `browser` globals expected by extensions.
+3. Pre-process the JS/TS code, stripping out the `main` function then tree-shaking unused code from the file
+
+However, this process is not perfect. It doesn't setup all the globals found in the browser and the APIs may behave differently. As such, **_you should avoid using browser or extension APIs outside the `main` function of your entrypoints!_**
+
+:::tip
+If you're running into errors while importing entrypoints, run `wxt prepare --debug` to see more details about this process. When debugging, WXT will print out the pre-processed code to help you identify issues.
+:::
+
+Once the environment has been polyfilled and your code pre-processed, it's up the entrypoint loader to import your code, extracting the options from the default export.
+
+There are two options for loading your entrypoints:
+
+1. `vite-node` - default as of `v0.19.0`
+2. `jiti` (**DEPRECATED, will be removed in `v0.20.0`**) - Default before `v0.19.0`
+
+## vite-node
+
+Since 0.19.0, WXT uses `vite-node`, the same tool that powers Vitest and Nuxt, to import your entrypoint files. It re-uses the same vite config used when building your extension, making it the most stable entrypoint loader.
+
+## jiti
+
+To enable `jiti`:
+
+```ts
+export default defineConfig({
+ entrypointLoader: 'jiti',
+});
+```
+
+This is the original method WXT used to import TS files. However, because it doesn't support vite plugins like `vite-node`, it does one additional pre-processing step: It removes **_ALL_** imports from your code.
+
+That means you cannot use imported variables outside the `main` function in JS entrypoints, like for content script `matches` or other options:
+
+```ts
+// entrypoints/content.ts
+import { GOOGLE_MATCHES } from '~/utils/match-patterns';
+
+export default defineContentScript({
+ matches: GOOGLE_MATCHES,
+ main() {
+ // ...
+ },
+});
+```
+
+```
+$ wxt build
+wxt build
+
+WXT 0.14.1
+ℹ Building chrome-mv3 for production with Vite 5.0.5
+✖ Command failed after 360 ms
+
+[8:55:54 AM] ERROR entrypoints/content.ts: Cannot use imported variable "GOOGLE_MATCHES" before main function.
+```
+
+Usually, this error occurs when you try to extract options into a shared file or when running code outside the `main` function. To fix the example from above, use literal values when defining an entrypoint instead of importing them:
+
+```ts
+import { GOOGLE_MATCHES } from '~/utils/match-patterns'; // [!code --]
+
+export default defineContentScript({
+ matches: GOOGLE_MATCHES, // [!code --]
+ matches: ['*//*.google.com/*'], // [!code ++]
+ main() {
+ // ...
+ },
+});
+```
diff --git a/docs/guide/essentials/config/environment-variables.md b/docs/guide/essentials/config/environment-variables.md
new file mode 100644
index 00000000..be97952f
--- /dev/null
+++ b/docs/guide/essentials/config/environment-variables.md
@@ -0,0 +1,54 @@
+# Environment Variables
+
+## Dotenv Files
+
+WXT supports [dotenv files the same way as Vite](https://vite.dev/guide/env-and-mode.html#env-files). Create any of the following files:
+
+```
+.env
+.env.local
+.env.[mode]
+.env.[mode].local
+```
+
+And any environment variables listed inside them will be available at runtime:
+
+```sh
+# .env
+VITE_API_KEY=...
+```
+
+```ts
+await fetch(`/some-api?apiKey=${import.meta.env.VITE_API_KEY}`);
+```
+
+Remember to prefix any environment variables with `VITE_`, otherwise they won't be available at runtime, as per [Vite's convention](https://vite.dev/guide/env-and-mode.html#env-files).
+
+## Built-in Environment Variables
+
+WXT provides some custom environment variables based on the current command:
+
+| Usage | Type | Description |
+| ---------------------------------- | --------- | ----------------------------------------------------- |
+| `import.meta.env.MANIFEST_VERSION` | `2 │ 3` | The target manifest version |
+| `import.meta.env.BROWSER` | `string` | The target browser |
+| `import.meta.env.CHROME` | `boolean` | Equivalent to `import.meta.env.BROWSER === "chrome"` |
+| `import.meta.env.FIREFOX` | `boolean` | Equivalent to `import.meta.env.BROWSER === "firefox"` |
+| `import.meta.env.SAFARI` | `boolean` | Equivalent to `import.meta.env.BROWSER === "safari"` |
+| `import.meta.env.EDGE` | `boolean` | Equivalent to `import.meta.env.BROWSER === "edge"` |
+| `import.meta.env.OPERA` | `boolean` | Equivalent to `import.meta.env.BROWSER === "opera"` |
+
+You can also access all of [Vite's environment variables](https://vite.dev/guide/env-and-mode.html#env-variables):
+
+| Usage | Type | Description |
+| ---------------------- | --------- | --------------------------------------------------------------------------- |
+| `import.meta.env.MODE` | `string` | The [mode](/guide/essentials/config/build-mode) the extension is running in |
+| `import.meta.env.PROD` | `boolean` | When `NODE_ENV='production'` |
+| `import.meta.env.DEV` | `boolean` | Opposite of `import.meta.env.PROD` |
+
+:::details Other Vite Environment Variables
+Vite provides two other environment variables, but they aren't useful in WXT projects:
+
+- `import.meta.env.BASE_URL`: Use `browser.runtime.getURL` instead.
+- `import.meta.env.SSR`: Always `false`.
+ :::
diff --git a/docs/guide/essentials/config/hooks.md b/docs/guide/essentials/config/hooks.md
new file mode 100644
index 00000000..930b8bdf
--- /dev/null
+++ b/docs/guide/essentials/config/hooks.md
@@ -0,0 +1,22 @@
+# Hooks
+
+WXT includes a system that lets you hook into the build process and make changes.
+
+Here's an example hook that modifies the `manifest.json` file before it is written to the output directory:
+
+```ts
+// wxt.config.ts
+export default defineConfig({
+ hooks: {
+ 'build:manifestGenerated': (wxt, manifest) => {
+ if (wxt.config.mode === 'development') {
+ manifest.title += ' (DEV)';
+ }
+ },
+ },
+});
+```
+
+> Most hooks provide the `wxt` object as the first argument. It contains the resolved config and other info about the current build.
+
+Putting one-off hooks like this in your config file is simple, but if you find yourself writing lots of hooks, you should extract them into [WXT Modules](/guide/essentials/wxt-modules) instead.
diff --git a/docs/guide/essentials/config/manifest.md b/docs/guide/essentials/config/manifest.md
new file mode 100644
index 00000000..6aeab49d
--- /dev/null
+++ b/docs/guide/essentials/config/manifest.md
@@ -0,0 +1,258 @@
+# Manifest
+
+In WXT, there is no `manifest.json` file in your source code. Instead, WXT generates it during the build process based off files in your project.
+
+## Manifest Config
+
+To manually add a property to the `manifest.json` output during builds, use the `manifest` config inside `wxt.config.ts`:
+
+```ts
+export default defineConfig({
+ manifest: {
+ // Put manual changes here
+ },
+});
+```
+
+You can also define the manifest as a function, and use JS to generate it based on the target browser, mode, and more.
+
+```ts
+export default defineConfig({
+ manifest: ({ browser, manifestVersion, mode, command }) => {
+ return {
+ // ...
+ };
+ },
+});
+```
+
+### MV2 and MV3 Compatibility
+
+When adding properties to the manifest, always define the property in it's MV3 format when possible. When targeting MV2, WXT will automatically convert these properties to their MV2 format.
+
+For example, for this config:
+
+```ts
+export default defineConfig({
+ manifest: {
+ action: {
+ default_title: 'Some Title',
+ },
+ web_accessible_resources: [
+ {
+ matches: ['*://*.google.com/*'],
+ resources: ['icon/*.png'],
+ },
+ ],
+ },
+});
+```
+
+WXT will generate the following manifests:
+
+:::code-group
+
+```json [MV2]
+{
+ "manifest_version": 2,
+ // ...
+ "browser_action": {
+ "default_title": "Some Title"
+ },
+ "web_accessible_resources": ["icon/*.png"]
+}
+```
+
+```json [MV3]
+{
+ "manifest_version": 3,
+ // ...
+ "action": {
+ "default_title": "Some Title"
+ },
+ "web_accessible_resources": [
+ {
+ "matches": ["*://*.google.com/*"],
+ "resources": ["icon/*.png"]
+ }
+ ]
+}
+```
+
+:::
+
+You can also specify properties specific to a single manifest version, and they will be stripped out when targeting the other manifest version.
+
+## Name
+
+> [Chrome Docs](https://developer.chrome.com/docs/extensions/mv3/manifest/name/)
+
+If not provided via the `manifest` config, the manifest's `name` property defaults to your `package.json`'s `name` property.
+
+## Version and Version Name
+
+> [Chrome Docs](https://developer.chrome.com/docs/extensions/mv3/manifest/version/)
+
+Your extension's `version` and `version_name` is based on the `version` from your `package.json`.
+
+- `version_name` is the exact string listed
+- `version` is the string cleaned up, with any invalid suffixes removed
+
+Example:
+
+```json
+// package.json
+{
+ "version": "1.3.0-alpha2"
+}
+```
+
+```json
+// .output//manifest.json
+{
+ "version": "1.3.0",
+ "version_name": "1.3.0-alpha2"
+}
+```
+
+If a version is not present in your `package.json`, it defaults to `"0.0.0"`.
+
+## Icons
+
+WXT automatically discovers your extension's icon by looking at files in the `public/` directory:
+
+```
+public/
+├─ icon-16.png
+├─ icon-24.png
+├─ icon-48.png
+├─ icon-96.png
+└─ icon-128.png
+```
+
+Specifically, if an icon must match one of these regex to be discovered:
+
+<<< @/../packages/wxt/src/core/utils/manifest.ts#snippet
+
+If you don't like these filename or you're migrating to WXT and don't want to rename the files, you can manually specify an `icon` in your manifest:
+
+```ts
+export default defineConfig({
+ manifest: {
+ icons: {
+ 16: '/extension-icon-16.png',
+ 24: '/extension-icon-24.png',
+ 48: '/extension-icon-48.png',
+ 96: '/extension-icon-96.png',
+ 128: '/extension-icon-128.png',
+ },
+ },
+});
+```
+
+Alternatively, you can use [`@wxt-dev/auto-icons`](https://www.npmjs.com/package/@wxt-dev/auto-icons) to let WXT generate your icon at the required sizes.
+
+## Permissions
+
+> [Chrome docs](https://developer.chrome.com/docs/extensions/reference/permissions/)
+
+```ts
+export default defineConfig({
+ manifest: {
+ permissions: ['storage', 'tabs'],
+ },
+});
+```
+
+## Host Permissions
+
+> [Chrome docs](https://developer.chrome.com/docs/extensions/develop/concepts/declare-permissions#host-permissions)
+
+```ts
+export default defineConfig({
+ manifest: {
+ permissions: ['storage', 'tabs'],
+ },
+});
+```
+
+:::warning
+If you use host permissions and target both MV2 and MV3, make sure to only include the required host permissions for each version:
+
+```ts
+export default defineConfig({
+ manifest: ({ manifestVersion }) => ({
+ host_permissions: manifestVersion === 2 ? [...] : [...],
+ }),
+});
+```
+
+:::
+
+## Default Locale
+
+```ts
+export default defineConfig({
+ manifest: {
+ name: '__MSG_extName__',
+ description: '__MSG_extDescription__',
+ default_locale: 'en',
+ },
+});
+```
+
+> See [I18n docs](/guide/essentials/i18n) for a full guide on internationalizing your extension.
+
+## Actions
+
+In MV2, you have two options: [`browser_action`](https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/WebExtensions/manifest.json/browser_action) and [`page_action`](https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/WebExtensions/manifest.json/page_action). In MV3, they were merged into a single [`action`](https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/WebExtensions/manifest.json/action) API.
+
+By default, whenever an `action` is generated, WXT falls back to `browser_action` when targeting MV2.
+
+### Action With Popup
+
+To generate a manifest where a UI appears after clicking the icon, just create a [Popup entrypoint](/guide/essentials/entrypoints#popup).
+
+```ts
+export default defineConfig({
+ hooks: {
+ build: {
+ manifestGenerated(manifest) {
+ // Update the manifest variable by reference
+ manifest.name = 'Overriden name';
+ },
+ },
+ },
+});
+```
+
+If you want to use a `page_action` for MV2, add the following meta tag to the HTML document's head:
+
+```html
+
+```
+
+### Action Without Popup
+
+If you want to use the `activeTab` permission or the `browser.action.onClicked` event, but don't want to show a popup:
+
+1. Delete the [Popup entrypoint](/guide/essentials/entrypoints#popup) if it exists
+2. Add the `action` key to your manifest:
+ ```ts
+ export default defineConfig({
+ manifest: {
+ action: {},
+ },
+ });
+ ```
+
+Same as an action with a popup, WXT will fallback on using `browser_action` for MV2. To use a `page_action` instead, add that key as well:
+
+```ts
+export default defineConfig({
+ manifest: {
+ action: {},
+ page_action: {},
+ },
+});
+```
diff --git a/docs/guide/essentials/config/runtime.md b/docs/guide/essentials/config/runtime.md
new file mode 100644
index 00000000..084a43f5
--- /dev/null
+++ b/docs/guide/essentials/config/runtime.md
@@ -0,0 +1,56 @@
+# Runtime Config
+
+> This API is still a WIP, with more features coming soon!
+
+Define runtime configuration in a single place, `/app.config.ts`:
+
+```ts
+import { defineAppConfig } from 'wxt/sandbox';
+
+// Define types for your config
+declare module 'wxt/sandbox' {
+ export interface WxtAppConfig {
+ theme?: 'light' | 'dark';
+ }
+}
+
+export default defineAppConfig({
+ theme: 'dark',
+});
+```
+
+:::warning
+This file is committed to the repo, so don't put any secrets here. Instead, use [Environment Variables](#environment-variables)
+:::
+
+To access runtime config, WXT provides the `useAppConfig` function:
+
+```ts
+import { useAppConfig } from 'wxt/sandbox';
+
+console.log(useAppConfig()); // { theme: "dark" }
+```
+
+## Environment Variables in App Config
+
+You can use environment variables in the `app.config.ts` file.
+
+```ts
+declare module 'wxt/sandbox' {
+ export interface WxtAppConfig {
+ apiKey?: string;
+ skipWelcome: boolean;
+ }
+}
+
+export default defineAppConfig({
+ apiKey: import.meta.env.VITE_API_KEY,
+ skipWelcome: import.meta.env.VITE_SKIP_WELCOME === 'true',
+});
+```
+
+This has several advantages:
+
+- Define all expected environment variables in a single file
+- Convert strings to other types, like booleans or arrays
+- Provide default values if an environment variable is not provided
diff --git a/docs/guide/essentials/config/typescript.md b/docs/guide/essentials/config/typescript.md
new file mode 100644
index 00000000..be220883
--- /dev/null
+++ b/docs/guide/essentials/config/typescript.md
@@ -0,0 +1,65 @@
+# TypeScript Configuration
+
+When you run [`wxt prepare`](/api/cli/wxt-prepare), WXT generates a base TSConfig file for your project at `/.wxt/tsconfig.json`.
+
+At a minimum, you need to create a TSConfig in your root directory that looks like this:
+
+```jsonc
+// /tsconfig.json
+{
+ "extends": ".wxt/tsconfig.json",
+}
+```
+
+Or if you're in a monorepo, you may not want to extend the config. If you don't extend it, you need to add `.wxt/wxt.d.ts` to the TypeScript project:
+
+```ts
+///
+```
+
+## Compiler Options
+
+To specify custom compiler options, add them in `/tsconfig.json`:
+
+```jsonc
+// /tsconfig.json
+{
+ "extends": ".wxt/tsconfig.json",
+ "compilerOptions": {
+ "jsx": "preserve",
+ },
+}
+```
+
+## TSConfig Paths
+
+WXT provides a default set of path aliases.
+
+| Alias | To | Example |
+| ----- | ------------- | ----------------------------------------------- |
+| `~~` | `/*` | `import "~~/scripts"` |
+| `@@` | `/*` | `import "@@/scripts"` |
+| `~` | `/*` | `import { toLowerCase } from "~/utils/strings"` |
+| `@` | `/*` | `import { toLowerCase } from "@/utils/strings"` |
+
+To add your own, DO NOT add them to your `tsconfig.json`! Instead, use the [`alias` option](/api/reference/wxt/interfaces/InlineConfig#alias) in `wxt.config.ts`.
+
+This will add your custom aliases to `/.wxt/tsconfig.json` next time you run `wxt prepare`. It also adds your alias to the bundler so it can resolve imports.
+
+```ts
+import { resolve } from 'node:path';
+
+export default defineConfig({
+ alias: {
+ // Directory:
+ testing: resolve('utils/testing'),
+ // File:
+ strings: resolve('utils/strings.ts'),
+ },
+});
+```
+
+```ts
+import { fakeTab } from 'testing/fake-objects';
+import { toLowerCase } from 'strings';
+```
diff --git a/docs/guide/essentials/config/vite.md b/docs/guide/essentials/config/vite.md
new file mode 100644
index 00000000..47bb8eff
--- /dev/null
+++ b/docs/guide/essentials/config/vite.md
@@ -0,0 +1,68 @@
+# Vite
+
+WXT uses [Vite](https://vitejs.dev/) under the hood to bundle your extension.
+
+This page explains how to customize your project's Vite config. Refer to [Vite's documentation](https://vite.dev/config/) to learn more about configuring the bundler.
+
+:::tip
+In most cases, you shouldn't change Vite's build settings. WXT provides sensible defaults that output a valid extension accepted by all stores when publishing.
+:::
+
+## Change Vite Config
+
+You can change Vite's config via the `wxt.config.ts` file:
+
+```ts
+// wxt.config.ts
+import { defineConfig } from 'wxt';
+
+export default defineConfig({
+ vite: () => ({
+ // Override config here, same as `defineConfig({ ... })`
+ // inside vite.config.ts files
+ }),
+});
+```
+
+## Add Vite Plugins
+
+To add a plugin, install the NPM package and add it to the Vite config:
+
+```ts
+// wxt.config.ts
+import { defineConfig } from 'wxt';
+import VueRouter from 'unplugin-vue-router/vite';
+
+export default defineConfig({
+ vite: () => ({
+ plugins: [
+ VueRouter({
+ /* ... */
+ }),
+ ],
+ }),
+});
+```
+
+:::warning
+Due to the way WXT orchestrates Vite builds, some plugins may not work as expected. For example, `vite-plugin-remove-console` normally only runs when you build for production (`vite build`). However, WXT uses a combination of dev server and builds during development, so you need to manually tell it when to run:
+
+```ts
+// wxt.config.ts
+import { defineConfig } from 'wxt';
+import removeConsole from 'vite-plugin-remove-console';
+
+export default defineConfig({
+ vite: (configEnv) => ({
+ plugins:
+ configEnv.mode === 'production'
+ ? [removeConsole({ includes: ['log'] })]
+ : [],
+ }),
+});
+```
+
+Search [GitHub issues](https://github.com/wxt-dev/wxt/issues?q=is%3Aissue+label%3A%22vite+plugin%22) if you run into issues with a specific plugin.
+
+If an issue doesn't exist for your plugin, [open a new one](https://github.com/wxt-dev/wxt/issues/new/choose).
+:::
diff --git a/docs/guide/key-concepts/content-script-ui.md b/docs/guide/essentials/content-scripts.md
similarity index 58%
rename from docs/guide/key-concepts/content-script-ui.md
rename to docs/guide/essentials/content-scripts.md
index c75935ee..caf74255 100644
--- a/docs/guide/key-concepts/content-script-ui.md
+++ b/docs/guide/essentials/content-scripts.md
@@ -1,8 +1,102 @@
-# Content Script UI
+---
+outline: deep
+---
-There are three ways to mount a UI inside a content script:
+# Content Scripts
-[[toc]]
+## Context
+
+The first argument to a content script's `main` function is it's "context".
+
+```ts
+// entrypoints/content.ts
+export default defineContentScript({
+ main(ctx) {},
+});
+```
+
+This object is responsible for tracking whether or not the content script's context is "invalidated". Most browsers, by default, do not stop content scripts if the extension is uninstalled, updated, or disabled. When this happens, content scripts start reporting this error:
+
+```
+Error: Extension context invalidated.
+```
+
+The `ctx` object provides several helpers to stop asynchronous code from running once the context is invalidated:
+
+```ts
+ctx.addEventListener(...);
+ctx.setTimeout(...);
+ctx.setInterval(...);
+ctx.requestAnimationFrame(...);
+// and more
+```
+
+You can also check if the context is invalidated manually:
+
+```ts
+if (ctx.isValid) {
+ // do something
+}
+// OR
+if (ctx.isInvalid) {
+ // do something
+}
+```
+
+## CSS
+
+In regular web extensions, CSS for content scripts is usually a separate CSS file, that is added to a CSS array in the manifest:
+
+```json
+{
+ "content_scripts": [
+ {
+ "css": ["content/style.css"],
+ "js": ["content/index.js"],
+ "matches": ["*://*/*"]
+ }
+ ]
+}
+```
+
+In WXT, to add CSS to a content script, simply import the CSS file into your JS entrypoint, and WXT will automatically add the bundled CSS output to the `css` array.
+
+```ts
+// entrypoints/content/index.ts
+import './style.css';
+
+export default defineContentScript({
+ // ...
+});
+```
+
+To create a standalone content script that only includes a CSS file:
+
+1. Create the CSS file: `entrypoints/example.content.css`
+2. Use the `build:manifestGenerated` hook to add the content script to the manifest:
+ ```ts
+ // wxt.config.ts
+ export default defineConfig({
+ hooks: {
+ "build:manifestGenerated": (wxt, manifest) => {
+ manifest.content_scripts ??= [];
+ manifest.content_scripts.push({
+ // Build extension once to see where your CSS get's written to
+ css: ["content-scripts/example.css"],
+ matches: ["*://*/*"]
+ )
+ }
+ }
+ })
+ ```
+
+## UI
+
+WXT provides 3 built-in utilities for adding UIs to a page from a content script:
+
+- [Integrated](#integrated) - `createIntegratedUi`
+- [Shadow Root](#shadow-root) -`createShadowRootUi`
+- [IFrame](#iframe) - `createIframeUi`
Each has their own set of advantages and disadvantages.
@@ -12,7 +106,7 @@ Each has their own set of advantages and disadvantages.
| Shadow Root | ✅ | ✅ (off by default) | ❌ | ✅ |
| IFrame | ✅ | ✅ | ✅ | ❌ |
-## Integrated
+### Integrated
Integrated content script UIs are injected alongside the content of a page. This means that they are affected by CSS on that page.
@@ -26,6 +120,7 @@ export default defineContentScript({
main(ctx) {
const ui = createIntegratedUi(ctx, {
position: 'inline',
+ anchor: 'body',
onMount: (container) => {
// Append children to the container
const app = document.createElement('p');
@@ -51,6 +146,7 @@ export default defineContentScript({
main(ctx) {
const ui = createIntegratedUi(ctx, {
position: 'inline',
+ anchor: 'body',
onMount: (container) => {
// Create the app and mount it to the UI container
const app = createApp(App);
@@ -80,6 +176,7 @@ export default defineContentScript({
main(ctx) {
const ui = createIntegratedUi(ctx, {
position: 'inline',
+ anchor: 'body',
onMount: (container) => {
// Create a root on the UI container and render a component
const root = ReactDOM.createRoot(container);
@@ -108,6 +205,7 @@ export default defineContentScript({
main(ctx) {
const ui = createIntegratedUi(ctx, {
position: 'inline',
+ anchor: 'body',
onMount: (container) => {
// Create the Svelte app inside the UI container
const app = new App({
@@ -137,6 +235,7 @@ export default defineContentScript({
main(ctx) {
const ui = createIntegratedUi(ctx, {
position: 'inline',
+ anchor: 'body',
onMount: (container) => {
// Render your app to the UI container
const unmount = render(() =>
...
, container);
@@ -157,13 +256,11 @@ export default defineContentScript({
See the [API Reference](/api/reference/wxt/client/functions/createIntegratedUi) for the complete list of options.
-You can control how CSS is injected for an integrated content script UI with the [`cssInjectionMode`](/api/reference/wxt/interfaces/BaseContentScriptEntrypointOptions#cssinjectionmode) property. Usually, you'll want to leave it as `"manifest"`, the default, so the UI inherits its style from the website's CSS.
-
-## Shadow Root
+### Shadow Root
Often in web extensions, you don't want your content script's CSS affecting the page, or vise-versa. The [`ShadowRoot`](https://developer.mozilla.org/en-US/docs/Web/API/ShadowRoot) API is ideal for this.
-WXT's [`createShadowRootUi`](/api/reference/wxt/client/functions/createShadowRootUi) abstracts all the `ShadowRoot` setup away, making it easy to create UIs with isolated CSS. It also supports an optional `isolateEvents` parameter to further isolate user interactions.
+WXT's [`createShadowRootUi`](/api/reference/wxt/client/functions/createShadowRootUi) abstracts all the `ShadowRoot` setup away, making it easy to create UIs whose styles are isolated from the page. It also supports an optional `isolateEvents` parameter to further isolate user interactions.
To use `createShadowRootUi`, follow these steps:
@@ -188,6 +285,7 @@ export default defineContentScript({
const ui = await createShadowRootUi(ctx, {
name: 'example-ui',
position: 'inline',
+ anchor: 'body',
onMount(container) {
// Define how your UI will be mounted inside the container
const app = document.createElement('p');
@@ -218,6 +316,7 @@ export default defineContentScript({
const ui = await createShadowRootUi(ctx, {
name: 'example-ui',
position: 'inline',
+ anchor: 'body',
onMount: (container) => {
// Define how your UI will be mounted inside the container
const app = createApp(App);
@@ -252,6 +351,7 @@ export default defineContentScript({
const ui = await createShadowRootUi(ctx, {
name: 'example-ui',
position: 'inline',
+ anchor: 'body',
onMount: (container) => {
// Container is a body, and React warns when creating a root on the body, so create a wrapper div
const app = document.createElement('div');
@@ -289,6 +389,7 @@ export default defineContentScript({
const ui = await createShadowRootUi(ctx, {
name: 'example-ui',
position: 'inline',
+ anchor: 'body',
onMount: (container) => {
// Create the Svelte app inside the UI container
const app = new App({
@@ -323,6 +424,7 @@ export default defineContentScript({
const ui = await createShadowRootUi(ctx, {
name: 'example-ui',
position: 'inline',
+ anchor: 'body',
onMount: (container) => {
// Render your app to the UI container
const unmount = render(() =>
...
, container);
@@ -343,17 +445,18 @@ export default defineContentScript({
See the [API Reference](/api/reference/wxt/client/functions/createShadowRootUi) for the complete list of options.
-:::info TailwindCSS
-`createShadowRootUi` supports TailwindCSS out of the box! When importing the styles, just import the main CSS file containing the `@tailwind` directives, and everything will just work :+1:.
-:::
+Full examples:
-## IFrame
+- [react-content-script-ui](https://github.com/wxt-dev/examples/tree/main/examples/react-content-script-ui)
+- [tailwindcss](https://github.com/wxt-dev/examples/tree/main/examples/tailwindcss)
+
+### IFrame
If you don't need to run your UI in the same frame as the content script, you can use an IFrame to host your UI instead. Since an IFrame just hosts an HTML page, **_HMR is supported_**.
WXT provides a helper function, [`createIframeUi`](/api/reference/wxt/client/functions/createIframeUi), which simplifies setting up the IFrame.
-1. Create an HTML page that will be loaded into your IFrame
+1. Create an HTML page that will be loaded into your IFrame:
```html
@@ -368,7 +471,7 @@ WXT provides a helper function, [`createIframeUi`](/api/reference/wxt/client/fun