Vue.js, a progressive JavaScript framework, streamlines the creation of powerful browser extensions. Reusable components are essential for creating efficient and scalable web development projects.
Vue.js, with its simplicity and flexibility, has gained significant popularity as a framework for building interactive user interfaces. When used with Vite, it provides a streamlined way to develop powerful browser add-ons with modern JavaScript frameworks.
This guide will walk you through setting up a Vue 3 project, integrating it with the CRXJS Vite plugin for Chrome extension configurations, and building a simple extension with a popup and an iframe. By the end, you’ll have a functional Chrome extension ready for deployment.
This guide assumes you are familiar with:
Follow these steps to create a Chrome Extension using Vue 3:
First, create a Vue 3 project using Vite by running the following command:
npm create vite@latest
Select Vue and TypeScript when prompted. After the project is created, navigate to the project directory and install the necessary dependencies:
npm install
Then, run the following command to spin up the server at http://localhost:5173/.
npm run dev
Now, install Ant Design and the CRXJS Vite Plugin:
npm install @crxjs/vite-plugin@beta -D
npm install ant-design-vue
To manage the Chrome extension configuration, create a manifest.config.ts file in the root directory:
import { defineManifest } from '@crxjs/vite-plugin' import packageJson from './package.json' const { version, name, description } = packageJson const [major, minor, patch, label = '0'] = version .replace(/[^\d.-]+/g, '') .split(/[.-]/) export default defineManifest(async () => ({ name: name, description, version: `${major}.${minor}.${patch}.${label}`, version_name: version, manifest_version: 3, host_permissions: ['<all_urls>'], icons: { 16: 'src/assets/vue.svg', 48: 'src/assets/vue.svg', 128: 'src/assets/vue.svg', }, permissions: ['tabs', 'scripting'], }))
Modify vite.config.ts to use the CRXJS Vite Plugin:
import { defineConfig } from 'vite' import vue from '@vitejs/plugin-vue' import manifest from './manifest.config' import { crx } from '@crxjs/vite-plugin' // https://vitejs.dev/config/ export default defineConfig({ plugins: [vue(), crx({ manifest })], build: { outDir: 'dist',
Build the project:
npm run build
Create a popup directory inside the src folder and add index.html, index.ts, and index.vue files.
index.html:
<!doctype html> <html lang="en"> <head> <meta charset="UTF-8" /> <link rel="icon" href="./src/assets/vue.svg" /> <meta name="viewport" content="width=device-width, initial-scale=1.0" /> <title>My Cool Extension</title> </head> <body> <div id="app"></div> <script type="module" src="./index.ts"></script> </body> <style> body { padding: 16px 20px; } </style> </html>
index.ts:
import { createApp } from 'vue' import MyButton from './index.vue' const app = createApp(MyButton) app.mount('#app')
Index.vue:
<template> <button>Click on me!</button> </template> <script lang="ts"> import { defineComponent } from 'vue' export default defineComponent({ name: 'my-button', }) </script>
Declare the popup in manifest.config.ts:
export default defineManifest(async () => ({ //...All the existing configuration... action: { default_popup: 'src/popup/index.html', }, }))
Build the application again and reload the extension in Chrome.
Install type definitions for Chrome:
npm install @types/chrome -D
Add the following to the compilerOptions in tsconfig.json:
"types": ["chrome"]
Create a service-worker.ts file:
chrome.action.onClicked.addListener(async (tab) => { try { await chrome.tabs.sendMessage(tab.id as number, { command: "inject-iframe", }); } catch (error) { console.log(error); } });
Create a content-script.ts file:
chrome.runtime.onMessage.addListener(async (request, sender, sendResponse) => { switch (request.command) { case "inject-iframe": { injectIframe(); sendResponse("done"); break; } } }); function injectIframe() { const iframe = document.createElement("iframe"); iframe.src = "http://localhost:5173/"; iframe.id = "#chrome-extension-iframe"; // Make the iframe a sandboxed iframe iframe.setAttribute("sandbox", "allow-scripts allow-same-origin"); // Styling the iframe iframe.style.height = "250px"; iframe.style.width = "320px"; iframe.style.position = "fixed"; iframe.style.zIndex = "999999"; iframe.style.bottom = "0"; iframe.style.right = "50px"; iframe.style.backgroundColor = "white"; iframe.style.overflow = "hidden"; iframe.style.opacity = "1"; iframe.style.display = "block !important"; iframe.style.visibility = "visible"; iframe.style.border = '1px solid black' iframe.style.borderTopLeftRadius = '6px' iframe.style.borderTopRightRadius = '6px' // Append the iframe to the body document.body.appendChild(iframe); return iframe; }
In App.vue, add the following:
<template> <div class="chat-bot"> <a-flex class="title" align="center" justify="space-between"> <p>Build with us</p> <a-flex :gap="10"> <minus-outlined /> <close-outlined /> </a-flex> </a-flex> <a-flex :gap="50" vertical justify="space-between" class="main-content"> <div class="message"> Hi, I’m the support assistant. To better serve you I need some additional information </div> <a-flex :gap="5" class="chat-box"> <a-input class="input-box" placeholder="Type your message" /> <a-button class="send-btn">Send</a-button> </a-flex> </a-flex> </div> </template> <script lang="ts"> import { defineComponent } from 'vue'; import { Flex, Input, Button } from 'ant-design-vue'; import { MinusOutlined, CloseOutlined } from '@ant-design/icons-vue'; export default defineComponent({ components: { AFlex: Flex, MinusOutlined: MinusOutlined, CloseOutlined: CloseOutlined, AInput: Input, AButton: Button, } }) </script> <style> body { margin: 0px; } p { margin-block-end: 0; margin-block-start: 0; } .chat-bot { text-align: left; } .title { padding: 15px; font-size: 18px; background-color: black; color: white; } .main-content { position: fixed; bottom: 0; padding: 15px; } .message { background-color: #F4F4F4; border-radius: 10px 10px 10px 0; padding: 10px; } .input-box { border: 1px solid black; } .input-box.ant-input::placeholder { color: black; } .send-btn { background-color: black; color: white; } </style>
Update manifest.config.ts:
import { defineManifest } from '@crxjs/vite-plugin' import packageJson from './package.json' const { version, name, description } = packageJson const [major, minor, patch, label = '0'] = version .replace(/[^\d.-]+/g, '') .split(/[.-]/) export default defineManifest(async () => ({ name: name, description, version: `${major}.${minor}.${patch}.${label}`, version_name: version, manifest_version: 3, host_permissions: ['<all_urls>'], icons: { 16: 'src/assets/vue.svg', 32: 'src/assets/vue.svg', 48: 'src/assets/vue.svg', 128: 'src/assets/vue.svg', }, permissions: ['tabs', 'scripting'], action: {}, background: { service_worker: 'service-worker.ts', }, content_scripts: [ { matches: ['<all_urls>'], js: ['content-script.ts'], }, ], }))
Modify content-script.ts to toggle the iframe:
let isIframeInjected = false; chrome.runtime.onMessage.addListener(async (request, sender, sendResponse) => { switch (request.command) { case "inject-iframe": { if (isIframeInjected) { toggleIframe(); } else { const iframe = injectIframe(); iframe.onload = () => { isIframeInjected = true; toggleIframe(); }; } sendResponse("done"); break; } } }); function toggleIframe() { const iframe = document.querySelector("#chrome-extension-iframe"); if (iframe) { const iframeDisplay = iframe.style.display; iframe.style.display = iframeDisplay === "none" ? "block" : "none"; } } function injectIframe() { const iframe = document.createElement("iframe"); iframe.src = "http://localhost:5173/"; iframe.id = "chrome-extension-iframe"; iframe.setAttribute("sandbox", "allow-scripts allow-same-origin"); iframe.style.height = "250px"; iframe.style.width = "320px"; iframe.style.position = "fixed"; iframe.style.zIndex = "999999"; iframe.style.bottom = "0"; iframe.style.right = "50px"; iframe.style.backgroundColor = "white"; iframe.style.overflow = "hidden"; iframe.style.opacity = "1"; iframe.style.display = "none"; iframe.style.visibility = "visible"; iframe.style.border = '1px solid black' iframe.style.borderTopLeftRadius = '6px' iframe.style.borderTopRightRadius = '6px' document.body.appendChild(iframe); return iframe; }
Now, rebuild your application and reload the extension in Chrome. You should now have a fully functional Chrome extension with a Vue 3 popup and an iframe that can be toggled on and off!
Building a Chrome extension using Vue 3 and Vite and the CRXJS Vite plugin offers a modern and efficient development workflow. Following this guide, you’ve learned how to set up a Vue 3 project, configure the manifest.json for Chrome extension requirements, and build a basic extension with a popup and an iframe.
This extension can serve as a foundation for more complex functionality, allowing you to leverage the power of Vue 3 and Vite’s fast build times. Whether adding more features to the popup, enhancing the content script, or integrating additional services, this setup provides a robust starting point for your Chrome extension development journey.
USA408 365 4638
1301 Shoreway Road, Suite 160,
Belmont, CA 94002
Whether you are a large enterprise looking to augment your teams with experts resources or an SME looking to scale your business or a startup looking to build something.
We are your digital growth partner.
Tel:
+1 408 365 4638
Support:
+1 (408) 512 1812
COMMENTS ()
Tweet