> ## Documentation Index
> Fetch the complete documentation index at: https://extension.js.org/llms.txt
> Use this file to discover all available pages before exploring further.

# Vue.js for browser extensions

> Build extension UIs with Vue single-file components. Extension.js detects Vue from your dependencies and configures SFC compilation and aliases.

Extension.js detects Vue from your dependencies. It configures single-file component (SFC) compilation, framework aliases, and dev-time update behavior automatically. Scoped styles, the Composition API, and `<script setup>` all work without extra bundler wiring.

## When Vue is a good fit

* You already ship production Vue apps.
* You prefer Vue single-file component authoring with scoped styles and the Composition API.
* You want extension UI surfaces that mirror existing Vue architecture.

## Template examples

### `new-vue`

<img src="https://mintcdn.com/extensionjs/etcqPhDg4wwyPkcC/images/examples/newtab-vue/screenshot.png?fit=max&auto=format&n=etcqPhDg4wwyPkcC&q=85&s=549a11874b4cc84122936db96524d7ff" alt="Template screenshot" width="2400" height="1800" data-path="images/examples/newtab-vue/screenshot.png" />

Build a Vue new-tab experience with SFC support from day one.

<CodeGroup>
  ```bash npm theme={null}
  npx extension@latest create my-extension --template=newtab-vue
  ```

  ```bash pnpm theme={null}
  pnpx extension@latest create my-extension --template=newtab-vue
  ```

  ```bash yarn 2+ theme={null}
  yarn dlx extension@latest create my-extension --template=newtab-vue
  ```

  ```bash bun theme={null}
  bunx extension@latest create my-extension --template=newtab-vue
  ```

  ```bash deno theme={null}
  deno run -A npm:extension@latest create my-extension --template=newtab-vue
  ```
</CodeGroup>

