When I decided to build a Chrome extension, I found that the number of blog posts and articles about building one is quite small. And there's even less information when you want to use one of the newer technologies like TailwindCSS.
In this tutorial we will discover how to build a Chrome extension using Parcel.js for bundling and watching and TailwindCSS for styling. We will also talk about how to separate your styling from the website to avoid colliding CSS – but more on that later.
There are a few types of Chrome extensions worth mentioning:
- Content scripts: The first type of Chrome extension is the most common. It runs in the context of a web page and can be used to modify its content. This is the type of extension we'll be creating.
- Popup: Popup-based extensions use the extension icon to the right of the address bar to open a popup which can contain any HTML content that you like.
- Options UI: You guessed it! This is a UI for customizing options as an extension. It's accessible by right clicking the extension icon and selecting "options" or by navigating to the extension's page from the Chrome extensions list
- DevTools Extension: "A DevTools extension adds functionality to the Chrome DevTools. It can add new UI panels and sidebars, interact with the inspected page, get information about network requests, and more". -Google Chrome documentation
In this tutorial we will build a Chrome extension using only content scripts by displaying content on the web page and interacting with the DOM.
How to bundle your Chrome Extension using Parcel.js V2
Parcel.js is a zero-configuration web application bundler. It can use any kind of file as an entry point. It's simple to use and will work for any type of app including Chrome Extensions.
First create a new folder called
demo-extension (make sure you have
npm installed, I am going to use
yarn for this post):
$ mkdir demo-extension && cd demo-extension && yarn init -y
Next we will install Parcel as a local dependency:
$ yarn add -D parcel@next
Now create a new directory called
$ mkdir src
Adding a manifest file
Every browser extension needs a manifest file. This is where we define the version and meta data about our extension, but also the scripts that are used (content, background, popup, .etc) and permissions if any.
You can find the full schema in Chrome's documentation: https://developer.chrome.com/extensions/manifest
Let's go ahead and add a new file in
manifest.json with this content:
Now, before we go into more detail about how chrome extensions work and the kind of stuff you can make with them, we are going to set up TailwindCSS
How to use TailwindCSS with your Chrome Extension
TailwindCSS is a CSS-framework that uses lower-level utility classes to create reusable but also customizable visual UI components.
Tailwind can be installed in two ways, but the most common way to use it is via its NPM package:
$ yarn add tailwindcss
Also, go ahead and add
$ yarn add -D autoprefixer postcss-import
You need these to add vendor prefixes to your styles and to be able to write syntax like
@import "tailwindcss/base" to import Tailwind files directly from
Now that we have it installed, let's make a new file inside our root directory and call it
postcss.config.js. This is the configuration file for PostCSS and it will contain, for now, these lines:
Order of plugins matters here!
That's it! That's all you need to get started using TailwindCSS within your Chrome extension.
Create a file called
style.css inside your
src folder and include Tailwind files:
Remove unused CSS using PurgeCSS
Let's also make sure we only import the styles we use by enabling Tailwind's purging capability.
Create a new Tailwind configuration file by running:
$ npx tailwindcss init: this will create a new
To remove unused CSS, we're going to add our source files to the purge field like this:
Now our CSS will be purged and unused styles will be removed when you build for production.
How to enable Hot Reloading within your Chrome Extension
One more thing before adding some content to our extension: let's enable hot reloading.
Chrome doesn't reload the source files when you make new changes – you need to hit the "Reload" button on the extensions page. Fortunately, there's an NPM package that does hot reloading for us.
$ yarn add crx-hotreload
In order to use it, we'll create a new file called
background.js inside our
src folder and import
Finally point to
manifest.json so it can be served with our extension (hot reloading is disabled in production by default):
Enough with configuration. Let's build a small script form within our extension.
Types of scripts in a Chrome extension
As mentioned in the beginning of this post, in Chrome extensions there a few types of scripts you can use.
A background script, on the other hand, is where you can react to browser events, and it has access to the Chrome extension APIs.
Adding a content script
Create a new file called
content-script.js under the
Let's add this HTML form to our newly created file:
Styling a browser extension is not as straightforward as you may think because you need to make sure that the website styles are not affected by your own styles.
In order to separate them, we are going to use something called the Shadow DOM. The Shadow DOM is a powerful encapsulation technique for styles. This means that styling is scoped to the Shadow tree. Therefore, nothing is leaked out to the visited web page. Also outside styles do not override the Shadow DOM's content, although CSS variables can still be accessible.
A shadow host is any DOM element we would like to attach our Shadow tree to. A Shadow Root is what is returned from
attachShadow and its content is what gets rendered.
We did exactly that with our
style.css bundle. Now we can inline the CSS automatically to the Shadow DOM at build time.
Now we have to tell the browser about this new file, let's go ahead and include it by adding these lines to our manifest:
In order to serve our extension, we are going to add a few scripts to our
Finally you can run
yarn watch, go to
chrome://extensions, and make sure Developer Mode is enabled on the top right of the page. Click on "Load Unpacked" and select the
dist folder under
- If you get this error:
Error: Bundles must have unique filePathsyou can simply fix it by removing the
mainfield in your
How to publish your extension to the Google Chrome Web Store
Before going further into this, let's add a new NPM script that will help us compress the extension files as required by Chrome.
If you haven't installed
zip yet, please do so:
brew install zip
sudo apt install zip
- For Windows, you can use the powershell command
powershell Compress-Archive -Path .\\dist\\ -Destination .\\chrome-extension.zip
Now all you have to do is head to Chrome Web Store Developer Dashboard to set up your account and publish your extension 🎉
- You can find the complete demo for this tutorial hosted on my GitHub account here
In the end, Chrome extensions are not that different from web apps. Today you learned how to develop an extension using the latest technologies and practices in web development.
Hopefully this tutorial helped you speed up your extension development a little bit!
If you found this helpful, please Tweet about it and follow me at @marouanerassili.