DEV Community

Cover image for Chrome extension in 20 min
r7kamura
r7kamura

Posted on • Updated on

Chrome extension in 20 min

This article is the detailed text version of the video I recently posted:

I'll explain how to create a Chrome extension from scratch that turns a new tab into a notepad.

demo

I use the following tools in this tutorial, but you don't have to have used these tools before.

  • node.js (14.x or later)
  • typescript
  • css
  • vite
  • monaco-editor
  • types/chrome
  • crxjs/vite-plugin

Create a new vite project

First, create a new vite project by create-vite npm package.

npm init vite@2.9.0
Enter fullscreen mode Exit fullscreen mode

npm init x (or npm create x as an alias) runs npm exec create-x, so in this case you will be running npm exec create-vite. See npm help init for more detials.

As you may have noticed, I specified the version 2.9.0. This is because the current latest vite version 3.0.0 doesn't work well with crxjs/vite-plugin yet, so we need to choose the older version for now.

After running the command, you are ready to build the default HTML + CSS + JavaScript source.

npm install
npm run dev
Enter fullscreen mode Exit fullscreen mode

This command will continuously compile the source files and output files into ./dist/ directory, then start http://localhost:3000 as a preview HTTP server.

Create an empty Chrome extension

To create an empty Chrome extension, you need to write manifest.json.

{
  "name": "newtab-editor",
  "description": "Editor in new tab",
  "manifest_version": 3,
  "version": "0.0.1"
}
Enter fullscreen mode Exit fullscreen mode

However, only putting this file won't cause Vite to include manifest.json to build target.

To achieve this, you also need to write vite.config.ts.
The @crxjs/vite-plugin does all the tedious work, so all you have to do is to pass manifest.json to it.

import { crx } from "@crxjs/vite-plugin";
import manifest from "./src/manifest.json";

export default {
  plugins: [crx({ manifest })],
};
Enter fullscreen mode Exit fullscreen mode

To import crx, don't forget to install it:

npm install --save-dev @crxjs/vite-plugin
Enter fullscreen mode Exit fullscreen mode

After these steps, you can build the extension.
If you are still leaving npm run dev on, you should have a built Chrome extension in the ./dist/ directory.

Install the extension

To install the extension from local files, follow the steps below:

  1. open Google Chrome
  2. go to Chrome extensions page (chrome://extensions/)
  3. enable "Developer mode"
  4. click "Load unpacked extension"
  5. select the ./dist/ directory

chrome extensions page

Override new tab

To override new tab page by our extension, add chrome_url_overrides property to manifest.json.

"chrome_url_overrides": {
  "newtab": "index.html"
},
Enter fullscreen mode Exit fullscreen mode

After reloading the extension, you should see the index.html in the new tab.

index.html in new tab

Add monaco-editor

This time I will use an editor called monaco-editor. This is a Visual Studio Code-based editor, which allows you to achieve roughly the same functionality in your browser.

It is easy to use, just import and mount it on some HTML element and it works.

npm install --save monaco-editor
Enter fullscreen mode Exit fullscreen mode
// src/main.ts
import "./style.css";
import * as monaco from "monaco-editor";

const app = document.querySelector<HTMLDivElement>("#app")!;
monaco.editor.create(app, {
  fontSize: 18,
  language: "markdown",
  lineHeight: 1.6,
  minimap: { enabled: false },
  padding: { bottom: 16, top: 16 },
  theme: "vs-dark",
});
Enter fullscreen mode Exit fullscreen mode

After reloading the extension, you should see the editor in the new tab.

monaco-editor

Save content via storage API

To save editor content, we can use the storage API.

Unfortunately, however, it is not possible to call these APIs directly from index.html.
Instead, you need to have a background process to which these APIs are allowed and send requests to it.

First, the editor content is changed, send a request to the background process to save the content.

// src/main.ts
editor.onDidChangeModelContent(() => {
  const content = editor.getValue();
  chrome.runtime.sendMessage({ type: "saveContent", content });
});
Enter fullscreen mode Exit fullscreen mode

Second, when you opened the new tab, restore the content you saved.

chrome.runtime.sendMessage({ type: "loadContent" }, ({ content }) => {
  editor.setValue(content);
});
Enter fullscreen mode Exit fullscreen mode

Finally, prepare a backgruond process that accepts requests.

// src/background.ts
chrome.runtime.onMessage.addListener((request, _, sendResponse) => {
  switch (request.type) {
    case "loadContent":
      chrome.storage.sync.get("content", ({ content }) => {
        sendResponse({ content });
      });
      return true;
    case "saveContent":
      chrome.storage.sync.set({ content: request.content });
      break;
  }
  return;
});
Enter fullscreen mode Exit fullscreen mode

It's done!

demo


Wrapping up

In this tutorial, I explained how to create a Chrome extension from scratch that turns a new tab into a notepad.

The finished version of this Chrome extension is available on Chrome Web Store.

Of course, the source code is also available on GitHub. If you are interested, I'd recommend you to read the source code. It includes techniques such as debounce to reduce the amount of communication with background processes, removing unnecessary language functions to reduce bundled code size, and using Worker to improve performance.

I learned how to create Chrome extensions these days from @jacksteamdev's excellent article series. I would like to take this opportunity to thank you.

As noted at the beginning of this article, I'm also providing a live coding video version of this tutorial. If you have any questions, don't hesitate to ask, either in the comments of this article or in the comments of the video.

I will be posting several other coding videos, so if you are interested, please check out my channel.

Thanks for reading this far. If you have any questions, please ask anything in the comments.

Good coding!

Top comments (0)