{"id":308141,"date":"2020-05-19T07:38:42","date_gmt":"2020-05-19T14:38:42","guid":{"rendered":"https:\/\/css-tricks.com\/?p=308141"},"modified":"2020-05-19T20:31:47","modified_gmt":"2020-05-20T03:31:47","slug":"how-to-build-a-chrome-extension","status":"publish","type":"post","link":"https:\/\/css-tricks.com\/how-to-build-a-chrome-extension\/","title":{"rendered":"How to Build a Chrome Extension"},"content":{"rendered":"\n

I made a Chrome extension this weekend because I found I was doing the same task over and over and wanted to automate it. Plus, I\u2019m a nerd living through a pandemic, so I spend my pent-up energy building things. I’ve made a few Chrome Extensions over the years, hope this post helps you get going, too. Let\u2019s get started!<\/p>\n\n\n\n\n\n\n

Create the manifest<\/h3>\n\n\n

The first step is creating a manifest.json<\/code> file in a project folder. This serves a similar purpose to a package.json<\/code>, it provides the Chrome Web Store with critical information about the project, including the name, version, the required permissions, and so forth. Here\u2019s an example:<\/p>\n\n\n\n

{\n \"manifest_version\": 2,\n \"name\": \"Sample Name\",\n \"version\": \"1.0.0\",\n \"description\": \"This is a sample description\",\n \"short_name\": \"Short Sample Name\",\n \"permissions\": [\"activeTab\", \"declarativeContent\", \"storage\", \"<all_urls>\"],\n \"content_scripts\": [\n   {\n     \"matches\": [\"<all_urls>\"],\n     \"css\": [\"background.css\"],\n     \"js\": [\"background.js\"]\n   }\n ],\n \"browser_action\": {\n   \"default_title\": \"Does a thing when you do a thing\",\n   \"default_popup\": \"popup.html\",\n   \"default_icon\": {\n     \"16\": \"icons\/icon16.png\",\n     \"32\": \"icons\/icon32.png\"\n   }\n }\n}<\/code><\/pre>\n\n\n\n

You might notice a few things- first: the names<\/strong> and descriptions<\/strong> can be anything you’d like.<\/p>\n\n\n\n

The permissions<\/strong> depend on what the extension needs to do. We have [\"activeTab\", \"declarativeContent\", \"storage\", \"<all_urls>\"]<\/code> in this example because this particular extension needs information about the active tab, needs to change the page content, needs to access localStorage<\/code>, and needs to be active on all sites. If it only needs it to be active on one site at a time, we can remove the last index of that array. <\/p>\n\n\n\n

A list of all of the permissions and what they mean can be found in Chrome’s extension docs<\/a>.<\/p>\n\n\n\n

\"content_scripts\": [\n  {\n    \"matches\": [\"<all_urls>\"],\n    \"css\": [\"background.css\"],\n    \"js\": [\"background.js\"]\n  }\n],<\/code><\/pre>\n\n\n\n

The content_scripts<\/code> section sets the sites where the extension should be active. If you want a single site, like Twitter for example, you would say [\"https:\/\/twitter.com\/*\"]<\/code>. The CSS and JavaScript files are everything needed for extensions. For instance, my productive Twitter extension<\/a> uses these files to override Twitter\u2019s default appearance.<\/p>\n\n\n\n

\"browser_action\": {\n  \"default_title\": \"Does a thing when you do a thing\",\n  \"default_popup\": \"popup.html\",\n  \"default_icon\": {\n    \"16\": \"icons\/icon16.png\",\n    \"32\": \"icons\/icon32.png\"\n  }\n}<\/code><\/pre>\n\n\n\n

There are things in browser_action<\/code> that are also optional. For example, if the extension doesn\u2019t need a popup for its functionality, then both the default_title<\/code> and default_popup<\/code> can be removed. In that case, all that’s needed the icon for the extension. If the extension only works on some sites, then Chrome will grey out the icon when it\u2019s inactive.<\/p>\n\n\n

Debugging<\/h3>\n\n\n

Once the manifest, CSS and JavaScript files are ready, head over to chrome:\/\/extensions\/<\/code>from the browser’s address bar and enable developer mode. That activates the “Load unpacked” button to add the extension files. It’s also possible to toggle whether or not the developer version of the extension is active.<\/p>\n\n\n\n

\"\"<\/figure>\n\n\n\n

I would highly recommend starting a GitHub repository to version control the files at this point. It’s a good way to save the work.<\/p>\n\n\n\n

The extension needs to be reloaded from this interface when it is updated. A little refresh icon will display on the screen. Also, if the extension has any errors during development, it will show an error button with a stack trace and more info here as well.<\/p>\n\n\n

Popup functionality<\/h3>\n\n\n

If the extension need to make use of a popup that comes off the extension icon, it\u2019s thankfully fairly straightforward. After designating the name of the file with browser_action<\/code> in the manifest file, a page can be built with whatever HTML and CSS you’ll like to include, including images (I tend to use inline SVG).<\/p>\n\n\n\n

We\u2019ll probably want to add some functionality to a popup. That make take some JavaScript, so make sure the JavaScript file is designated in the manifest file and is linked up in your popup file as well, like this: <script src=\"background.js\"><\/script><\/code><\/p>\n\n\n\n

In that file, start by creating functionality and we’ll have access to the popup DOM like this:<\/p>\n\n\n\n

document.addEventListener(\"DOMContentLoaded\", () => {\n var button = document.getElementById(\"submit\")\n\n button.addEventListener(\"click\", (e) => {\n   console.log(e)\n })\n})<\/code><\/pre>\n\n\n\n

If we create a button in the popup.html<\/code> file, assign it an ID called submit<\/code>, and then return a console log, you might notice that nothing is actually logged in the console. That\u2019s because we\u2019re in a different context, meaning we\u2019ll need to right-click on the popup and open up a different set of DevTools.<\/p>\n\n\n\n

\"Showing<\/figure>\n\n\n\n

We now have access to logging and debugging! Keep in mind, though, that if anything is set in localStorage<\/code>, then it will only exist in the extension\u2019s DevTools localStorage<\/code>; not the user\u2019s browser localStorage<\/code>. (This bit me the first time I tried it!)<\/p>\n\n\n

Running scripts outside the extension<\/h3>\n\n\n

This is all fine and good, but say we want to run a script that has access to information on the current tab? Here\u2019s a couple of ways we would do this. I would typically call a separate function from inside the DOMContentLoaded<\/code> event listener:<\/p>\n\n\n

Example 1: Activate a file<\/strong><\/h4>\n\n\n
function exampleFunction() {\n chrome.tabs.executeScript(() => {\n   chrome.tabs.executeScript({ file: \"content.js\" })\n })\n}<\/code><\/pre>\n\n\n

Example 2: Execute just a bit of code<\/strong><\/h4>\n\n\n

This way is great if there’s only a small bit of code to run. However, it quickly gets tough to work with since it requires passing everything as a string or template literal.<\/p>\n\n\n\n

function exampleFunction() {\n chrome.tabs.executeScript({\n   code: `console.log(\u2018hi there\u2019)`\n  })\n}<\/code><\/pre>\n\n\n

Example 3: Activate a file and pass a parameter<\/strong><\/h4>\n\n\n

Remember, the extension and tab are operating in different contexts. That makes passing parameters between them a not-so-trivial task. What we\u2019ll do here is nest the first two examples to pass a bit of code into the second file. I will store everything I need in a single option, but we\u2019ll have to stringify the object for that to work properly.<\/p>\n\n\n\n

function exampleFunction(options) {\n chrome.tabs.executeScript(\n   { code: \"var options = \" + JSON.stringify(options) },\n   function() {\n     chrome.tabs.executeScript({ file: \"content.js\" })\n   }\n )\n}<\/code><\/pre>\n\n\n

Icons<\/h3>\n\n\n

Even though the manifest file only defines two icons, we need two more to officially submit the extension to the Chrome Web Store: one that\u2019s 128px square, and one that I call icon128_proper.png<\/code>, which is also 128px, but has a little padding inside it between the edge of the image and the icon.<\/p>\n\n\n\n

Keep in mind that whatever icon is used needs to look good both in light mode and dark mode for the browser. I usually find my icons on the Noun Project<\/a>.<\/p>\n\n\n

Submitting to the Chrome Web Store<\/h3>\n\n\n

Now we get to head over to the Chrome Web Store developer console<\/a> to submit the extension! Click the \u201cNew Item\u201d button, the drag and drop the zipped project file into the uploader.<\/p>\n\n\n\n

\"\"<\/figure>\n\n\n\n

From there, Chrome will ask a few questions about the extension, request information about the permissions requested in the extension and why they’re needed. Fair warning:<\/strong> requesting \u201cactiveTab\u201d<\/code> or \u201ctabs\u201d<\/code> permissions will require a longer review to make sure the code isn\u2019t doing anything abusive.<\/p>\n\n\n\n

That\u2019s it! This should get you all set up and on your way to building a Chrome browser extension! 🎉<\/p>\n","protected":false},"excerpt":{"rendered":"

I made a Chrome extension this weekend because I found I was doing the same task over and over and wanted to automate it. Plus, I\u2019m a nerd living through a pandemic, so I spend my pent-up energy building things. I’ve made a few Chrome Extensions over the years, hope this post helps you get […]<\/p>\n","protected":false},"author":7699,"featured_media":308338,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"_bbp_topic_count":0,"_bbp_reply_count":0,"_bbp_total_topic_count":0,"_bbp_total_reply_count":0,"_bbp_voice_count":0,"_bbp_anonymous_reply_count":0,"_bbp_topic_count_hidden":0,"_bbp_reply_count_hidden":0,"_bbp_forum_subforum_count":0,"sig_custom_text":"","sig_image_type":"featured-image","sig_custom_image":0,"sig_is_disabled":false,"inline_featured_image":false,"c2c_always_allow_admin_comments":false,"footnotes":"","jetpack_publicize_message":"","jetpack_is_tweetstorm":false,"jetpack_publicize_feature_enabled":true,"jetpack_social_post_already_shared":true,"jetpack_social_options":[]},"categories":[4],"tags":[1022,14710,1021,14712,14711],"jetpack_publicize_connections":[],"acf":[],"jetpack_featured_media_url":"https:\/\/i0.wp.com\/css-tricks.com\/wp-content\/uploads\/2020\/05\/chrome-web-store-extensions.png?fit=1200%2C600&ssl=1","jetpack-related-posts":[{"id":363079,"url":"https:\/\/css-tricks.com\/nextjs-chrome-extension-starter\/","url_meta":{"origin":308141,"position":0},"title":"Helpful Tips for Starting a Next.js Chrome Extension","date":"February 10, 2022","format":false,"excerpt":"I recently rewrote one of my projects \u2014 Minimal Theme for Twitter \u2014 as a Next.js Chrome extension because I wanted to use React for the pop-up. Using React would allow me to clearly separate my extension\u2019s pop-up component and its application logic from its content scripts, which are the\u2026","rel":"","context":"In "Article"","img":{"alt_text":"","src":"https:\/\/i0.wp.com\/css-tricks.com\/wp-content\/uploads\/2022\/02\/s_EC3F93C336EF3FAA64B8384642A6F175FADA3A235447EC3A34E69E391074F1AA_1642397608126_CleanShot2022-01-17at00.32.342x.png?fit=1200%2C796&ssl=1&resize=350%2C200","width":350,"height":200},"classes":[]},{"id":376454,"url":"https:\/\/css-tricks.com\/how-to-transition-to-manifest-v3-for-chrome-extensions\/","url_meta":{"origin":308141,"position":1},"title":"How to Transition to Manifest V3 for Chrome Extensions","date":"January 19, 2023","format":false,"excerpt":"While I am not a regular Chrome extension programmer, I have certainly coded enough extensions and have a wide enough web development portfolio to know my way around the task. However, just recently, I had a client reject one of my extensions as I received feedback that my extension was\u2026","rel":"","context":"In "Article"","img":{"alt_text":"","src":"https:\/\/i0.wp.com\/css-tricks.com\/wp-content\/uploads\/2018\/09\/chrome-browser-extension-blueprint.jpg?fit=1200%2C600&ssl=1&resize=350%2C200","width":350,"height":200},"classes":[]},{"id":157577,"url":"https:\/\/css-tricks.com\/colorpeek-part-2-building-first-chrome-extension\/","url_meta":{"origin":308141,"position":2},"title":"Colorpeek, Part 2: Building Your First Chrome Extension","date":"December 3, 2013","format":false,"excerpt":"The following is a guest post by Tyler Sticka. Tyler created a tool called Colorpeek. Yesterday we looked at the what and why of things. Today we'll look at how he built the Chrome Extension, which will serve as a great tutorial on how you can get started building your\u2026","rel":"","context":"In "Article"","img":{"alt_text":"","src":"","width":0,"height":0},"classes":[]},{"id":276639,"url":"https:\/\/css-tricks.com\/control-the-internet-with-chrome-extensions\/","url_meta":{"origin":308141,"position":3},"title":"Control the Internet With Chrome Extensions!","date":"September 24, 2018","format":false,"excerpt":"As a web UI developer and designer, there are countless things to learn and only so many hours in the day. There are topics I\u2019ve purposefully avoided, like mobile and offline application development because, at some point, you have to draw a line somewhere in the millions of shiny new\u2026","rel":"","context":"In "Article"","img":{"alt_text":"","src":"https:\/\/i0.wp.com\/css-tricks.com\/wp-content\/uploads\/2018\/09\/chrome-browser-extension-blueprint.jpg?fit=1200%2C600&ssl=1&resize=350%2C200","width":350,"height":200},"classes":[]},{"id":249792,"url":"https:\/\/css-tricks.com\/xvg\/","url_meta":{"origin":308141,"position":4},"title":"xvg","date":"January 7, 2017","format":false,"excerpt":"Varun Vachhar: A Chrome extension for debugging SVG paths by converting them to outlines and displaying anchors, control points, handles and arc ellipses. An amazing contribution to this open source project would be to make all those points draggable, and then be able to spit out the newly adjusted code.\u2026","rel":"","context":"In "Link"","img":{"alt_text":"","src":"","width":0,"height":0},"classes":[]},{"id":359615,"url":"https:\/\/css-tricks.com\/how-to-create-a-browser-extension\/","url_meta":{"origin":308141,"position":5},"title":"How to Create a Browser Extension","date":"January 3, 2022","format":false,"excerpt":"I\u2019ll bet you are using browser extensions right now. Some of them are extremely popular and useful, like ad blockers, password managers, and PDF viewers. These extensions (or \"add-ons\") are not limited to those purposes \u2014 you can do a lot more with them! In this article, I will give\u2026","rel":"","context":"In "Article"","img":{"alt_text":"","src":"https:\/\/i0.wp.com\/css-tricks.com\/wp-content\/uploads\/2021\/12\/unnamed-file.png?fit=1200%2C600&ssl=1&resize=350%2C200","width":350,"height":200},"classes":[]}],"_links":{"self":[{"href":"https:\/\/css-tricks.com\/wp-json\/wp\/v2\/posts\/308141"}],"collection":[{"href":"https:\/\/css-tricks.com\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/css-tricks.com\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/css-tricks.com\/wp-json\/wp\/v2\/users\/7699"}],"replies":[{"embeddable":true,"href":"https:\/\/css-tricks.com\/wp-json\/wp\/v2\/comments?post=308141"}],"version-history":[{"count":11,"href":"https:\/\/css-tricks.com\/wp-json\/wp\/v2\/posts\/308141\/revisions"}],"predecessor-version":[{"id":311343,"href":"https:\/\/css-tricks.com\/wp-json\/wp\/v2\/posts\/308141\/revisions\/311343"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/css-tricks.com\/wp-json\/wp\/v2\/media\/308338"}],"wp:attachment":[{"href":"https:\/\/css-tricks.com\/wp-json\/wp\/v2\/media?parent=308141"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/css-tricks.com\/wp-json\/wp\/v2\/categories?post=308141"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/css-tricks.com\/wp-json\/wp\/v2\/tags?post=308141"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}