DEV Community

Lukasz Kuklis
Lukasz Kuklis

Posted on • Edited on

Creating a Chrome Extension with React and TypeScript

In my quest to develop a Chrome Extension using React and TypeScript, I found that existing resources didn't quite meet my needs. This prompted me to embark on a journey of creating my own guide, tailored to my specific requirements.

Here's what I aimed to achieve:

  • Develop a Chrome extension featuring an interactive popup screen for enhanced user experience.
  • Write the code in TypeScript, leveraging its robust typing system for better maintainability and error prevention. Generate two distinct outputs for the published code:
  • A Popup app
  • Content scripts, background scripts, and other necessary components
  • Prepare the code for publication, which includes:
  • Uglifying the code to minimize its size and improve performance
  • Compressing the code into a ZIP file, creating a package ready for Chrome Extension publication.

Create React app

Prepare environment

npx create-react-app chrome-react --template typescript
Enter fullscreen mode Exit fullscreen mode

When created I remove react-script package and copies build dependencies, configuration files and scripts into the app directory. It is needed to modify webpack.config file. (Another solution is to use react-app-rewired

npm run eject
Enter fullscreen mode Exit fullscreen mode

Then for be sure everything works I have run a build

npm run build
Enter fullscreen mode Exit fullscreen mode

which finished successfully
Image description

Now I want to be able to create build for development so I need to add

npm install -g win-node-env
Enter fullscreen mode Exit fullscreen mode

I set $env:NODE_ENV="production" in Powershell terminal.

Prepare project files

I deleted marked files from /src directory
Image description

Then I have updated two left files which are now:
App.tsx

import React from 'react';

function App() {
  return (
    <div>
      Chrome extension popup
    </div>
  );
}

export default App;
Enter fullscreen mode Exit fullscreen mode

index.tsx

import React from 'react';
import ReactDOM from 'react-dom/client';
import App from './App';

const root = ReactDOM.createRoot(
  document.getElementById('root') as HTMLElement
);
root.render(
  <React.StrictMode>
    <App />
  </React.StrictMode>
);
Enter fullscreen mode Exit fullscreen mode

Another directory to cleanup is public. There are few files to remove:

Image description

And the content of index.html needs to be updated so it will looks like that:

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="utf-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1" />
    <meta name="theme-color" content="#000000" />
    <title>Chrome Extension </title>
  </head>
  <body>
    <noscript>You need to enable JavaScript to run this app.</noscript>
    <div id="root"></div>
  </body>
</html>
Enter fullscreen mode Exit fullscreen mode

The second file to update is the hearth of extension - manifest.json file.

The current manifest.json is file created by create-react-app and it content is related to Web App Manifest which mostly is useful for mobile devices. As I am working on Chrome Extension I can removed its contents and put the valid extension manifest

{
  "name": "Chrome Extension",
  "description": "Chrome Extension Description",
  "version": "1.0",
  "manifest_version": 3,
  "action": {
    "default_popup": "index.html",
    "default_title": "Open the popup"
  }
}
Enter fullscreen mode Exit fullscreen mode

Now I build project and check if I can load properly extension into the Chrome by this steps:

Open chrome://extensions/ then Load Unpacked Extension and point to build directory in the source of project.

Image description

Here we go! Extension works. But this is only beginning.

Content script

Now I've created directory extension where I want to put all files related to work with browsing page.
Then created new file extension\content.ts and put sample code console.log('hello')

Now it will be good to compile this file as separate in output.

Edit config\webpack.config.js Go to line 205 and edit value of entry property

  entry: {
      main: paths.appIndexJs,
      contentScript: paths.contentScript
    },
Enter fullscreen mode Exit fullscreen mode

Then add in config\paths.js property with path to contentScript

 contentScript: resolveModule(resolveApp, 'extension/content'),
Enter fullscreen mode Exit fullscreen mode

Good... I have content script file in build directory but it contains hash which during development will make difficulties as it need to be statically typed into manifest.json

So for development process edit scripts\build.js and edit line 50 and set running configFactory with development parameter.

const config = configFactory('development');
Enter fullscreen mode Exit fullscreen mode

REMEMBER if this script will be used to make final build it needs to be switched back to production value.

Yes, the right solution is to put parameter with value of environment variable. But I will skip this for now as do t manually is enough for me.

Now I can check if everything is allright...

npm run build
Enter fullscreen mode Exit fullscreen mode

and the result is:

Image description

Of course! Webpack in development mode is set to put all files content to bundle.js so it needs to be updated.

Go to webpack.config.js line 216-218 and change development value of filename to be dynamic based on name of entries.

filename: isEnvProduction
        ? 'static/js/[name].[contenthash:8].js'
        : isEnvDevelopment && 'static/js/[name].js',
Enter fullscreen mode Exit fullscreen mode

Ooops next issue

Image description

It means I need to turn off fast refresh
Powershell

$env:FAST_REFRESH="false"  
Enter fullscreen mode Exit fullscreen mode

CMD

set FAST_REFRESH=false
Enter fullscreen mode Exit fullscreen mode

Voilà! Build success and now it is time to make an last update to manifest file to include content script.

{
  "name": "Chrome Extension",
  "description": "Chrome Extension Description",
  "version": "1.0",
  "manifest_version": 3,
  "action": {
    "default_popup": "index.html",
    "default_title": "Open the popup"
  },
  "content_scripts": [
    {
      "matches": [
        "https://*.com/*"
      ],
      "js": [
        "static/js/contentScript.js"
      ],
      "run_at": "document_end"
    }
  ]
}
Enter fullscreen mode Exit fullscreen mode

Refresh extension in Chrome and check console in devtools when enter for any page with com domain.

Image description

Preparation to publish

This part will be described later.

Top comments (0)