DEV Community

Cover image for Plasmo - a new way to create modern browser extensions
Corentin Le Berre
Corentin Le Berre

Posted on

Plasmo - a new way to create modern browser extensions

article banner

Context

I have had the opportunity to develop a new AI-based solution recently to enhance sellers' performance when using online conferencing platforms such as Zoom. After technical analysis, we concluded that the most effective approach is to create a browser extension for Chrome.

Okay, but how do I create a Chrome extension? Can I use React or other tools I'm accustomed to? It is entirely possible, but setting one up can be time consuming and diverge from the standard configuration. While researching how to integrate React easily into my project, I encountered Plasmo. This framework promises to supercharge your extension with a set of features that I'll detail in this article.

Introduction

Plasmo is an open-source tool with great documentation. It has a strong opinion on project architecture and configuration (no, you don't need to touch the usual manifest file). I don't think it is for everyone, but I found it pleasant to work with, especially if your codebase is growing rapidly. The team also offers SaaS for easier extension testing and deployment but is not included in today's topic.

Currently in version 0.82, Plasmo supports React, Svelte, and Vue out of the box providing live reloading and Hot Module Replacement for the ease of use. The tool makes it easy to compile the code, therefore enabling the compatibility with Chrome, Firefox and Safari, ensuring interoperability and consistency across platforms.

Plasmo production build

In this article, I'll show you how to quickly create your first Plasmo project with React and Typescript. The code will be provided on this repo.

Create your first extension

Today, we're going to work together on creating a straightforward and enjoyable Plasmo extension – something fun like replacing the 𝕏 logo with the old blue and black Twitter logo, or even using a logo of your choice. The goal is to familiarize ourselves with the sharing mechanics of storage and understand how Plasmo allows us to interact with content scripts and popups easily.

Gif illustrating the final plasmo project

Our focus will be on the Popup and Content Script aspects. We will explore these specific components in detail (you can also interact with the Background service worker, Tabs and Options easily). Our primary attention will be paid to understanding how modifications of the popup interface should be made in popup.tsx, and how content script enhancements belong in content.tsx.

πŸ‘‰ Create the project

npm create plasmo plasmo-intro
cd plasmo-intro
npm run dev
Enter fullscreen mode Exit fullscreen mode

You will then have the following file structure :

πŸ“¦ plasmo-intro  
┣ πŸ“‚ assets  
┣ πŸ“‚ build
┣ πŸ“œ package.json  
┣ πŸ“œ popup.tsx
┣ πŸ“œ README.md  
β”— πŸ“œ tsconfig.json
Enter fullscreen mode Exit fullscreen mode

Now you can load the extension in your favorite browser

πŸ‘‰ Customise the Popup

The popup is what shows up when you click on the extension in your browser. All the code needed can be found in popup.tsx.

This code defines an UI component that displays a set of radio buttons, each representing a different Twitter logo.

popup without styling and useStorage

πŸ‘‰ Add the logic and styling

Plasmo provides utilities to ease communication between your popup, content script and the rest of your extension. We're going to add Plasmo Storage to our project to preserve a state in the browser. It will be used to pass the icon we want to display on x.com between the popup and the content script.

npm install @plasmohq/storage
Enter fullscreen mode Exit fullscreen mode

more docs about Plasmo storage here

// package.json

{
  "name": "plasmo-intro",
  ...
  "manifest": {
    "host_permissions": [
      "https://twitter.com/*",
      "https://x.com/*"
    ],
    "permissions": [
      "storage"
    ]
  }
}
Enter fullscreen mode Exit fullscreen mode

Browsers and Mobile apps need to explicitly declare features that you want to use for security reasons. That's why you will need to specify it in the permissions section of the manifest in the package.json.

Now we can use the useStorage hook to manipulate a preserved state in our react components.

import { useStorage } from "@plasmohq/storage/hook"

const [logo, setLogo] = useStorage("logo", "original")
Enter fullscreen mode Exit fullscreen mode

To make our application more user-friendly, we can add our different SVG and custom styles in popup.tsx.

Popup.tsx file with styles and useStorage hook

Users can now select a logo, and the chosen option will be stored and managed using the useStorage hook from the @plasmohq/storage library.

To make this code type safe you should define a type LogoType in types.ts containing all of your different logo.

Logo types file

Finally your popup should look like thisπŸ‘‡

Final popup result

Now that we've managed the popup, we'll move on to the more important task which is to modify the x.com page.

πŸ‘‰ Customise the Content Script to modify x.com behaviors

To modify the integrity of a web page you will need to add the "scripting" permission to the manifest.

Plasmo manages different lifecycles to help you mount and unmount components in the content script. The global process is well described in their docs.

Content.tsx

To make it concise, you need to create or locate an anchor on the website. In our case, it will be the x.com logo with the selector [aria-label="X"]. You can then access the local element to modify or replace it by mounting your own component.

In summary, the code below defines a custom getRootContainer function that asynchronously looks for a specific HTML, with the attribute aria-label set to "X". Once the element appears on the page, the interval is cleared and our new logo is replaced.

The SVG logos are being dynamically rendered by the components mounted on the page, based on the value that we stored in local storage using popup.tsx.

DisplaySvg.tsx

Now that everything's configured, the extension should be fully functional and will allow you to replace the x.com logo dynamically according to your preferences. The final step is to build your project and make it available to the world. πŸ₯³

πŸ‘‰ Build the project

Plasmo provides the ability to build your project for different browsers, through the plasmo package command. By default, only the Chrome artifact is built.

npm run build
Enter fullscreen mode Exit fullscreen mode

After the build, you will then have the following file structure :

πŸ“¦ plasmo-intro  
┣ πŸ“‚ build
 ┣ πŸ“‚ chrome-mv3-dev 
 β”— πŸ“‚ chrome-mv3-prod
Enter fullscreen mode Exit fullscreen mode

Finally, you can zip the prod folders and share it as is or publish it on the Chrome web store.

To go further

In this article, I chose a relatively simple example to illustrate Plasmo's key features. If you're interested in the subject, I implore you to take a closer look at their well structured documentation. They also provide a Github repository with over 50 powerful templates, including Supabase, Tailwind, NextJS, Svelte, React-Router etc.

Conclusion

Plasmo shows great potential for developing modern extensions without compromising the browsing experience. It allows you to create extensions for different platforms, but you need to be aware of cross-browser API limitations. Despite some unexpected behavior in dev mode, the tool offers several benefits, including rich documentation, an active community, and simplified manifest management. Additionally, it provides SaaS for testing and deployment of extensions across different online stores. This can be very valuable for development teams.

Despite some challenges, Plasmo framework presents itself as a promising solution for the development of modern extensions!

Top comments (1)

Collapse
 
jonrandy profile image
Jon Randy πŸŽ–οΈ

The whole point of (most) browser extensions is that they are small, efficient, and do a single job well. Quite why you'd want to bloat them up with an entire framework like React/Vue/etc. is beyond me. In most cases this is simply prioritising 'ease' of use and familiarity for lazy developers, at the expensive of essentially packing your browser with bloatware.

Granted there are some bigger, complex things that you may want to build into a browser extension - and for those, including a framework and libraries might well be a good idea. For your average browser extension (and stuff like your sample) - this is definitely overkill... using a sledgehammer to crack a nut.