How to Create a Chrome Extension Using Vue 3?

COMMENTS ()
Tweet

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.

Pre-requisites

This guide assumes you are familiar with:

  • Vue.js
  • JavaScript
  • TypeScript
  • CSS

Step-by-Step to Create a Chrome Extension Using Vue 3

Follow these steps to create a Chrome Extension using Vue 3:

Step 1: Setting Up the Project

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

Step 2: Configuring manifest.json

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'],

}))

Step 3: Integrating with Vite

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

Step 4: Create the Extension Popup

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.

Step 5: Injecting an Iframe

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;

}

Step 6: Creating a Chatbot UI

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'],

    },

  ],

}))

Step 7: Toggling the Iframe

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!

Conclusion

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.

Senior Software Engineer

CALL

USA408 365 4638

VISIT

1301 Shoreway Road, Suite 160,

Belmont, CA 94002

Contact us

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