WXT Template
The WXT template provides a modern browser extension development framework with Vue 3, TypeScript, and Vite, perfect for building cross-browser extensions with excellent developer experience.
Technical Stack
- WXT - Browser extension development framework
- Vue 3 - Progressive JavaScript framework
- TypeScript - Type-safe development
- Vite - Fast build tool and dev server
- Vue Router - Client-side routing
- Pinia - State management library
- Vue i18n - Internationalization
- Tailwind CSS - Utility-first CSS framework
Quick Start
1. Create Project
bash
# Initialize project
vup init my-extension-project
cd my-extension-project
# Add WXT template
vup add my-extension2. Install Dependencies
bash
# Install dependencies
pnpm install3. Start Development
bash
# Start development server
cd apps/my-extension
pnpm devThe extension will be available in the browser with hot reload support.
Project Structure
apps/my-extension/
├── src/
│ ├── assets/ # Static assets
│ │ ├── vue.svg # Logo asset
│ │ └── images/
│ │ └── hero-logo.svg
│ ├── entrypoints/ # Extension entry points
│ │ ├── background.ts # Background script
│ │ ├── content.ts # Content script
│ │ ├── newtab/ # New tab page
│ │ │ ├── index.html
│ │ │ ├── App.vue
│ │ │ └── main.ts
│ │ ├── options/ # Options page
│ │ │ ├── index.html
│ │ │ ├── App.vue
│ │ │ └── main.ts
│ │ └── popup/ # Popup page
│ │ ├── index.html
│ │ ├── App.vue
│ │ └── main.ts
│ └── manifest.ts # Extension manifest
├── public/ # Public assets
│ ├── icon/ # Extension icons
│ │ ├── 16.png
│ │ ├── 32.png
│ │ ├── 48.png
│ │ ├── 96.png
│ │ └── 128.png
│ └── wxt.svg # Favicon/logo
├── package.json # Dependencies and scripts
├── tsconfig.json # TypeScript configuration
└── wxt.config.ts # WXT configurationCore Features
Multiple Entry Points
Background Script
typescript
// src/entrypoints/background.ts
export default defineBackground(() => {
console.log('Background script loaded');
// Listen for extension installation
chrome.runtime.onInstalled.addListener((details) => {
console.log('Extension installed:', details);
});
// Listen for messages from content scripts
chrome.runtime.onMessage.addListener((message, sender, sendResponse) => {
console.log('Message received:', message);
sendResponse({ status: 'success' });
});
});Content Script
typescript
// src/entrypoints/content.ts
export default defineContentScript({
matches: ['<all_urls>'],
main() {
console.log('Content script loaded');
// Inject content into the page
const div = document.createElement('div');
div.textContent = 'Hello from WXT!';
div.style.cssText = `
position: fixed;
top: 10px;
right: 10px;
background: #007aff;
color: white;
padding: 10px;
border-radius: 5px;
z-index: 10000;
`;
document.body.appendChild(div);
},
});Popup Page
vue
<!-- src/entrypoints/popup/App.vue -->
<template>
<div class="popup-container">
<h1>{{ title }}</h1>
<p>{{ description }}</p>
<button @click="handleClick" class="btn">
{{ buttonText }}
</button>
</div>
</template>
<script setup lang="ts">
import { ref } from 'vue';
const title = ref('WXT Extension');
const description = ref('A modern browser extension');
const buttonText = ref('Click Me');
const handleClick = () => {
chrome.tabs.query({ active: true, currentWindow: true }, (tabs) => {
if (tabs[0]?.id) {
chrome.tabs.sendMessage(tabs[0].id, { action: 'hello' });
}
});
};
</script>
<style scoped>
.popup-container {
@apply w-80 p-4 bg-white;
}
.btn {
@apply w-full px-4 py-2 bg-blue-500 text-white rounded hover:bg-blue-600;
}
</style>Options Page
vue
<!-- src/entrypoints/options/App.vue -->
<template>
<div class="options-container">
<h1>Extension Settings</h1>
<form @submit.prevent="saveSettings">
<div class="form-group">
<label for="theme">Theme:</label>
<select v-model="settings.theme" id="theme">
<option value="light">Light</option>
<option value="dark">Dark</option>
</select>
</div>
<div class="form-group">
<label for="notifications">Enable Notifications:</label>
<input
v-model="settings.notifications"
type="checkbox"
id="notifications"
/>
</div>
<button type="submit" class="btn">Save Settings</button>
</form>
</div>
</template>
<script setup lang="ts">
import { ref, onMounted } from 'vue';
interface Settings {
theme: string;
notifications: boolean;
}
const settings = ref<Settings>({
theme: 'light',
notifications: true,
});
onMounted(() => {
// Load settings from storage
chrome.storage.sync.get(['theme', 'notifications'], (result) => {
settings.value = {
theme: result.theme || 'light',
notifications: result.notifications !== false,
};
});
});
const saveSettings = () => {
chrome.storage.sync.set(settings.value, () => {
console.log('Settings saved');
});
};
</script>
<style scoped>
.options-container {
@apply max-w-2xl mx-auto p-6;
}
.form-group {
@apply mb-4;
}
.form-group label {
@apply block mb-2 font-medium;
}
.form-group select,
.form-group input[type='checkbox'] {
@apply w-full p-2 border rounded;
}
.btn {
@apply px-4 py-2 bg-blue-500 text-white rounded hover:bg-blue-600;
}
</style>New Tab Page
vue
<!-- src/entrypoints/newtab/App.vue -->
<template>
<div class="newtab-container">
<div class="hero">
<h1>{{ title }}</h1>
<p>{{ description }}</p>
</div>
<div class="features">
<div v-for="feature in features" :key="feature.title" class="feature">
<h3>{{ feature.title }}</h3>
<p>{{ feature.description }}</p>
</div>
</div>
</div>
</template>
<script setup lang="ts">
import { ref } from 'vue';
const title = ref('WXT Extension');
const description = ref('A modern browser extension with Vue 3');
const features = ref([
{
title: 'Modern Framework',
description: 'Built with Vue 3 and TypeScript',
},
{
title: 'Fast Development',
description: 'Hot reload and instant feedback',
},
{
title: 'Cross-browser',
description: 'Works on Chrome, Firefox, and Edge',
},
]);
</script>
<style scoped>
.newtab-container {
@apply min-h-screen bg-gray-100 p-8;
}
.hero {
@apply text-center mb-12;
}
.hero h1 {
@apply text-4xl font-bold text-gray-900 mb-4;
}
.hero p {
@apply text-xl text-gray-600;
}
.features {
@apply grid grid-cols-1 md:grid-cols-3 gap-8;
}
.feature {
@apply bg-white p-6 rounded-lg shadow-md;
}
.feature h3 {
@apply text-xl font-semibold text-gray-900 mb-2;
}
.feature p {
@apply text-gray-600;
}
</style>Extension Manifest
typescript
// src/manifest.ts
import { defineManifest } from '@wxt/sdk';
export default defineManifest({
manifest_version: 3,
name: 'WXT Extension',
version: '1.0.0',
description: 'A modern browser extension built with WXT',
permissions: ['storage', 'tabs', 'activeTab'],
host_permissions: ['<all_urls>'],
action: {
default_popup: '/popup.html',
default_title: 'WXT Extension',
},
background: {
service_worker: '/background.js',
},
content_scripts: [
{
matches: ['<all_urls>'],
js: ['/content.js'],
},
],
web_accessible_resources: [
{
resources: ['icon/*.png'],
matches: ['<all_urls>'],
},
],
icons: {
16: 'icon/16.png',
32: 'icon/32.png',
48: 'icon/48.png',
96: 'icon/96.png',
128: 'icon/128.png',
},
});Development Tools
WXT Configuration
typescript
// wxt.config.ts
import { defineConfig } from 'wxt';
export default defineConfig({
manifest: {
name: 'WXT Extension',
version: '1.0.0',
description: 'A modern browser extension built with WXT',
},
modules: ['vue'],
vue: {
// Vue-specific configuration
},
runner: {
// Development server configuration
startUrls: ['https://example.com'],
},
build: {
// Build configuration
outDir: '.output',
},
});TypeScript Configuration
json
// tsconfig.json
{
"extends": ["./.wxt/tsconfig.json"]
}Available Scripts
json
{
"scripts": {
"dev": "wxt",
"dev:firefox": "wxt -b firefox",
"build": "wxt build",
"build:firefox": "wxt build -b firefox",
"zip": "wxt zip",
"zip:firefox": "wxt zip -b firefox",
"postinstall": "wxt prepare",
"lint": "eslint src/ --ext .vue,.ts,.js",
"lint:fix": "eslint src/ --ext .vue,.ts,.js --fix",
"format": "prettier --write \"src/**/*.{js,ts,vue,json,css,scss}\"",
"format:check": "prettier --check \"src/**/*.{js,ts,vue,json,css,scss}\""
}
}Build and Deployment
Development Build
bash
cd apps/my-extension
# Build for development
pnpm build
# The built files will be in the .output directoryProduction Build
bash
cd apps/my-extension
# Build for specific browser
pnpm build
pnpm build:firefox
# Create ZIP package
pnpm zipBrowser Installation
- Chrome/Edge: Load unpacked extension from
.outputdirectory - Firefox: Load temporary add-on from
.outputdirectory - Production: Upload ZIP file to respective web stores