Vue.js
Build extension UIs with Vue single-file components (SFCs) without maintaining custom bundler wiring.
Extension.js detects Vue from your dependencies and configures SFC compilation, framework aliases, and development behavior automatically.
When Vue is a good fit
- Your team already ships production Vue apps.
- You prefer SFC ergonomics with scoped styles and composition APIs.
- You want extension UI surfaces that mirror existing Vue architecture.
Template examples
new-vue

Build a Vue new-tab experience with SFC support from day one.
npx extension@latest create my-extension --template=new-vue
Repository: extension-js/examples/new-vue
content-vue

Inject Vue components directly into web pages with a content-script setup.
npx extension@latest create my-extension --template=content-vue
Repository: extension-js/examples/content-vue
Usage with an existing extension
To integrate Vue.js into an existing extension, follow these steps:
Installation
Install the required dependencies:
For explicit setup in existing projects, install the Vue SFC toolchain too:
npm install -D vue-loader @vue/compiler-sfc
Configuration
Extension.js expects Vue components in .vue files. It wires vue-loader, VueLoaderPlugin, and Vue-related define flags in the Rspack pipeline.
Development behavior
When Vue is detected, Extension.js:
- 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 optional Vue tooling is missing, Extension.js installs it and asks for a restart.
Troubleshooting
- Missing Vue tooling warning: install
vue-loader and @vue/compiler-sfc.
- Prompt to restart after install: stop and rerun
extension dev so the newly installed loader/plugin is picked up.
- Unexpected
.vue handling issues: verify vue is present in project dependencies so Vue integration is detected.
Usage examples
In a new tab extension
To use Vue.js in a new tab extension, include your entry file in HTML:
<!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>
import { createApp } from "vue";
import App from "./App.vue";
createApp(App).mount("#app");
<!-- 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:
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.
Next steps
Video walkthrough
