Chrome extension in 20 min
r7kamura
Posted on July 19, 2022
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.
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
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
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"
}
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 })],
};
To import crx, don't forget to install it:
npm install --save-dev @crxjs/vite-plugin
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:
- open Google Chrome
- go to Chrome extensions page (chrome://extensions/)
- enable "Developer mode"
- click "Load unpacked extension"
- select the ./dist/ directory
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"
},
After reloading the extension, you should see the index.html in the 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
// 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",
});
After reloading the extension, you should see the editor in the new tab.
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 });
});
Second, when you opened the new tab, restore the content you saved.
chrome.runtime.sendMessage({ type: "loadContent" }, ({ content }) => {
editor.setValue(content);
});
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;
});
It's done!
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!
Posted on July 19, 2022
Join Our Newsletter. No Spam, Only the good stuff.
Sign up to receive the latest update from our blog.