Repository: [`extension-js/examples/newtab-vue`](https://github.com/extension-js/examples/tree/main/examples/newtab-vue)

### `content-vue`

<img src="https://mintcdn.com/extensionjs/VCnDd7fX2Nza24SE/images/examples/content-vue/screenshot.png?fit=max&auto=format&n=VCnDd7fX2Nza24SE&q=85&s=828db99559e99c5ea991db16f7013769" alt="Template screenshot" width="2400" height="1800" data-path="images/examples/content-vue/screenshot.png" />

Inject Vue components directly into web pages with a content-script setup.

<CodeGroup>
  ```bash npm theme={null}
  npx extension@latest create my-extension --template=content-vue
  ```

  ```bash pnpm theme={null}
  pnpx extension@latest create my-extension --template=content-vue
  ```

  ```bash yarn 2+ theme={null}
  yarn dlx extension@latest create my-extension --template=content-vue
  ```

  ```bash bun theme={null}
  bunx extension@latest create my-extension --template=content-vue
  ```

  ```bash deno theme={null}
  deno run -A npm:extension@latest create my-extension --template=content-vue
  ```
</CodeGroup>

Repository: [`extension-js/examples/content-vue`](https://github.com/extension-js/examples/tree/main/examples/content-vue)

## Usage with an existing extension

Add Vue to an existing extension with the steps below.

### Installation

Install the required dependencies:

<CodeGroup>
  ```bash npm theme={null}
  npm install vue
  ```

  ```bash pnpm theme={null}
  pnpm add vue
  ```

  ```bash yarn theme={null}
  yarn add vue
  ```

  ```bash bun theme={null}
  bun add vue
  ```

  ```bash deno theme={null}
  deno add npm:vue
  ```
</CodeGroup>

For explicit setup in existing projects, install the Vue SFC toolchain too:

<CodeGroup>
  ```bash npm theme={null}
  npm install -D vue-loader @vue/compiler-sfc
  ```

  ```bash pnpm theme={null}
  pnpm add -D vue-loader @vue/compiler-sfc
  ```

  ```bash yarn theme={null}
  yarn add -D vue-loader @vue/compiler-sfc
  ```

  ```bash bun theme={null}
  bun add -d vue-loader @vue/compiler-sfc
  ```

  ```bash deno theme={null}
  deno add -D npm:vue-loader npm:@vue/compiler-sfc
  ```
</CodeGroup>

When an optional Vue integration package such as `vue-loader` or `@vue/compiler-sfc` is missing, the build error names the exact version that matches the bundled toolchain. The install command in the error is phrased for the package manager that your project uses.

### Configuration

Extension.js expects Vue components in `.vue` files. It configures `vue-loader`, `VueLoaderPlugin`, and Vue-related define flags in the Rspack pipeline. JSX and TSX pages in a Vue project compile through `vue/jsx-runtime`.

## Development behavior

When Extension.js detects Vue, it:

* Enables `.vue` compilation in the framework plugin.
* Applies Vue runtime aliases (for consistent runtime resolution).
* Supports content script updates by remounting after relevant Vue SFC changes.

If your project lacks optional Vue tooling, Extension.js warns with the exact install command instead of installing it for you.

### Troubleshooting

* **Missing Vue tooling warning:** Install `vue-loader` and `@vue/compiler-sfc`.
* **Prompt to restart after install:** Stop and rerun `extension dev` so Extension.js can load the newly installed loader/plugin.
* **Unexpected `.vue` handling issues:** Verify `vue` is present in project dependencies so Extension.js detects Vue integration.

## Usage examples

### In a new tab extension

To use Vue in a new tab extension, include your entry file in HTML:

```html theme={null}
<!doctype html>
<html lang="en">
  <head>
    <meta charset="utf-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1" />
    <title>New Extension</title>
  </head>
  <body>
    <noscript>You need to enable JavaScript to run this extension.</noscript>
    <div id="app"></div>
  </body>
  <script src="./main.ts"></script>
</html>
```

```ts theme={null}
import { createApp } from "vue";
import App from "./App.vue";

createApp(App).mount("#app");
```

```vue theme={null}
<!-- App.vue -->
<template>
  <h1>Hello, Vue.js Extension!</h1>
</template>

<script setup lang="ts">
// Component logic goes here.
</script>

<style scoped>
h1 {
  color: #42b983;
}
</style>
```

### In a `content_script` file

For content scripts, mount a Vue app into an injected root node:

```ts theme={null}
import { createApp } from "vue";
import App from "./App.vue";
import "./content.css";

const rootDiv = document.createElement("div");
rootDiv.id = "extension-root";
document.body.appendChild(rootDiv);

createApp(App).mount("#extension-root");
```

## Best practices

* Keep UI entrypoints framework-first (`main.ts`, `App.vue`) and keep extension APIs in dedicated modules.
* Use scoped styles in SFCs when possible to reduce style leaks in extension pages.
* For large UIs, split components and shared composables to keep content scripts small.

## Runtime build and the Options API

From 4.1.19, a Vue project bundles the runtime-only build, `vue.runtime.esm-bundler.js`, the file that the `vue` package names as its `module` entry. Extension.js aliases `vue$`, `@vue/runtime-dom`, `@vue/runtime-core`, and `@vue/shared` to that build, so your app and any transpiled workspace dependency share one Vue instance. The full build ships the template compiler, which an extension CSP cannot run, and its `innerHTML` and `Function` calls fail the addons.mozilla.org store check.

The runtime build has no template compiler, so templates must be precompiled. Single-file components give you that: `vue-loader` compiles each `<template>` at build time. In-DOM templates and string `template:` options are not supported. Vue warns in the console and renders nothing for such a component.

To keep runtime compilation, alias `vue$` to the full build in `extension.config.js`:

```js extension.config.js theme={null}
export default {
  config: (config) => {
    config.resolve = config.resolve || {};
    config.resolve.alias = {
      ...(config.resolve.alias || {}),
      vue$: "vue/dist/vue.esm-bundler.js",
    };
    return config;
  },
};
```

That build compiles templates with `new Function`, which needs `unsafe-eval` in the extension CSP. Manifest V3 extension pages forbid it, so the alias only helps a Manifest V2 project.

The Options API stays on in production. Extension.js sets `__VUE_OPTIONS_API__` to `true` in every mode, so a component written with `data()`, `methods`, or `computed` works in the shipped bundle. `__VUE_PROD_DEVTOOLS__` and hydration mismatch details are off in production.

One more define applies to every bundle, for every framework. The free identifier `global` is defined as `globalThis`, and Rspack's own global helper is off. That helper falls back to `Function("return this")`, which fails the addons.mozilla.org store check, so no such shim reaches your bundle.

## Next steps

* Learn more about [TypeScript support](/docs/languages-and-frameworks/typescript).
* Explore [Sass and Sass modules](/docs/languages-and-frameworks/sass).

## Video walkthrough

<Frame>
  <iframe className="w-full aspect-video rounded-xl" src="https://www.youtube-nocookie.com/embed/Jdf4wHl0s1I?rel=0" title="Extension.js: Vue HMR" loading="lazy" allow="encrypted-media; picture-in-picture; fullscreen" allowFullScreen />
</Frame>

## See the template run

<Frame>
  <iframe className="w-full aspect-video rounded-xl" src="https://www.youtube-nocookie.com/embed/LIfZxR-B2Nk?rel=0" title="Extension.js: Content Vue template" loading="lazy" allow="encrypted-media; picture-in-picture; fullscreen" allowFullScreen />
</Frame>


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.