Build a React TailwindCSS Chrome Extension in 5 Minutes
Jonathan Braat / November 21, 2022 โ 5 min read
Learn how to build a Google Chrome web extension with React, TypeScript and TailwindCSS in less than 5 minutes. Includes a free template.
I have been building Chrome extensions for a while now.
Most notably Supatabs available in the Chrome Web Store. Supatabs is a an extension to reduce tab clutter and improve your computer's performance. By reducing the amount of open tabs you can save up to 95% of your RAM.
In this article I want to show you how you can build a production ready extension in only 5 minutes.
Start with a template
One of the byproducts of Supatabs is an extension template I built.
The template is setup and ready to go with Manifest V3, React, TailwindCSS and TypeScript. Instead of webpack I used vite for bundling. In my opinion it's easier to maintain.
Follow these steps to set up your new chrome extension:
- Go to the template repository
- Click the "Use this template" button to create your own project in your GitHub. If you aren't using GitHub just clone the repository
- Go back to the template repository and give it a star ๐
Setting up the development environment
Follow these steps to start your dev environment:
- Change the
name
,displayName
anddescription
in thepackage.json
. These properties will be used in the build step to name your extension for the store yarn install
to install the dependenciesyarn dev
to start the development server- Load the extension in Chrome:
- Open your Chrome browser
- Enter
chrome://extensions
in the address bar and hit enter - Turn on developer mode
- Load unpacked extension: Use the generated
dist
directory in your project folder. This will be generated and updated when you useyarn dev
oryarn build
That's all you need to do.
A manifest with version 3 will be generated automatically for you.
Whenever you save changes and the dev
command is running, the dist
directory will automatically be
updated thanks to nodemon
. This means you don't have to do step 4 more than once.
Implement some functionality
To get a feel for how extension development works, we will implement a simple button in the extension popup to open a specific url in new tab or focus it if it already exists in your tabstrip.
Prepare
As we won't need most of the different Chrome extension pages in the template, we will remove the irrelevant source files from the project and modify our config. This is so they won't be included in the final build.
Stop the development server and then delete the following directories including content:
src/pages/content/*
src/pages/devtools/*
src/pages/newtab/*
src/pages/options/*
src/pages/panel/*
src/pages/background/*
Then we will modify the vite.config.ts
to look like the following:
import { defineConfig } from 'vite';
import react from '@vitejs/plugin-react';
import { resolve } from 'path';
import makeManifest from './utils/plugins/make-manifest';
const root = resolve(__dirname, 'src');
const pagesDir = resolve(root, 'pages');
const assetsDir = resolve(root, 'assets');
const outDir = resolve(__dirname, 'dist');
const publicDir = resolve(__dirname, 'public');
export default defineConfig({
resolve: {
alias: {
"@src": root,
"@assets": assetsDir,
"@pages": pagesDir,
},
},
plugins: [react(), makeManifest()],
publicDir,
build: {
outDir,
sourcemap: process.env.__DEV__ === "true",
rollupOptions: {
input: {
popup: resolve(pagesDir, 'popup', 'index.html'),
},
output: {
entryFileNames: (chunk) => `src/pages/${chunk.name}/index.js`,
},
},
},
});
Now we need to modify src/manifest.ts
accordingly. We will remove unnecessary
config params and add chrome.tabs
permission to query active tabs.
import pkg from '../package.json';
import { ManifestType } from '@src/manifest-type';
const manifest: ManifestType = {
manifest_version: 3,
name: pkg.displayName,
version: pkg.version,
description: pkg.description,
action: {
default_popup: 'src/pages/popup/index.html',
default_icon: 'icon-34.png',
},
permissions: [ 'tabs' ], // <-- this how you add permissions
icons: {
"128": 'icon-128.png',
},
web_accessible_resources: [
{
resources: ['icon-128.png', 'icon-34.png'],
matches: [],
},
],
};
export default manifest;
Implement
Once we are done with the preparation, we can restart the dev server with yarn dev
.
We will now add the button to open a tab with a specific URL or focus it if it already exists. The implementation will be in React with TypeScript. We will apply styles with TailwindCSS.
import React from 'react';
export default function Popup(): JSX.Element {
/**
* A simple function to query the chrome.tabs api for an url
* If a tab with the matching url was found we focus it
* Otherwise create the tab and focus
*
* chrome.tabs docs here: https://developer.chrome.com/docs/extensions/reference/tabs/
*/
const createOrFocus = async () => {
const [res] = await chrome.tabs.query({ url: 'https://www.google.com/' });
if (res?.id) {
await chrome.tabs.update(res.id, { active: true });
} else {
chrome.tabs.create({ url: 'https://google.com' });
}
}
// Some react code styled with TailwindCSS
return (
<div className="absolute top-0 left-0 right-0 bottom-0 flex justify-center items-center text-white text-center h-full p-3 bg-gray-800">
<button
className="font-bold text-lg px-4 py-1.5 border border-white rounded-lg hover:bg-gray-600"
onClick={() => createOrFocus()}
>
Open or focus tab
</button>
</div>
);
}
I won't go into the implementational details, as this is fairly simple react with a button.
This showcases how similar extension development can be to normal web development.
Publish
To publish stop the dev server and run yarn build
Now you can zip the dist
folder and upload your package to the Chrome Web Store.
That's it. You just built a Chrome extension with React, TypeScript and TailwindCSS under 5 minutes.
If you liked this article and know a friend who could benefit from it as well, please consider sharing it ๐ If you want to get news and updates from me, subscribe to the newsletter below ๐ or follow me on Twitter.