Introduction #

In this tutorial we will create a new Chrome Extension using Manifest V3 that allows us to create our own names for a URL or a set of URLs. This is useful when you have a set of URLs that you want to open at once, or you want to create a name for a URL that is hard to remember. Or if you just don’t want to use bookmarks. We will also save these shortlinks to the Chrome Sync key-value pair store. This extension will also allow the user to type commands when it is activated (in its popped up state). And we will use Typescript and React to build it.

Before we get started, here are some good references to take a look at:

  1. Official Chrome docs on writing an extension
  2. Template to build a Chrome extension without having to configure Typescript and React

🚀 Please star and fork / clone the Shortlink repo 🌟 Install the Chrome Extension 🛠️

Step 1: Create a new Chrome Extension #

The first thing to is to create a new repo on GitHub using this template repo.

You can do this in two ways.

  1. Using and a web browser. Here are the instructions on how to do this.
  2. Using the GitHub CLI. You must have the GitHub CLI installed and be logged in to (using gh auth login).
    # More info:
    gh repo create shortlink --public --template r3bl-org/chrome-extension-typescript-react-template
    # More info:
    gh repo clone shortlink

Step 2: Build and load the Chrome Extension #

At this point we have a shortlink git repo on our local machine that is setup to build a Chrome Extension. You can run the following command to build it.

npm install
npm run build

This will generate a dist/ directory that contains the Chrome Extension. You can load this into Chrome by:

  1. Type chrome:extensions in the URL bar.
  2. Turn on “Developer Mode”.
  3. Then click on “Load unpacked” and select the dist/ directory. Your extension will be loaded into Chrome.

Step 3: Add functionality #

In our extension we will ask for the minimum of permissions from the user. This ensures that our extension doesn’t have access to anything more than it needs. All of this is specified in the public/manifest.json file. Here’s an example of what this file might look like when we are done building our extension.

  "manifest_version": 3,
  "name": "R3BL Shortlink",
  "description": "Make go links",
  "version": "2.0",
  "icons": {
    "16": "icon16.png",
    "32": "icon32.png",
    "48": "icon48.png",
    "128": "icon128.png"
  "action": {
    "default_title": "Click to make go link for URL",
    "default_popup": "popup.html",
    "default_icon": {
      "16": "icon16.png",
      "32": "icon32.png",
      "48": "icon48.png",
      "128": "icon128.png"
  "omnibox": {
    "keyword": "go"
  "background": {
    "service_worker": "js/background.js"
  "commands": {
    "_execute_action": {
      "suggested_key": {
        "default": "Alt+L",
        "mac": "Alt+L"
      "description": "Make a go link for URL in address bar"
  "permissions": ["activeTab", "tabs", "storage", "clipboardWrite"]

Now that we have our permissions sorted, we can start by adding functionality to the extension. When we activate the extension by clicking its icon in the Chrome toolbar or by pressing the shortcut Alt+l the popup.tsx file will be run which itself is loaded by popup.html.

You can learn more about activating the extension and the popup in the chrome.browserAction docs.

This popup.tsx file will be the entry point for our extension. It is the main function in a node program or the App top level component in a React app. It sets up the UI and handles the user input events (key presses).

This is what the UI looks like on Linux on my machine: Shortlink Screenshot

This is what the file looks like in the real Shortcut extension: popup.tsx. If you go through this code, these are some of the things you will notice:

  1. The main() function just sets up the main React component Popup and mounts it to the DOM (div with id root).
  2. There are some useEffect() hooks which ensures that when changes, the global state is updated and the component is re-rendered. Learn more about in the API reference here. Another hook is responsible for painting the badge on the extension icon in the toolbar (when the Shortlink[] in the state changes).
  3. The Popup function component returns some JSX that is used to render the global state, which are two things: Shortlink[] and string. The Shortlink[] is used to render the list of shortcuts and the string is used to render the input field.
  4. The handleOnChange() and handleEnterKey() function is where the user input that is typed is interpreted into a command and then executed.

There are some other files of note. Please take a look at their linked source code.

  1. command.ts: The main logic for parsing a string into a command is handled by this file. The parseUserInputTextIntoCommand() function does all the work of converting a given string into a Command, and has a very Rust “vibe”. Please check out how this works. It makes it very easy to add or change commands in the future.
  2. storage.ts: This is where all the functions to manipulate the storage that syncs w/ Chrome accounts is located. Functions that handle shortlinks to be deleted, or added, or updated can all be found here. The Chrome storage API is async which is why the code in this file is written in the way that it is.
  3. omnibox.ts: This file works w/ background.ts to handle the omnibox functionality. The omnibox is the address bar in Chrome. When the user types go and then a space, the omnibox will be activated and the user can type in a shortcut. When the user presses Enter, the background.ts file will be run and the shortcut will be expanded to the full URL.

Step 4: Publish it #

Please read this guide on how to publish the extension. You will have to get a developer account, and then upload the extension binaries. There’s a script provided in this repo that will create a zip file that you can upload to the Chrome Web Store.

As part of publishing a version you have to provide justification for why you are requesting the permissions that you are. The fewer the permissions that you use, the better for the end user, and also for the review process to take less time.

🚀 Please star and fork / clone the Shortlink repo 🌟 Install the Chrome Extension 🛠️

If you would like to get involved in an open source project and like Chrome extensions, please feel free to contribute to the Shortlink repo. There are a lot of small features that need to be added. And they can be a nice stepping stone into the world of open source contribution 🎉.

Related Posts