In this blog, we will explore how to set up and develop a Chrome extension using TypeScript, React, Tailwind CSS, and Webpack. We will create a minimal extension called "NoteMe" ✍️ to put our understanding to the test. Our extension will include the following features:
In this blog, we will learn how to build a Chrome extension using modern technologies. This guide assumes that you already have some familiarity with building and uploading an extension to Chrome during local development. If you are new to this or need a detailed walkthrough of the basics, I recommend checking out my previous blog: Link
The extension will include the following components:
Below are screenshots showcasing how the extension will look upon completion:
Before diving into this tutorial, ensure you have the following tools installed on your system:
The figure above provides a high-level overview of the internal workings of this extension. Here are some key points we can derive from the diagram:
While Chrome extension projects don’t mandate a specific project structure, they do require a manifest.json file to be located at the root of the build directory. Taking advantage of this flexibility, we’ll define a custom project structure that helps organize different scripts effectively. This structure will enable better code reuse across scripts and minimize duplication, streamlining our development process.
To get started, we’ll set up a foundational directory structure for the project. You can use the following bash script to create the basic structure along with the manifest.json file:
#!/bin/bash bash_script_absolute_path=$(pwd) declare public_paths=("public" "public/assets" "public/assets/images") declare source_paths=("src" "src/lib" "src/scripts" "src/scripts/background" "src/scripts/content" "src/scripts/injected" "src/scripts/popup" "src/styles") declare public_directory_path="public" declare manifest_file="manifest.json" declare project_name="note-me" create_directory () { if [ ! -d "" ]; then mkdir fi } create_file () { if [ ! -e "/" ]; then touch / fi } create_public_directories () { for public_path in "${public_paths[@]}"; do create_directory $public_path done } create_source_directories () { for source_path in "${source_paths[@]}"; do create_directory $source_path done } execute () { echo "creating project struture at "${bash_script_absolute_path} create_directory $project_name cd $bash_script_absolute_path"/"$project_name create_public_directories create_source_directories create_file $manifest_file $public_directory_path echo "done creating project struture at "${bash_script_absolute_path}" with project name "$project_name } execute
Ensure that your directory structure resembles the one shown in the screenshot below.
{ "manifest_version": 3, "name": "NoteMe", "version": "1.0", "description": "A Chrome extension built with React and TypeScript using Webpack.", "action": { "default_popup": "popup.html", "default_icon": "app-icon.png" }, "background": { "service_worker": "background.js", "type": "module" }, "content_scripts": [ { "matches": ["<all_urls>"], "js": ["content.js"], "run_at": "document_end" } ], "permissions": [ "storage", "activeTab", "scripting", "webNavigation" ], "host_permissions": ["<all_urls>"], "web_accessible_resources": [ { "resources": ["styles.css", "sidebar-open.png", "sidebar-close.png"], "matches": ["<all_urls>"] } ] }
Points to note:
Create following files which are referenced in the manifest.json: backgroun.ts, content.ts and popup.html.
Add the following code to the popup.html file in the public directory:
#!/bin/bash bash_script_absolute_path=$(pwd) declare public_paths=("public" "public/assets" "public/assets/images") declare source_paths=("src" "src/lib" "src/scripts" "src/scripts/background" "src/scripts/content" "src/scripts/injected" "src/scripts/popup" "src/styles") declare public_directory_path="public" declare manifest_file="manifest.json" declare project_name="note-me" create_directory () { if [ ! -d "" ]; then mkdir fi } create_file () { if [ ! -e "/" ]; then touch / fi } create_public_directories () { for public_path in "${public_paths[@]}"; do create_directory $public_path done } create_source_directories () { for source_path in "${source_paths[@]}"; do create_directory $source_path done } execute () { echo "creating project struture at "${bash_script_absolute_path} create_directory $project_name cd $bash_script_absolute_path"/"$project_name create_public_directories create_source_directories create_file $manifest_file $public_directory_path echo "done creating project struture at "${bash_script_absolute_path}" with project name "$project_name } execute
Note:
The code above installs two listeners:
Additionally, the import statement brings in listeners from the src/lib directory. The core app logic is built in src/lib, enabling reuse across different contexts (e.g., content and background scripts).
The src/lib directory houses the core logic of the extension. Below is an overview of its structure and key components:
For detailed implementation, please refer to the actual code in the repository.
In this step, we mount the React components for rendering. These components are mounted in two different scripts:src/scripts/content/content.ts and src/scripts/popup/popup.ts.
Popup Script: Found in src/scripts/popup/popup.ts.
#!/bin/bash bash_script_absolute_path=$(pwd) declare public_paths=("public" "public/assets" "public/assets/images") declare source_paths=("src" "src/lib" "src/scripts" "src/scripts/background" "src/scripts/content" "src/scripts/injected" "src/scripts/popup" "src/styles") declare public_directory_path="public" declare manifest_file="manifest.json" declare project_name="note-me" create_directory () { if [ ! -d "" ]; then mkdir fi } create_file () { if [ ! -e "/" ]; then touch / fi } create_public_directories () { for public_path in "${public_paths[@]}"; do create_directory $public_path done } create_source_directories () { for source_path in "${source_paths[@]}"; do create_directory $source_path done } execute () { echo "creating project struture at "${bash_script_absolute_path} create_directory $project_name cd $bash_script_absolute_path"/"$project_name create_public_directories create_source_directories create_file $manifest_file $public_directory_path echo "done creating project struture at "${bash_script_absolute_path}" with project name "$project_name } execute
Content Script: Found in src/scripts/content/content.ts.
{ "manifest_version": 3, "name": "NoteMe", "version": "1.0", "description": "A Chrome extension built with React and TypeScript using Webpack.", "action": { "default_popup": "popup.html", "default_icon": "app-icon.png" }, "background": { "service_worker": "background.js", "type": "module" }, "content_scripts": [ { "matches": ["<all_urls>"], "js": ["content.js"], "run_at": "document_end" } ], "permissions": [ "storage", "activeTab", "scripting", "webNavigation" ], "host_permissions": ["<all_urls>"], "web_accessible_resources": [ { "resources": ["styles.css", "sidebar-open.png", "sidebar-close.png"], "matches": ["<all_urls>"] } ] }
Adding the configurations required for compiling and building the extension
To successfully compile and build the extension, we need to configure the following files:
These configurations handle the TypeScript compilation, Tailwind CSS integration, and the overall Webpack build process for the extension.
Here are some screenshots captured during the testing of the extension.
Here are a few key takeaways from this blog,
In the future, I plan to work on another blog where we will explore the process of publishing a fully functional Chrome extension to the Chrome Web Store. The goal of that blog will be to:
Thank you for taking the time to read this blog! Your interest and support mean so much to me. I’m excited to share more insights as I continue this journey.
Happy coding!
github link: https://github.com/gauravnadkarni/chrome-extension-starter-app
This article was originally published on Medium.
The above is the detailed content of Chrome Extension Development - Develop minimal app with TypeScript, React, Tailwind CSS and Webpack. For more information, please follow other related articles on the PHP Chinese website!