{"componentChunkName":"component---src-gatsby-theme-try-ghost-templates-post-js","path":"/chrome-extension-tutorial-replace-images-in-any-website-with-pikachu/","result":{"data":{"customPost":{"id":"Ghost__Post__6127ba1b3ed159214d382e71","title":"Chrome Extension Tutorial — Replace Images in Any Website with Pikachu","slug":"chrome-extension-tutorial-replace-images-in-any-website-with-pikachu","featured":false,"feature_image":"https://res-3.cloudinary.com/hbqmf3mbz/image/upload/q_auto/v1/ghost-blog-images/chrome-extension.gif","excerpt":"What’s a better way to learn how to make a Chrome extension than making the web cuter with Pikachu images?","custom_excerpt":"What’s a better way to learn how to make a Chrome extension than making the web cuter with Pikachu images?","visibility":"public","created_at_pretty":"6 Nov 2020","published_at_pretty":"11 Apr 2019","updated_at_pretty":"26 Aug 2021","created_at":"2020-11-06T18:32:10.000+00:00","published_at":"2019-04-11T18:30:00.000+00:00","updated_at":"2021-08-26T17:55:09.000+00:00","meta_title":null,"meta_description":null,"og_description":null,"og_image":null,"og_title":null,"twitter_description":null,"twitter_image":null,"twitter_title":null,"authors":[{"slug":"shahed","url":"https://backend.shahednasser.com/author/shahed/","name":"Shahed Nasser","bio":null,"cover_image":null,"profile_image":"https://backend.shahednasser.com/content/images/2022/03/IMG_0591.jpg","location":null,"website":null,"twitter":null,"facebook":null,"meta_title":null,"meta_description":null,"coverImageSharp":null,"profileImageSharp":null}],"primary_author":{"slug":"shahed","url":"https://backend.shahednasser.com/author/shahed/","name":"Shahed Nasser","bio":null,"cover_image":null,"profile_image":"https://backend.shahednasser.com/content/images/2022/03/IMG_0591.jpg","location":null,"website":null,"twitter":null,"facebook":null,"meta_title":null,"meta_description":null,"coverImageSharp":null,"profileImageSharp":{"base":"IMG_0591.jpg","publicURL":"/static/ceb49c3c631485453e71e00d7f84b069/IMG_0591.jpg","imageMeta":{"width":1182,"height":1179},"childImageSharp":{"fluid":{"base64":"data:image/jpeg;base64,/9j/2wBDABALDA4MChAODQ4SERATGCgaGBYWGDEjJR0oOjM9PDkzODdASFxOQERXRTc4UG1RV19iZ2hnPk1xeXBkeFxlZ2P/2wBDARESEhgVGC8aGi9jQjhCY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2P/wgARCAAUABQDASIAAhEBAxEB/8QAGAABAQEBAQAAAAAAAAAAAAAAAAMEAQL/xAAWAQEBAQAAAAAAAAAAAAAAAAADBAL/2gAMAwEAAhADEAAAAdXiFM6i0CohUWXoKn//xAAcEAACAgIDAAAAAAAAAAAAAAACAwESBBEhM0H/2gAIAQEAAQUCWySE3WEr7SzbXjAj4iKty+sOQ//EABYRAQEBAAAAAAAAAAAAAAAAAAERIP/aAAgBAwEBPwEhj//EABQRAQAAAAAAAAAAAAAAAAAAACD/2gAIAQIBAT8BH//EAB4QAAIBBAMBAAAAAAAAAAAAAAABIRESMUECECJx/9oACAEBAAY/ApVGWvOjzgtUwLlTZA0sdL4f/8QAHBAAAwACAwEAAAAAAAAAAAAAAAERITFBkbHB/9oACAEBAAE/IahkCy+N2GwZpjQiJHJCspUFY0QrSi+HqiW2rgf/2gAMAwEAAgADAAAAEPw3/wD/xAAYEQEBAAMAAAAAAAAAAAAAAAAAARExQf/aAAgBAwEBPxCtjDqP/8QAFxEBAQEBAAAAAAAAAAAAAAAAAQAxEf/aAAgBAgEBPxBFus6Tt//EAB8QAQEAAgIBBQAAAAAAAAAAAAERACExQWFRcYGR0f/aAAgBAQABPxAuaBPPzkO1wyX7F4wkwXanfZrFQgeqE9JgS14vVOvrERIJomVBKwt2jebAeP0yVa8h1n//2Q==","aspectRatio":1,"src":"/static/ceb49c3c631485453e71e00d7f84b069/31709/IMG_0591.jpg","srcSet":"/static/ceb49c3c631485453e71e00d7f84b069/f340b/IMG_0591.jpg 28w,\n/static/ceb49c3c631485453e71e00d7f84b069/22d64/IMG_0591.jpg 55w,\n/static/ceb49c3c631485453e71e00d7f84b069/31709/IMG_0591.jpg 110w,\n/static/ceb49c3c631485453e71e00d7f84b069/aa249/IMG_0591.jpg 165w,\n/static/ceb49c3c631485453e71e00d7f84b069/0dc33/IMG_0591.jpg 220w,\n/static/ceb49c3c631485453e71e00d7f84b069/d8257/IMG_0591.jpg 1182w","srcWebp":"/static/ceb49c3c631485453e71e00d7f84b069/8678c/IMG_0591.webp","srcSetWebp":"/static/ceb49c3c631485453e71e00d7f84b069/59cda/IMG_0591.webp 28w,\n/static/ceb49c3c631485453e71e00d7f84b069/7da75/IMG_0591.webp 55w,\n/static/ceb49c3c631485453e71e00d7f84b069/8678c/IMG_0591.webp 110w,\n/static/ceb49c3c631485453e71e00d7f84b069/f282e/IMG_0591.webp 165w,\n/static/ceb49c3c631485453e71e00d7f84b069/a7b21/IMG_0591.webp 220w,\n/static/ceb49c3c631485453e71e00d7f84b069/63099/IMG_0591.webp 1182w","sizes":"(max-width: 110px) 100vw, 110px"}}}},"primary_tag":{"slug":"browser-extensions","url":"https://backend.shahednasser.com/tag/browser-extensions/","name":"Browser Extensions","visibility":"public","feature_image":"https://backend.shahednasser.com/content/images/2022/01/photo-1616499370260-485b3e5ed653-2.jpeg","description":"Learn more about Browser Extensions through tutorials, articles, and tips.","meta_title":null,"meta_description":null,"featureImageSharp":{"base":"photo-1616499370260-485b3e5ed653-2.jpeg","publicURL":"/static/17d3062927434b0d9d22f3e45ece68b8/photo-1616499370260-485b3e5ed653-2.jpeg","imageMeta":{"width":2000,"height":1334},"childImageSharp":{"fluid":{"base64":"data:image/jpeg;base64,/9j/2wBDABALDA4MChAODQ4SERATGCgaGBYWGDEjJR0oOjM9PDkzODdASFxOQERXRTc4UG1RV19iZ2hnPk1xeXBkeFxlZ2P/2wBDARESEhgVGC8aGi9jQjhCY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2P/wgARCAANABQDASIAAhEBAxEB/8QAGAAAAgMAAAAAAAAAAAAAAAAAAAMCBAX/xAAVAQEBAAAAAAAAAAAAAAAAAAAAAv/aAAwDAQACEAMQAAABQ+hsRUBAf//EABoQAQADAAMAAAAAAAAAAAAAAAIAAQMEEyL/2gAIAQEAAQUCKpA3nefSo/JALzvlaXP/xAAVEQEBAAAAAAAAAAAAAAAAAAAQEf/aAAgBAwEBPwGH/8QAFREBAQAAAAAAAAAAAAAAAAAAERD/2gAIAQIBAT8BSf/EAB4QAAEDBAMAAAAAAAAAAAAAAAEAAiEDEBEiMVFh/9oACAEBAAY/Ai1x9CG2/VmEcoVCJwogL//EABoQAQEBAQADAAAAAAAAAAAAAAERACFBcYH/2gAIAQEAAT8h6pD2YW6ifbqrwMDk3W6VFUxyFZ4ib//aAAwDAQACAAMAAAAQLB//xAAXEQEAAwAAAAAAAAAAAAAAAAAAAREh/9oACAEDAQE/EJs1/8QAFREBAQAAAAAAAAAAAAAAAAAAABH/2gAIAQIBAT8QFf/EABwQAQEAAwEAAwAAAAAAAAAAAAERACExQVFhgf/aAAgBAQABPxCsM1ex6ZRBIol9InPbigkXRbrDW2E9042KoTWHL8fWeOKEM/Xuf//Z","aspectRatio":1.5028901734104045,"src":"/static/17d3062927434b0d9d22f3e45ece68b8/d5c54/photo-1616499370260-485b3e5ed653-2.jpg","srcSet":"/static/17d3062927434b0d9d22f3e45ece68b8/65d8c/photo-1616499370260-485b3e5ed653-2.jpg 260w,\n/static/17d3062927434b0d9d22f3e45ece68b8/c5f21/photo-1616499370260-485b3e5ed653-2.jpg 520w,\n/static/17d3062927434b0d9d22f3e45ece68b8/d5c54/photo-1616499370260-485b3e5ed653-2.jpg 1040w,\n/static/17d3062927434b0d9d22f3e45ece68b8/81a53/photo-1616499370260-485b3e5ed653-2.jpg 1560w,\n/static/17d3062927434b0d9d22f3e45ece68b8/4e5f3/photo-1616499370260-485b3e5ed653-2.jpg 2000w","srcWebp":"/static/17d3062927434b0d9d22f3e45ece68b8/e4875/photo-1616499370260-485b3e5ed653-2.webp","srcSetWebp":"/static/17d3062927434b0d9d22f3e45ece68b8/dc8f3/photo-1616499370260-485b3e5ed653-2.webp 260w,\n/static/17d3062927434b0d9d22f3e45ece68b8/2db4b/photo-1616499370260-485b3e5ed653-2.webp 520w,\n/static/17d3062927434b0d9d22f3e45ece68b8/e4875/photo-1616499370260-485b3e5ed653-2.webp 1040w,\n/static/17d3062927434b0d9d22f3e45ece68b8/f5845/photo-1616499370260-485b3e5ed653-2.webp 1560w,\n/static/17d3062927434b0d9d22f3e45ece68b8/49d6b/photo-1616499370260-485b3e5ed653-2.webp 2000w","sizes":"(max-width: 1040px) 100vw, 1040px"}}}},"tags":[{"slug":"browser-extensions","url":"https://backend.shahednasser.com/tag/browser-extensions/","name":"Browser Extensions","visibility":"public","feature_image":"https://backend.shahednasser.com/content/images/2022/01/photo-1616499370260-485b3e5ed653-2.jpeg","description":"Learn more about Browser Extensions through tutorials, articles, and tips.","meta_title":null,"meta_description":null,"featureImageSharp":null},{"slug":"beginner","url":"https://backend.shahednasser.com/tag/beginner/","name":"Beginners","visibility":"public","feature_image":"https://backend.shahednasser.com/content/images/2022/01/photo-1521185496955-15097b20c5fe-2.jpeg","description":"Tutorials, articles, and tips to help beginners accelerate their journey in programming.","meta_title":"Beginners","meta_description":"Tutorials, articles, and tips to help beginners accelerate their journey in programming.","featureImageSharp":null},{"slug":"js","url":"https://backend.shahednasser.com/tag/js/","name":"Javascript","visibility":"public","feature_image":"https://backend.shahednasser.com/content/images/2022/01/photo-1592609931095-54a2168ae893-2.jpeg","description":"Learn more about Javascript through tutorials, articles, and tips.","meta_title":null,"meta_description":"Learn more about Javascript through tutorials, articles and tips.","featureImageSharp":null}],"plaintext":"This article was also published on Level Up Coding's gitconnected\n[https://levelup.gitconnected.com/chrome-extension-tutorial-replace-images-in-any-website-with-pikachu-de2a6e3548bb]\n\nWhat’s a better way to learn how to make a Chrome extension than making the web\ncuter with Pikachu images?\n\nIn this tutorial, I will show you how you can create a simple Chrome extension\nthat replaces images on websites with Pikachu images. We will discuss topics\nlike content scripts, background scripts, and messages between scripts in Chrome\nextensions.\n\nThe code for this tutorial is available here\n[https://github.com/shahednasser/pikachu-everywhere].\n\nFirst thing you need to do when creating any chrome extension is start with\ncreating a manifest.json file in the root of the extension’s directory. For now,\nthis file will hold only the following basic information about your extension:\n\nHere is what everything in this file means:\n\n 1. name: The name of the extension. I named the extension “Pikachu Everywhere”\n 2. version: This is the version of the extension. When you publish the\n    extension in the Chrome Web Store and you need to update it, you will have\n    to increase the version number so it’s good to start with a low number.\n 3. description: (Optional) Description of your extension\n 4. manifest_version: As of Chrome 18, developers are required to use manifest\n    version 2, since manifest version 1 is now deprecated.\n 5. icons: (Optional) Here we specify the extension’s icon. This icon will show\n    up in your browser’s toolbar, the Chrome Web Store, and any other place that\n    requires to show it. For that reason, it’s good to include different sizes\n    of the icon. I have used an icon from Free Icons PNG\n    [https://www.freeiconspng.com/img/17360] and resized the icons using \n    https://resizeimage.net/ (You can find the images in the GitHub repository\n    [https://github.com/shahednasser/pikachu-everywhere]). The paths specified\n    for these images is relative to the root of the extension.\n\nWith this, we have the core of any Chrome extension. Next, we’ll add the\nextension to the browser.\n\nGo to chrome://extensions. There, you can toggle developer mode at the top right\ncorner. Once you do that, a new toolbar will show up with three buttons: “Load\nunpacked,” “Pack extension,” and “Update.” Click on “Load unpacked” and choose\nyour extension’s directory.\n\nOnce you do that, you will see that your new chrome extension is now working.\nYes, it does nothing, but it’s working.\n\nIn Chrome extensions, there are two types of scripts you can use. There are\nBackground scripts and Content scripts. Background scripts run (obviously) in\nthe background. They are loaded when needed and unloaded when idle. They are\nused to listen to certain events in your Chrome extension.\n\nContent scripts are scripts loaded once you open a website and injected it into\nit. Once you open a website, they will start running just like any other script\non that website. After you close the website, the content scripts on that\nwebsite will stop running as well.\n\nFor our extension, we want to replace every image on every website with Pikachu\nimages. In order to do that we need to specify a content script that works every\ntime you open a web page.\n\nLet’s create our content script in assets/js/contentScript.js and include the\nfollowing script to test whether our extension is working or not:\n\nOne other thing we need to do is tell Chrome which script do we need to run as a\ncontent script. Add the following lines to manifest.json\n\nHere we are using content_scripts as key with the value as an array. Here’s what\neverything in the array means:\n\n 1. matches: here you can specify which URLs the content script should run on.\n    In our case we want our content script to run on all pages. So, we use the\n    placeholder “<all_urls>” to specify that.\n 2. all_frames: this key is used to allow the extension to specify if JavaScript\n    and CSS files should be injected into all frames matching the specified URL\n    requirements or only into the topmost frame in a tab.\n 3. js: here we specify the script we want to run as a content script. You can\n    specify more than one script, so the value has to be an array\n\nNow we’re ready to test our content script.\n\nBut before we do that we need to do one small step. In order to inform chrome\nthat we updated our chrome extension, we need to go back to chrome://extensions,\ngo to our extension, and press the reload icon.\n\nTo test if our content script is working, go to any web page you want, reload\nthe page if it was opened before, then right-click and press Inspect. In\nDevTools, open the Console tab. You will see in your console as expected:\n\nConsole of a web pageSo, we can verify that our content script is working. Try\nreloading the page, and the content script will execute again. Try opening\nanother web page, and the content script will execute on that page\nindependently.\n\nNow, we know how to inject a script on any web page, and through that script we\ncan do our work.\n\nIn our case, we need to replace every image in any website with random Pikachu\nimages. To do that, we will use this API\n[https://some-random-api.ml/img/pikachu] which will provide us with a JSON\nobject containing the link of a Pikachu image. Each request to that API will\nlook something like this:\n\nResult of API callWith the help of this API, the steps to achieve what we need\nwill be as follows:\n\n 1. Get all image elements on the web page.\n 2. Perform a GET request to the API to retrieve a random Pikachu image link.\n 3. Replace the image elements’ src attributes with the links we retrieve.\n\nThis all sounds good and easy, but there is one complication: content scripts in\nChrome extensions cannot perform Cross-Origin requests.\n\nSo how can we fetch the image URLs from the API? We will need to use a\nBackground script.\n\nAs we specified earlier, Background scripts run in the background in Chrome.\nThey are not dependent on any web page.\n\nScripts in Chrome extensions are isolated from each other. However, they share a\nmean of communication through the Chrome API\n[https://developer.chrome.com/extensions/api_index].\n\nWhat we will do is that we will listen for a message in the background script\nsent from content scripts. Every time a web page opens, our content script will\nrun, send a message to the background script requesting an image URL. The\nbackground script will then perform the asynchronous call to the Pikachu API,\nretrieve the link and send it back to the content script.\n\nThis may sound complicated at first, but it’s actually done in a very simple\nmanner.\n\nLet us first start by creating the background script and see it in action.\nCreate assets/js/background.js with the following content:\n\nNow, we need to tell Chrome that the file we just created will be our background\nscript. In order to do that, we need to go back to our manifest.json and add the\nfollowing:\n\nHere is what everything means:\n\n 1. scripts: An array that holds the paths of the scripts we will use as\n    background scripts. In our case, it’s just one.\n 2. persistent: should always be false unless the extension uses Chrome’s Web\n    Request API to block or modify network requests.\n\nNow, Chrome will know about our background script. It will load it when it’s\nneeded, and unload it when it becomes idle.\n\nTo test our background script, we need to reload our extension as we did\nearlier.\n\nOnce you do that, you will see a new link appear in your extension’s info box\nwith the text “inspect views background page”:\n\nIf you click on it, a new DevTool will open and in the console you will see:\n\nWe can now confirm that our background script is working. Even if you close any\nother web page or reload any web page, nothing will change in the background\nscript.\n\nLet’s go back to our objective again. We now need to listen to messages in our\nbackground script, and on receiving a new message fetch the URL from the Pikachu\nAPI.\n\nIn order to listen to messages, we will use the following method in Chrome’s\nAPI:\n\nWe need to pass a function to addListener. This function will be executed on\nreceiving any new message. The function takes three parameters:\n\n 1. message: the message sent of type any.\n 2. sender: of type MessageSender\n    [https://developer.chrome.com/extensions/runtime#type-MessageSender]\n 3. senderResponse: Callback function to call (at most once) when you have a\n    response. This function is sent by the sender on sending the message.\n\nSo this is what our background.js file should look like now:\n\nNow our background script is ready to receive messages, but there are no\nmessages being sent.\n\nAs we said earlier, each time a page opens, our content script will loop through\nimage elements in the web page, and send a message to the background script to\nfetch the Pikachu image link.\n\nIn order to send messages, we will need to use the following method from\nChrome’s API:\n\nThe parameter’s being sent are as follows:\n\n 1. extensionId: (Optional) the ID of the extension we are sending the message\n    to. If it’s omitted, the message will be sent to our own extension.\n 2. message: The message we are sending of type any.\n 3. options: Optional\n 4. responseCallback: The function to call after receiving the message. This is\n    the senderResponse parameter in the previous method we saw.\n\nSo, we will use this method in our content script to send a message to the\nbackground script to fetch the Pikachu URL from the API. Our content script\nshould now look like this:\n\nHere’s what we’re doing in the above code:\n\n 1. Line 1: get all image elements on the web page.\n 2. Line 2–6: Loop through the image elements. On each element, we send a\n    message to the background script. As you can tell, we are omitting the\n    optional extensionId and options parameters. The first parameter is the \n    message we are sending, and the second parameter is the callback function we\n    want the background to execute after it fetches the image’s link.\n\nWe are sending the object {msg: ‘image’, index: i} to our extension. This object\nhas two properties. The first one is to specify what our message is about. In\nour extension, it might not be very helpful, but when you have an extension that\nuses different types of messages, it’s a good idea to differentiate what each\nmessage is for. The second property is to specify the index of the image the\nlink is for in the images array. The reason behind this is that the callback\nfunction replacing the image will be called asynchronously, so i might point at\nanother index at the time it’s being executed.\n\nThe callback function receives an object that has two properties. data will be\nthe data received from the API call, and index will be the index of the image in\nthe array images. Once it’s called, it will set the image at index in images to\nthe link property is data.\n\nNow, we need to fetch the data from the API in our background script whenever\nthe content script sends a message. We can do this as follows:\n\nHere’s what’s going on here:\n\n 1. Line 1: Listen to messages in our extension\n 2. Line 2: Check if the message is to get the image URL. This will use the msg \n    attribute we passed when we sent the message in contentScript\n 3. Line 3–8: If the condition in Line 2 is true, fetch the data from the API.\n    In the first Promise we get the response text, then in the second Promise we\n    parse the JSON object and call senderResponse which is the callback function\n    specified by the sender of the message. We pass with it an object with the\n    parameters data (the data received through the API call) and index (the\n    index of the image, sent by the sender).\n 4. Line 9: simple handling of any error that might occur.\n 5. Line 10: If the sender specifies a callback function, it has to be called\n    before the listener stops executing or else it will throw an error. In our\n    case, we have to wait until we receive a response from the API which will\n    happen asynchronously, so we return true to specify that it will be called\n    asynchronously.\n\nOkay, our scripts are ready. Only one thing left — we need to give permission to\nthe API in our manifest.json:\n\nAll you need to do now is reload the extension in chrome://extensions, then open\nany page and see Pikachu everywhere!\n\nOf course, this extension will not replace background images. This can be done\nby looping through elements, checking if they have background images, and\nreplacing them the same way.","html":"<p><em>This article was also published on <a href=\"https://levelup.gitconnected.com/chrome-extension-tutorial-replace-images-in-any-website-with-pikachu-de2a6e3548bb\">Level Up Coding's gitconnected</a></em></p><p>What’s a better way to learn how to make a Chrome extension than making the web cuter with Pikachu images?</p><p>In this tutorial, I will show you how you can create a simple Chrome extension that replaces images on websites with Pikachu images. We will discuss topics like content scripts, background scripts, and messages between scripts in Chrome extensions.</p><p>The code for this tutorial is available <a href=\"https://github.com/shahednasser/pikachu-everywhere\" rel=\"noopener\">here</a>.</p><p>First thing you need to do when creating any chrome extension is start with creating a <strong><strong>manifest.json </strong></strong>file in the root of the extension’s directory. For now, this file will hold only the following basic information about your extension:</p><!--kg-card-begin: html--><script src=\"https://gist.github.com/shahednasser/6beec3aa60a2187e99e85479f8806c30.js\"></script><!--kg-card-end: html--><p>Here is what everything in this file means:</p><ol><li><strong><strong>name:</strong></strong> The name of the extension. I named the extension “Pikachu Everywhere”</li><li><strong><strong>version</strong></strong>: This is the version of the extension. When you publish the extension in the Chrome Web Store and you need to update it, you will have to increase the version number so it’s good to start with a low number.</li><li><strong><strong>description: (Optional) </strong></strong>Description of your extension</li><li><strong><strong>manifest_version</strong></strong>: As of Chrome 18, developers are required to use manifest version 2, since manifest version 1 is now deprecated.</li><li><strong><strong>icons: (Optional) </strong></strong>Here we specify the extension’s icon. This icon will show up in your browser’s toolbar, the Chrome Web Store, and any other place that requires to show it. For that reason, it’s good to include different sizes of the icon. I have used an icon from <a href=\"https://www.freeiconspng.com/img/17360\" rel=\"noopener\">Free Icons PNG</a> and resized the icons using <a href=\"https://resizeimage.net/\" rel=\"noopener\">https://resizeimage.net/</a> (You can find the images in the <a href=\"https://github.com/shahednasser/pikachu-everywhere\" rel=\"noopener\">GitHub repository</a>). The paths specified for these images is relative to the root of the extension.</li></ol><p>With this, we have the core of any Chrome extension. Next, we’ll add the extension to the browser.</p><p>Go to chrome://extensions. There, you can toggle developer mode at the top right corner. Once you do that, a new toolbar will show up with three buttons: “Load unpacked,” “Pack extension,” and “Update.” Click on “Load unpacked” and choose your extension’s directory.</p><p>Once you do that, you will see that your new chrome extension is now working. Yes, it does nothing, but it’s working.</p><p>In Chrome extensions, there are two types of scripts you can use. There are Background scripts and Content scripts. Background scripts run (obviously) in the background. They are loaded when needed and unloaded when idle. They are used to listen to certain events in your Chrome extension.</p><p>Content scripts are scripts loaded once you open a website and injected it into it. Once you open a website, they will start running just like any other script on that website. After you close the website, the content scripts on that website will stop running as well.</p><p>For our extension, we want to replace every image on every website with Pikachu images. In order to do that we need to specify a content script that works every time you open a web page.</p><p>Let’s create our content script in <code>assets/js/contentScript.js</code> and include the following script to test whether our extension is working or not:</p><!--kg-card-begin: html--><script src=\"https://gist.github.com/shahednasser/342ba17b0b6d715adaafd334a14416f9.js\"></script><!--kg-card-end: html--><p>One other thing we need to do is tell Chrome which script do we need to run as a content script. Add the following lines to <strong><strong>manifest.json</strong></strong></p><figure class=\"kg-card kg-embed-card\"><iframe src=\"https://levelup.gitconnected.com/media/d35d7df4566f532c2a0948ea540ee302\" allowfullscreen=\"\" frameborder=\"0\" height=\"0\" width=\"0\" title=\"adding content script to manifest.json\" class=\"t u v hm aj\" scrolling=\"auto\" style=\"box-sizing: inherit; position: absolute; top: 0px; left: 0px; width: 680px; height: 0px;\"></iframe></figure><p>Here we are using <code>content_scripts</code> as key with the value as an array. Here’s what everything in the array means:</p><ol><li><strong><strong>matches</strong></strong>: here you can specify which URLs the content script should run on. In our case we want our content script to run on all pages. So, we use the placeholder “&lt;all_urls&gt;” to specify that.</li><li><strong><strong>all_frames</strong></strong>: this key is used to allow the extension to specify if JavaScript and CSS files should be injected into all frames matching the specified URL requirements or only into the topmost frame in a tab.</li><li><strong><strong>js: </strong></strong>here we specify the script we want to run as a content script. You can specify more than one script, so the value has to be an array</li></ol><p>Now we’re ready to test our content script.</p><p>But before we do that we need to do one small step. In order to inform chrome that we updated our chrome extension, we need to go back to chrome://extensions, go to our extension, and press the reload icon.</p><p>To test if our content script is working, go to any web page you want, reload the page if it was opened before, then right-click and press Inspect. In DevTools, open the Console tab. You will see in your console as expected:</p><figure class=\"kg-card kg-image-card kg-card-hascaption\"><img src=\"https://res-3.cloudinary.com/hbqmf3mbz/image/upload/q_auto/v1/ghost-blog-images/chrome-extension-screenshot-1.png\" class=\"kg-image\" alt loading=\"lazy\"><figcaption>Console of a web page</figcaption></figure><p>So, we can verify that our content script is working. Try reloading the page, and the content script will execute again. Try opening another web page, and the content script will execute on that page independently.</p><p>Now, we know how to inject a script on any web page, and through that script we can do our work.</p><p>In our case, we need to replace every image in any website with random Pikachu images. To do that, we will use <a href=\"https://some-random-api.ml/img/pikachu\" rel=\"noopener\">this API</a> which will provide us with a JSON object containing the link of a Pikachu image. Each request to that API will look something like this:</p><figure class=\"kg-card kg-image-card kg-card-hascaption\"><img src=\"https://res-3.cloudinary.com/hbqmf3mbz/image/upload/q_auto/v1/ghost-blog-images/chrome-extension-screenshot-2.png\" class=\"kg-image\" alt loading=\"lazy\"><figcaption>Result of API call</figcaption></figure><p>With the help of this API, the steps to achieve what we need will be as follows:</p><ol><li>Get all image elements on the web page.</li><li>Perform a GET request to the API to retrieve a random Pikachu image link.</li><li>Replace the image elements’ <code>src</code> attributes with the links we retrieve.</li></ol><p>This all sounds good and easy, but there is one complication: content scripts in Chrome extensions cannot perform Cross-Origin requests.</p><p>So how can we fetch the image URLs from the API? We will need to use a Background script.</p><p>As we specified earlier, Background scripts run in the background in Chrome. They are not dependent on any web page.</p><p>Scripts in Chrome extensions are isolated from each other. However, they share a mean of communication through the <a href=\"https://developer.chrome.com/extensions/api_index\" rel=\"noopener\">Chrome API</a>.</p><p>What we will do is that we will listen for a message in the <strong><strong>background</strong></strong> script sent from <strong><strong>content</strong></strong> scripts. Every time a web page opens, our content script will run, send a message to the background script requesting an image URL. The background script will then perform the asynchronous call to the Pikachu API, retrieve the link and send it back to the content script.</p><p>This may sound complicated at first, but it’s actually done in a very simple manner.</p><p>Let us first start by creating the background script and see it in action. Create <code>assets/js/background.js</code> with the following content:</p><!--kg-card-begin: html--><script src=\"https://gist.github.com/shahednasser/b45de97a11eda346d1ea427b88c406f4.js\"></script><!--kg-card-end: html--><p>Now, we need to tell Chrome that the file we just created will be our background script. In order to do that, we need to go back to our <strong><strong>manifest.json</strong></strong> and add the following:</p><!--kg-card-begin: html--><script src=\"https://gist.github.com/shahednasser/c7a26cf5b3b2415110cb97e44f2b3834.js\"></script><!--kg-card-end: html--><p>Here is what everything means:</p><ol><li><strong><strong>scripts: </strong></strong>An array that holds the paths of the scripts we will use as background scripts. In our case, it’s just one.</li><li><strong><strong>persistent</strong></strong>: should always be false unless the extension uses Chrome’s Web Request API to block or modify network requests.</li></ol><p>Now, Chrome will know about our background script. It will load it when it’s needed, and unload it when it becomes idle.</p><p>To test our background script, we need to reload our extension as we did earlier.</p><p>Once you do that, you will see a new link appear in your extension’s info box with the text “inspect views background page”:</p><figure class=\"kg-card kg-image-card\"><img src=\"https://res-5.cloudinary.com/hbqmf3mbz/image/upload/q_auto/v1/ghost-blog-images/chrome-extension-screenshot-3.png\" class=\"kg-image\" alt loading=\"lazy\"></figure><p>If you click on it, a new DevTool will open and in the console you will see:</p><figure class=\"kg-card kg-image-card\"><img src=\"https://res-4.cloudinary.com/hbqmf3mbz/image/upload/q_auto/v1/ghost-blog-images/chrome-extension-screenshot-4.png\" class=\"kg-image\" alt loading=\"lazy\"></figure><p>We can now confirm that our background script is working. Even if you close any other web page or reload any web page, nothing will change in the background script.</p><p>Let’s go back to our objective again. We now need to listen to messages in our background script, and on receiving a new message fetch the URL from the Pikachu API.</p><p>In order to listen to messages, we will use the following method in Chrome’s API:</p><!--kg-card-begin: html--><script src=\"https://gist.github.com/shahednasser/4426ca477880552e4c563be8264b968c.js\"></script><!--kg-card-end: html--><p>We need to pass a function to <code>addListener</code>. This function will be executed on receiving any new message. The function takes three parameters:</p><ol><li><strong><strong>message: </strong></strong>the message sent of type any.</li><li><strong><strong>sender: </strong></strong>of type <a href=\"https://developer.chrome.com/extensions/runtime#type-MessageSender\" rel=\"noopener\">MessageSender</a></li><li><strong><strong>senderResponse: </strong></strong>Callback function to call (at most once) when you have a response. This function is sent by the sender on sending the message.</li></ol><p>So this is what our <strong><strong>background.js </strong></strong>file should look like now:</p><!--kg-card-begin: html--><script src=\"https://gist.github.com/shahednasser/4920b314274d2695b5ac0e771a4cd52c.js\"></script><!--kg-card-end: html--><p>Now our background script is ready to receive messages, but there are no messages being sent.</p><p>As we said earlier, each time a page opens, our content script will loop through image elements in the web page, and send a message to the background script to fetch the Pikachu image link.</p><p>In order to send messages, we will need to use the following method from Chrome’s API:</p><!--kg-card-begin: html--><script src=\"https://gist.github.com/shahednasser/03e371015982ef271518922e56139c19.js\"></script><!--kg-card-end: html--><p>The parameter’s being sent are as follows:</p><ol><li><strong><strong>extensionId: (Optional) </strong></strong>the ID of the extension we are sending the message to. If it’s omitted, the message will be sent to our own extension.</li><li><strong><strong>message:</strong></strong> The message we are sending of type any.</li><li><strong><strong>options: Optional</strong></strong></li><li><strong><strong>responseCallback: </strong></strong>The function to call after receiving the message. This is the <strong><strong>senderResponse</strong></strong> parameter in the previous method we saw.</li></ol><p>So, we will use this method in our content script to send a message to the background script to fetch the Pikachu URL from the API. Our content script should now look like this:</p><!--kg-card-begin: html--><script src=\"https://gist.github.com/shahednasser/bd4051325006a5f3ca01e882b51eebc5.js\"></script><!--kg-card-end: html--><p>Here’s what we’re doing in the above code:</p><ol><li><strong><strong>Line 1: </strong></strong>get all image elements on the web page.</li><li><strong><strong>Line 2–6: </strong></strong>Loop through the image elements. On each element, we send a message to the background script. As you can tell, we are omitting the optional <strong><strong>extensionId </strong></strong>and <strong><strong>options</strong></strong> parameters. The first parameter is the <strong><strong>message </strong></strong>we are sending, and the second parameter is the callback function we want the background to execute after it fetches the image’s link.</li></ol><p>We are sending the object <strong><strong>{msg: ‘image’, index: i}</strong></strong> to our extension. This object has two properties. The first one is to specify what our message is about. In our extension, it might not be very helpful, but when you have an extension that uses different types of messages, it’s a good idea to differentiate what each message is for. The second property is to specify the index of the image the link is for in the <strong><strong>images</strong></strong> array. The reason behind this is that the callback function replacing the image will be called asynchronously, so <strong><strong>i </strong></strong>might point at another index at the time it’s being executed.</p><p>The callback function receives an object that has two properties. <strong><strong>data</strong></strong> will be the data received from the API call, and <strong><strong>index</strong></strong> will be the index of the image in the array <strong><strong>images</strong></strong>. Once it’s called, it will set the image at <strong><strong>index</strong></strong> in <strong><strong>images</strong></strong> to the link property is <strong><strong>data</strong></strong>.</p><p>Now, we need to fetch the data from the API in our background script whenever the content script sends a message. We can do this as follows:</p><!--kg-card-begin: html--><script src=\"https://gist.github.com/shahednasser/9c024a8b40396e7c6a6ae1250fb5d94d.js\"></script><!--kg-card-end: html--><p>Here’s what’s going on here:</p><ol><li><strong><strong>Line 1:</strong></strong> Listen to messages in our extension</li><li><strong><strong>Line 2: </strong></strong>Check if the message is to get the image URL. This will use the <strong><strong>msg </strong></strong>attribute we passed when we sent the message in <strong><strong>contentScript</strong></strong></li><li><strong><strong>Line 3–8:</strong></strong> If the condition in <strong><strong>Line 2</strong></strong> is true, fetch the data from the API. In the first <code>Promise</code> we get the response text, then in the second <code>Promise</code> we parse the JSON object and call <strong><strong>senderResponse</strong></strong> which is the callback function specified by the sender of the message. We pass with it an object with the parameters <strong><strong>data </strong></strong>(the data received through the API call) and <strong><strong>index</strong></strong> (the index of the image, sent by the sender).</li><li><strong><strong>Line 9: </strong></strong>simple handling of any error that might occur.</li><li><strong><strong>Line 10</strong></strong>: If the sender specifies a callback function, it has to be called before the listener stops executing or else it will throw an error. In our case, we have to wait until we receive a response from the API which will happen asynchronously, so we return true to specify that it will be called asynchronously.</li></ol><p>Okay, our scripts are ready. Only one thing left — we need to give permission to the API in our <strong><strong>manifest.json:</strong></strong></p><!--kg-card-begin: html--><script src=\"https://gist.github.com/shahednasser/8d6786f1583a91fe31635715caf44b0c.js\"></script><!--kg-card-end: html--><p>All you need to do now is reload the extension in chrome://extensions, then open any page and see Pikachu everywhere!</p><p>Of course, this extension will not replace background images. This can be done by looping through elements, checking if they have background images, and replacing them the same way.</p>","url":"https://backend.shahednasser.com/chrome-extension-tutorial-replace-images-in-any-website-with-pikachu/","canonical_url":null,"uuid":"d3c062c3-8b05-4b63-919c-4fbbcdb59cc3","codeinjection_foot":null,"codeinjection_head":null,"codeinjection_styles":null,"comment_id":"5fa596aabc1317001e675bff","reading_time":9,"send_email_when_published":null,"email_subject":null,"childHtmlRehype":{"html":"<p><em>This article was also published on <a href=\"https://levelup.gitconnected.com/chrome-extension-tutorial-replace-images-in-any-website-with-pikachu-de2a6e3548bb\">Level Up Coding's gitconnected</a></em></p><p>What’s a better way to learn how to make a Chrome extension than making the web cuter with Pikachu images?</p><p>In this tutorial, I will show you how you can create a simple Chrome extension that replaces images on websites with Pikachu images. We will discuss topics like content scripts, background scripts, and messages between scripts in Chrome extensions.</p><p>The code for this tutorial is available <a href=\"https://github.com/shahednasser/pikachu-everywhere\" rel=\"noopener\">here</a>.</p><p>First thing you need to do when creating any chrome extension is start with creating a <strong><strong>manifest.json </strong></strong>file in the root of the extension’s directory. For now, this file will hold only the following basic information about your extension:</p><!--kg-card-begin: html--><script src=\"https://gist.github.com/shahednasser/6beec3aa60a2187e99e85479f8806c30.js\"></script><!--kg-card-end: html--><p>Here is what everything in this file means:</p><ol><li><strong><strong>name:</strong></strong> The name of the extension. I named the extension “Pikachu Everywhere”</li><li><strong><strong>version</strong></strong>: This is the version of the extension. When you publish the extension in the Chrome Web Store and you need to update it, you will have to increase the version number so it’s good to start with a low number.</li><li><strong><strong>description: (Optional) </strong></strong>Description of your extension</li><li><strong><strong>manifest_version</strong></strong>: As of Chrome 18, developers are required to use manifest version 2, since manifest version 1 is now deprecated.</li><li><strong><strong>icons: (Optional) </strong></strong>Here we specify the extension’s icon. This icon will show up in your browser’s toolbar, the Chrome Web Store, and any other place that requires to show it. For that reason, it’s good to include different sizes of the icon. I have used an icon from <a href=\"https://www.freeiconspng.com/img/17360\" rel=\"noopener\">Free Icons PNG</a> and resized the icons using <a href=\"https://resizeimage.net/\" rel=\"noopener\">https://resizeimage.net/</a> (You can find the images in the <a href=\"https://github.com/shahednasser/pikachu-everywhere\" rel=\"noopener\">GitHub repository</a>). The paths specified for these images is relative to the root of the extension.</li></ol><p>With this, we have the core of any Chrome extension. Next, we’ll add the extension to the browser.</p><p>Go to chrome://extensions. There, you can toggle developer mode at the top right corner. Once you do that, a new toolbar will show up with three buttons: “Load unpacked,” “Pack extension,” and “Update.” Click on “Load unpacked” and choose your extension’s directory.</p><p>Once you do that, you will see that your new chrome extension is now working. Yes, it does nothing, but it’s working.</p><p>In Chrome extensions, there are two types of scripts you can use. There are Background scripts and Content scripts. Background scripts run (obviously) in the background. They are loaded when needed and unloaded when idle. They are used to listen to certain events in your Chrome extension.</p><p>Content scripts are scripts loaded once you open a website and injected it into it. Once you open a website, they will start running just like any other script on that website. After you close the website, the content scripts on that website will stop running as well.</p><p>For our extension, we want to replace every image on every website with Pikachu images. In order to do that we need to specify a content script that works every time you open a web page.</p><p>Let’s create our content script in <code class=\"language-text\">assets/js/contentScript.js</code> and include the following script to test whether our extension is working or not:</p><!--kg-card-begin: html--><script src=\"https://gist.github.com/shahednasser/342ba17b0b6d715adaafd334a14416f9.js\"></script><!--kg-card-end: html--><p>One other thing we need to do is tell Chrome which script do we need to run as a content script. Add the following lines to <strong><strong>manifest.json</strong></strong></p><figure class=\"kg-card kg-embed-card\"><iframe src=\"https://levelup.gitconnected.com/media/d35d7df4566f532c2a0948ea540ee302\" allowfullscreen frameborder=\"0\" height=\"0\" width=\"0\" title=\"adding content script to manifest.json\" class=\"t u v hm aj\" scrolling=\"auto\" style=\"box-sizing: inherit; position: absolute; top: 0px; left: 0px; width: 680px; height: 0px;\"></iframe></figure><p>Here we are using <code class=\"language-text\">content_scripts</code> as key with the value as an array. Here’s what everything in the array means:</p><ol><li><strong><strong>matches</strong></strong>: here you can specify which URLs the content script should run on. In our case we want our content script to run on all pages. So, we use the placeholder “&#x3C;all_urls>” to specify that.</li><li><strong><strong>all_frames</strong></strong>: this key is used to allow the extension to specify if JavaScript and CSS files should be injected into all frames matching the specified URL requirements or only into the topmost frame in a tab.</li><li><strong><strong>js: </strong></strong>here we specify the script we want to run as a content script. You can specify more than one script, so the value has to be an array</li></ol><p>Now we’re ready to test our content script.</p><p>But before we do that we need to do one small step. In order to inform chrome that we updated our chrome extension, we need to go back to chrome://extensions, go to our extension, and press the reload icon.</p><p>To test if our content script is working, go to any web page you want, reload the page if it was opened before, then right-click and press Inspect. In DevTools, open the Console tab. You will see in your console as expected:</p><figure class=\"kg-card kg-image-card kg-card-hascaption\"><img src=\"https://res-3.cloudinary.com/hbqmf3mbz/image/upload/q_auto/v1/ghost-blog-images/chrome-extension-screenshot-1.png\" class=\"kg-image\" alt=\"\" loading=\"lazy\"><figcaption>Console of a web page</figcaption></figure><p>So, we can verify that our content script is working. Try reloading the page, and the content script will execute again. Try opening another web page, and the content script will execute on that page independently.</p><p>Now, we know how to inject a script on any web page, and through that script we can do our work.</p><p>In our case, we need to replace every image in any website with random Pikachu images. To do that, we will use <a href=\"https://some-random-api.ml/img/pikachu\" rel=\"noopener\">this API</a> which will provide us with a JSON object containing the link of a Pikachu image. Each request to that API will look something like this:</p><figure class=\"kg-card kg-image-card kg-card-hascaption\"><img src=\"https://res-3.cloudinary.com/hbqmf3mbz/image/upload/q_auto/v1/ghost-blog-images/chrome-extension-screenshot-2.png\" class=\"kg-image\" alt=\"\" loading=\"lazy\"><figcaption>Result of API call</figcaption></figure><p>With the help of this API, the steps to achieve what we need will be as follows:</p><ol><li>Get all image elements on the web page.</li><li>Perform a GET request to the API to retrieve a random Pikachu image link.</li><li>Replace the image elements’ <code class=\"language-text\">src</code> attributes with the links we retrieve.</li></ol><p>This all sounds good and easy, but there is one complication: content scripts in Chrome extensions cannot perform Cross-Origin requests.</p><p>So how can we fetch the image URLs from the API? We will need to use a Background script.</p><p>As we specified earlier, Background scripts run in the background in Chrome. They are not dependent on any web page.</p><p>Scripts in Chrome extensions are isolated from each other. However, they share a mean of communication through the <a href=\"https://developer.chrome.com/extensions/api_index\" rel=\"noopener\">Chrome API</a>.</p><p>What we will do is that we will listen for a message in the <strong><strong>background</strong></strong> script sent from <strong><strong>content</strong></strong> scripts. Every time a web page opens, our content script will run, send a message to the background script requesting an image URL. The background script will then perform the asynchronous call to the Pikachu API, retrieve the link and send it back to the content script.</p><p>This may sound complicated at first, but it’s actually done in a very simple manner.</p><p>Let us first start by creating the background script and see it in action. Create <code class=\"language-text\">assets/js/background.js</code> with the following content:</p><!--kg-card-begin: html--><script src=\"https://gist.github.com/shahednasser/b45de97a11eda346d1ea427b88c406f4.js\"></script><!--kg-card-end: html--><p>Now, we need to tell Chrome that the file we just created will be our background script. In order to do that, we need to go back to our <strong><strong>manifest.json</strong></strong> and add the following:</p><!--kg-card-begin: html--><script src=\"https://gist.github.com/shahednasser/c7a26cf5b3b2415110cb97e44f2b3834.js\"></script><!--kg-card-end: html--><p>Here is what everything means:</p><ol><li><strong><strong>scripts: </strong></strong>An array that holds the paths of the scripts we will use as background scripts. In our case, it’s just one.</li><li><strong><strong>persistent</strong></strong>: should always be false unless the extension uses Chrome’s Web Request API to block or modify network requests.</li></ol><p>Now, Chrome will know about our background script. It will load it when it’s needed, and unload it when it becomes idle.</p><p>To test our background script, we need to reload our extension as we did earlier.</p><p>Once you do that, you will see a new link appear in your extension’s info box with the text “inspect views background page”:</p><figure class=\"kg-card kg-image-card\"><img src=\"https://res-5.cloudinary.com/hbqmf3mbz/image/upload/q_auto/v1/ghost-blog-images/chrome-extension-screenshot-3.png\" class=\"kg-image\" alt=\"\" loading=\"lazy\"></figure><p>If you click on it, a new DevTool will open and in the console you will see:</p><figure class=\"kg-card kg-image-card\"><img src=\"https://res-4.cloudinary.com/hbqmf3mbz/image/upload/q_auto/v1/ghost-blog-images/chrome-extension-screenshot-4.png\" class=\"kg-image\" alt=\"\" loading=\"lazy\"></figure><p>We can now confirm that our background script is working. Even if you close any other web page or reload any web page, nothing will change in the background script.</p><p>Let’s go back to our objective again. We now need to listen to messages in our background script, and on receiving a new message fetch the URL from the Pikachu API.</p><p>In order to listen to messages, we will use the following method in Chrome’s API:</p><!--kg-card-begin: html--><script src=\"https://gist.github.com/shahednasser/4426ca477880552e4c563be8264b968c.js\"></script><!--kg-card-end: html--><p>We need to pass a function to <code class=\"language-text\">addListener</code>. This function will be executed on receiving any new message. The function takes three parameters:</p><ol><li><strong><strong>message: </strong></strong>the message sent of type any.</li><li><strong><strong>sender: </strong></strong>of type <a href=\"https://developer.chrome.com/extensions/runtime#type-MessageSender\" rel=\"noopener\">MessageSender</a></li><li><strong><strong>senderResponse: </strong></strong>Callback function to call (at most once) when you have a response. This function is sent by the sender on sending the message.</li></ol><p>So this is what our <strong><strong>background.js </strong></strong>file should look like now:</p><!--kg-card-begin: html--><script src=\"https://gist.github.com/shahednasser/4920b314274d2695b5ac0e771a4cd52c.js\"></script><!--kg-card-end: html--><p>Now our background script is ready to receive messages, but there are no messages being sent.</p><p>As we said earlier, each time a page opens, our content script will loop through image elements in the web page, and send a message to the background script to fetch the Pikachu image link.</p><p>In order to send messages, we will need to use the following method from Chrome’s API:</p><!--kg-card-begin: html--><script src=\"https://gist.github.com/shahednasser/03e371015982ef271518922e56139c19.js\"></script><!--kg-card-end: html--><p>The parameter’s being sent are as follows:</p><ol><li><strong><strong>extensionId: (Optional) </strong></strong>the ID of the extension we are sending the message to. If it’s omitted, the message will be sent to our own extension.</li><li><strong><strong>message:</strong></strong> The message we are sending of type any.</li><li><strong><strong>options: Optional</strong></strong></li><li><strong><strong>responseCallback: </strong></strong>The function to call after receiving the message. This is the <strong><strong>senderResponse</strong></strong> parameter in the previous method we saw.</li></ol><p>So, we will use this method in our content script to send a message to the background script to fetch the Pikachu URL from the API. Our content script should now look like this:</p><!--kg-card-begin: html--><script src=\"https://gist.github.com/shahednasser/bd4051325006a5f3ca01e882b51eebc5.js\"></script><!--kg-card-end: html--><p>Here’s what we’re doing in the above code:</p><ol><li><strong><strong>Line 1: </strong></strong>get all image elements on the web page.</li><li><strong><strong>Line 2–6: </strong></strong>Loop through the image elements. On each element, we send a message to the background script. As you can tell, we are omitting the optional <strong><strong>extensionId </strong></strong>and <strong><strong>options</strong></strong> parameters. The first parameter is the <strong><strong>message </strong></strong>we are sending, and the second parameter is the callback function we want the background to execute after it fetches the image’s link.</li></ol><p>We are sending the object <strong><strong>{msg: ‘image’, index: i}</strong></strong> to our extension. This object has two properties. The first one is to specify what our message is about. In our extension, it might not be very helpful, but when you have an extension that uses different types of messages, it’s a good idea to differentiate what each message is for. The second property is to specify the index of the image the link is for in the <strong><strong>images</strong></strong> array. The reason behind this is that the callback function replacing the image will be called asynchronously, so <strong><strong>i </strong></strong>might point at another index at the time it’s being executed.</p><p>The callback function receives an object that has two properties. <strong><strong>data</strong></strong> will be the data received from the API call, and <strong><strong>index</strong></strong> will be the index of the image in the array <strong><strong>images</strong></strong>. Once it’s called, it will set the image at <strong><strong>index</strong></strong> in <strong><strong>images</strong></strong> to the link property is <strong><strong>data</strong></strong>.</p><p>Now, we need to fetch the data from the API in our background script whenever the content script sends a message. We can do this as follows:</p><!--kg-card-begin: html--><script src=\"https://gist.github.com/shahednasser/9c024a8b40396e7c6a6ae1250fb5d94d.js\"></script><!--kg-card-end: html--><p>Here’s what’s going on here:</p><ol><li><strong><strong>Line 1:</strong></strong> Listen to messages in our extension</li><li><strong><strong>Line 2: </strong></strong>Check if the message is to get the image URL. This will use the <strong><strong>msg </strong></strong>attribute we passed when we sent the message in <strong><strong>contentScript</strong></strong></li><li><strong><strong>Line 3–8:</strong></strong> If the condition in <strong><strong>Line 2</strong></strong> is true, fetch the data from the API. In the first <code class=\"language-text\">Promise</code> we get the response text, then in the second <code class=\"language-text\">Promise</code> we parse the JSON object and call <strong><strong>senderResponse</strong></strong> which is the callback function specified by the sender of the message. We pass with it an object with the parameters <strong><strong>data </strong></strong>(the data received through the API call) and <strong><strong>index</strong></strong> (the index of the image, sent by the sender).</li><li><strong><strong>Line 9: </strong></strong>simple handling of any error that might occur.</li><li><strong><strong>Line 10</strong></strong>: If the sender specifies a callback function, it has to be called before the listener stops executing or else it will throw an error. In our case, we have to wait until we receive a response from the API which will happen asynchronously, so we return true to specify that it will be called asynchronously.</li></ol><p>Okay, our scripts are ready. Only one thing left — we need to give permission to the API in our <strong><strong>manifest.json:</strong></strong></p><!--kg-card-begin: html--><script src=\"https://gist.github.com/shahednasser/8d6786f1583a91fe31635715caf44b0c.js\"></script><!--kg-card-end: html--><p>All you need to do now is reload the extension in chrome://extensions, then open any page and see Pikachu everywhere!</p><p>Of course, this extension will not replace background images. This can be done by looping through elements, checking if they have background images, and replacing them the same way.</p>","htmlAst":{"type":"root","children":[{"type":"element","tagName":"p","properties":{},"children":[{"type":"element","tagName":"em","properties":{},"children":[{"type":"text","value":"This article was also published on "},{"type":"element","tagName":"a","properties":{"href":"https://levelup.gitconnected.com/chrome-extension-tutorial-replace-images-in-any-website-with-pikachu-de2a6e3548bb"},"children":[{"type":"text","value":"Level Up Coding's gitconnected"}]}]}]},{"type":"element","tagName":"p","properties":{},"children":[{"type":"text","value":"What’s a better way to learn how to make a Chrome extension than making the web cuter with Pikachu images?"}]},{"type":"element","tagName":"p","properties":{},"children":[{"type":"text","value":"In this tutorial, I will show you how you can create a simple Chrome extension that replaces images on websites with Pikachu images. We will discuss topics like content scripts, background scripts, and messages between scripts in Chrome extensions."}]},{"type":"element","tagName":"p","properties":{},"children":[{"type":"text","value":"The code for this tutorial is available "},{"type":"element","tagName":"a","properties":{"href":"https://github.com/shahednasser/pikachu-everywhere","rel":["noopener"]},"children":[{"type":"text","value":"here"}]},{"type":"text","value":"."}]},{"type":"element","tagName":"p","properties":{},"children":[{"type":"text","value":"First thing you need to do when creating any chrome extension is start with creating a "},{"type":"element","tagName":"strong","properties":{},"children":[{"type":"element","tagName":"strong","properties":{},"children":[{"type":"text","value":"manifest.json "}]}]},{"type":"text","value":"file in the root of the extension’s directory. For now, this file will hold only the following basic information about your extension:"}]},{"type":"comment","value":"kg-card-begin: html"},{"type":"element","tagName":"script","properties":{"src":"https://gist.github.com/shahednasser/6beec3aa60a2187e99e85479f8806c30.js"},"children":[]},{"type":"comment","value":"kg-card-end: html"},{"type":"element","tagName":"p","properties":{},"children":[{"type":"text","value":"Here is what everything in this file means:"}]},{"type":"element","tagName":"ol","properties":{},"children":[{"type":"element","tagName":"li","properties":{},"children":[{"type":"element","tagName":"strong","properties":{},"children":[{"type":"element","tagName":"strong","properties":{},"children":[{"type":"text","value":"name:"}]}]},{"type":"text","value":" The name of the extension. I named the extension “Pikachu Everywhere”"}]},{"type":"element","tagName":"li","properties":{},"children":[{"type":"element","tagName":"strong","properties":{},"children":[{"type":"element","tagName":"strong","properties":{},"children":[{"type":"text","value":"version"}]}]},{"type":"text","value":": This is the version of the extension. When you publish the extension in the Chrome Web Store and you need to update it, you will have to increase the version number so it’s good to start with a low number."}]},{"type":"element","tagName":"li","properties":{},"children":[{"type":"element","tagName":"strong","properties":{},"children":[{"type":"element","tagName":"strong","properties":{},"children":[{"type":"text","value":"description: (Optional) "}]}]},{"type":"text","value":"Description of your extension"}]},{"type":"element","tagName":"li","properties":{},"children":[{"type":"element","tagName":"strong","properties":{},"children":[{"type":"element","tagName":"strong","properties":{},"children":[{"type":"text","value":"manifest_version"}]}]},{"type":"text","value":": As of Chrome 18, developers are required to use manifest version 2, since manifest version 1 is now deprecated."}]},{"type":"element","tagName":"li","properties":{},"children":[{"type":"element","tagName":"strong","properties":{},"children":[{"type":"element","tagName":"strong","properties":{},"children":[{"type":"text","value":"icons: (Optional) "}]}]},{"type":"text","value":"Here we specify the extension’s icon. This icon will show up in your browser’s toolbar, the Chrome Web Store, and any other place that requires to show it. For that reason, it’s good to include different sizes of the icon. I have used an icon from "},{"type":"element","tagName":"a","properties":{"href":"https://www.freeiconspng.com/img/17360","rel":["noopener"]},"children":[{"type":"text","value":"Free Icons PNG"}]},{"type":"text","value":" and resized the icons using "},{"type":"element","tagName":"a","properties":{"href":"https://resizeimage.net/","rel":["noopener"]},"children":[{"type":"text","value":"https://resizeimage.net/"}]},{"type":"text","value":" (You can find the images in the "},{"type":"element","tagName":"a","properties":{"href":"https://github.com/shahednasser/pikachu-everywhere","rel":["noopener"]},"children":[{"type":"text","value":"GitHub repository"}]},{"type":"text","value":"). The paths specified for these images is relative to the root of the extension."}]}]},{"type":"element","tagName":"p","properties":{},"children":[{"type":"text","value":"With this, we have the core of any Chrome extension. Next, we’ll add the extension to the browser."}]},{"type":"element","tagName":"p","properties":{},"children":[{"type":"text","value":"Go to chrome://extensions. There, you can toggle developer mode at the top right corner. Once you do that, a new toolbar will show up with three buttons: “Load unpacked,” “Pack extension,” and “Update.” Click on “Load unpacked” and choose your extension’s directory."}]},{"type":"element","tagName":"p","properties":{},"children":[{"type":"text","value":"Once you do that, you will see that your new chrome extension is now working. Yes, it does nothing, but it’s working."}]},{"type":"element","tagName":"p","properties":{},"children":[{"type":"text","value":"In Chrome extensions, there are two types of scripts you can use. There are Background scripts and Content scripts. Background scripts run (obviously) in the background. They are loaded when needed and unloaded when idle. They are used to listen to certain events in your Chrome extension."}]},{"type":"element","tagName":"p","properties":{},"children":[{"type":"text","value":"Content scripts are scripts loaded once you open a website and injected it into it. Once you open a website, they will start running just like any other script on that website. After you close the website, the content scripts on that website will stop running as well."}]},{"type":"element","tagName":"p","properties":{},"children":[{"type":"text","value":"For our extension, we want to replace every image on every website with Pikachu images. In order to do that we need to specify a content script that works every time you open a web page."}]},{"type":"element","tagName":"p","properties":{},"children":[{"type":"text","value":"Let’s create our content script in "},{"type":"element","tagName":"code","properties":{"className":["language-text"]},"children":[{"type":"text","value":"assets/js/contentScript.js"}]},{"type":"text","value":" and include the following script to test whether our extension is working or not:"}]},{"type":"comment","value":"kg-card-begin: html"},{"type":"element","tagName":"script","properties":{"src":"https://gist.github.com/shahednasser/342ba17b0b6d715adaafd334a14416f9.js"},"children":[]},{"type":"comment","value":"kg-card-end: html"},{"type":"element","tagName":"p","properties":{},"children":[{"type":"text","value":"One other thing we need to do is tell Chrome which script do we need to run as a content script. Add the following lines to "},{"type":"element","tagName":"strong","properties":{},"children":[{"type":"element","tagName":"strong","properties":{},"children":[{"type":"text","value":"manifest.json"}]}]}]},{"type":"element","tagName":"figure","properties":{"className":["kg-card","kg-embed-card"]},"children":[{"type":"element","tagName":"iframe","properties":{"src":"https://levelup.gitconnected.com/media/d35d7df4566f532c2a0948ea540ee302","allowFullScreen":true,"frameBorder":"0","height":0,"width":0,"title":"adding content script to manifest.json","className":["t","u","v","hm","aj"],"scrolling":"auto","style":"box-sizing: inherit; position: absolute; top: 0px; left: 0px; width: 680px; height: 0px;"},"children":[]}]},{"type":"element","tagName":"p","properties":{},"children":[{"type":"text","value":"Here we are using "},{"type":"element","tagName":"code","properties":{"className":["language-text"]},"children":[{"type":"text","value":"content_scripts"}]},{"type":"text","value":" as key with the value as an array. Here’s what everything in the array means:"}]},{"type":"element","tagName":"ol","properties":{},"children":[{"type":"element","tagName":"li","properties":{},"children":[{"type":"element","tagName":"strong","properties":{},"children":[{"type":"element","tagName":"strong","properties":{},"children":[{"type":"text","value":"matches"}]}]},{"type":"text","value":": here you can specify which URLs the content script should run on. In our case we want our content script to run on all pages. So, we use the placeholder “<all_urls>” to specify that."}]},{"type":"element","tagName":"li","properties":{},"children":[{"type":"element","tagName":"strong","properties":{},"children":[{"type":"element","tagName":"strong","properties":{},"children":[{"type":"text","value":"all_frames"}]}]},{"type":"text","value":": this key is used to allow the extension to specify if JavaScript and CSS files should be injected into all frames matching the specified URL requirements or only into the topmost frame in a tab."}]},{"type":"element","tagName":"li","properties":{},"children":[{"type":"element","tagName":"strong","properties":{},"children":[{"type":"element","tagName":"strong","properties":{},"children":[{"type":"text","value":"js: "}]}]},{"type":"text","value":"here we specify the script we want to run as a content script. You can specify more than one script, so the value has to be an array"}]}]},{"type":"element","tagName":"p","properties":{},"children":[{"type":"text","value":"Now we’re ready to test our content script."}]},{"type":"element","tagName":"p","properties":{},"children":[{"type":"text","value":"But before we do that we need to do one small step. In order to inform chrome that we updated our chrome extension, we need to go back to chrome://extensions, go to our extension, and press the reload icon."}]},{"type":"element","tagName":"p","properties":{},"children":[{"type":"text","value":"To test if our content script is working, go to any web page you want, reload the page if it was opened before, then right-click and press Inspect. In DevTools, open the Console tab. You will see in your console as expected:"}]},{"type":"element","tagName":"figure","properties":{"className":["kg-card","kg-image-card","kg-card-hascaption"]},"children":[{"type":"element","tagName":"img","properties":{"src":"https://res-3.cloudinary.com/hbqmf3mbz/image/upload/q_auto/v1/ghost-blog-images/chrome-extension-screenshot-1.png","className":["kg-image"],"alt":"","loading":"lazy"},"children":[]},{"type":"element","tagName":"figcaption","properties":{},"children":[{"type":"text","value":"Console of a web page"}]}]},{"type":"element","tagName":"p","properties":{},"children":[{"type":"text","value":"So, we can verify that our content script is working. Try reloading the page, and the content script will execute again. Try opening another web page, and the content script will execute on that page independently."}]},{"type":"element","tagName":"p","properties":{},"children":[{"type":"text","value":"Now, we know how to inject a script on any web page, and through that script we can do our work."}]},{"type":"element","tagName":"p","properties":{},"children":[{"type":"text","value":"In our case, we need to replace every image in any website with random Pikachu images. To do that, we will use "},{"type":"element","tagName":"a","properties":{"href":"https://some-random-api.ml/img/pikachu","rel":["noopener"]},"children":[{"type":"text","value":"this API"}]},{"type":"text","value":" which will provide us with a JSON object containing the link of a Pikachu image. Each request to that API will look something like this:"}]},{"type":"element","tagName":"figure","properties":{"className":["kg-card","kg-image-card","kg-card-hascaption"]},"children":[{"type":"element","tagName":"img","properties":{"src":"https://res-3.cloudinary.com/hbqmf3mbz/image/upload/q_auto/v1/ghost-blog-images/chrome-extension-screenshot-2.png","className":["kg-image"],"alt":"","loading":"lazy"},"children":[]},{"type":"element","tagName":"figcaption","properties":{},"children":[{"type":"text","value":"Result of API call"}]}]},{"type":"element","tagName":"p","properties":{},"children":[{"type":"text","value":"With the help of this API, the steps to achieve what we need will be as follows:"}]},{"type":"element","tagName":"ol","properties":{},"children":[{"type":"element","tagName":"li","properties":{},"children":[{"type":"text","value":"Get all image elements on the web page."}]},{"type":"element","tagName":"li","properties":{},"children":[{"type":"text","value":"Perform a GET request to the API to retrieve a random Pikachu image link."}]},{"type":"element","tagName":"li","properties":{},"children":[{"type":"text","value":"Replace the image elements’ "},{"type":"element","tagName":"code","properties":{"className":["language-text"]},"children":[{"type":"text","value":"src"}]},{"type":"text","value":" attributes with the links we retrieve."}]}]},{"type":"element","tagName":"p","properties":{},"children":[{"type":"text","value":"This all sounds good and easy, but there is one complication: content scripts in Chrome extensions cannot perform Cross-Origin requests."}]},{"type":"element","tagName":"p","properties":{},"children":[{"type":"text","value":"So how can we fetch the image URLs from the API? We will need to use a Background script."}]},{"type":"element","tagName":"p","properties":{},"children":[{"type":"text","value":"As we specified earlier, Background scripts run in the background in Chrome. They are not dependent on any web page."}]},{"type":"element","tagName":"p","properties":{},"children":[{"type":"text","value":"Scripts in Chrome extensions are isolated from each other. However, they share a mean of communication through the "},{"type":"element","tagName":"a","properties":{"href":"https://developer.chrome.com/extensions/api_index","rel":["noopener"]},"children":[{"type":"text","value":"Chrome API"}]},{"type":"text","value":"."}]},{"type":"element","tagName":"p","properties":{},"children":[{"type":"text","value":"What we will do is that we will listen for a message in the "},{"type":"element","tagName":"strong","properties":{},"children":[{"type":"element","tagName":"strong","properties":{},"children":[{"type":"text","value":"background"}]}]},{"type":"text","value":" script sent from "},{"type":"element","tagName":"strong","properties":{},"children":[{"type":"element","tagName":"strong","properties":{},"children":[{"type":"text","value":"content"}]}]},{"type":"text","value":" scripts. Every time a web page opens, our content script will run, send a message to the background script requesting an image URL. The background script will then perform the asynchronous call to the Pikachu API, retrieve the link and send it back to the content script."}]},{"type":"element","tagName":"p","properties":{},"children":[{"type":"text","value":"This may sound complicated at first, but it’s actually done in a very simple manner."}]},{"type":"element","tagName":"p","properties":{},"children":[{"type":"text","value":"Let us first start by creating the background script and see it in action. Create "},{"type":"element","tagName":"code","properties":{"className":["language-text"]},"children":[{"type":"text","value":"assets/js/background.js"}]},{"type":"text","value":" with the following content:"}]},{"type":"comment","value":"kg-card-begin: html"},{"type":"element","tagName":"script","properties":{"src":"https://gist.github.com/shahednasser/b45de97a11eda346d1ea427b88c406f4.js"},"children":[]},{"type":"comment","value":"kg-card-end: html"},{"type":"element","tagName":"p","properties":{},"children":[{"type":"text","value":"Now, we need to tell Chrome that the file we just created will be our background script. In order to do that, we need to go back to our "},{"type":"element","tagName":"strong","properties":{},"children":[{"type":"element","tagName":"strong","properties":{},"children":[{"type":"text","value":"manifest.json"}]}]},{"type":"text","value":" and add the following:"}]},{"type":"comment","value":"kg-card-begin: html"},{"type":"element","tagName":"script","properties":{"src":"https://gist.github.com/shahednasser/c7a26cf5b3b2415110cb97e44f2b3834.js"},"children":[]},{"type":"comment","value":"kg-card-end: html"},{"type":"element","tagName":"p","properties":{},"children":[{"type":"text","value":"Here is what everything means:"}]},{"type":"element","tagName":"ol","properties":{},"children":[{"type":"element","tagName":"li","properties":{},"children":[{"type":"element","tagName":"strong","properties":{},"children":[{"type":"element","tagName":"strong","properties":{},"children":[{"type":"text","value":"scripts: "}]}]},{"type":"text","value":"An array that holds the paths of the scripts we will use as background scripts. In our case, it’s just one."}]},{"type":"element","tagName":"li","properties":{},"children":[{"type":"element","tagName":"strong","properties":{},"children":[{"type":"element","tagName":"strong","properties":{},"children":[{"type":"text","value":"persistent"}]}]},{"type":"text","value":": should always be false unless the extension uses Chrome’s Web Request API to block or modify network requests."}]}]},{"type":"element","tagName":"p","properties":{},"children":[{"type":"text","value":"Now, Chrome will know about our background script. It will load it when it’s needed, and unload it when it becomes idle."}]},{"type":"element","tagName":"p","properties":{},"children":[{"type":"text","value":"To test our background script, we need to reload our extension as we did earlier."}]},{"type":"element","tagName":"p","properties":{},"children":[{"type":"text","value":"Once you do that, you will see a new link appear in your extension’s info box with the text “inspect views background page”:"}]},{"type":"element","tagName":"figure","properties":{"className":["kg-card","kg-image-card"]},"children":[{"type":"element","tagName":"img","properties":{"src":"https://res-5.cloudinary.com/hbqmf3mbz/image/upload/q_auto/v1/ghost-blog-images/chrome-extension-screenshot-3.png","className":["kg-image"],"alt":"","loading":"lazy"},"children":[]}]},{"type":"element","tagName":"p","properties":{},"children":[{"type":"text","value":"If you click on it, a new DevTool will open and in the console you will see:"}]},{"type":"element","tagName":"figure","properties":{"className":["kg-card","kg-image-card"]},"children":[{"type":"element","tagName":"img","properties":{"src":"https://res-4.cloudinary.com/hbqmf3mbz/image/upload/q_auto/v1/ghost-blog-images/chrome-extension-screenshot-4.png","className":["kg-image"],"alt":"","loading":"lazy"},"children":[]}]},{"type":"element","tagName":"p","properties":{},"children":[{"type":"text","value":"We can now confirm that our background script is working. Even if you close any other web page or reload any web page, nothing will change in the background script."}]},{"type":"element","tagName":"p","properties":{},"children":[{"type":"text","value":"Let’s go back to our objective again. We now need to listen to messages in our background script, and on receiving a new message fetch the URL from the Pikachu API."}]},{"type":"element","tagName":"p","properties":{},"children":[{"type":"text","value":"In order to listen to messages, we will use the following method in Chrome’s API:"}]},{"type":"comment","value":"kg-card-begin: html"},{"type":"element","tagName":"script","properties":{"src":"https://gist.github.com/shahednasser/4426ca477880552e4c563be8264b968c.js"},"children":[]},{"type":"comment","value":"kg-card-end: html"},{"type":"element","tagName":"p","properties":{},"children":[{"type":"text","value":"We need to pass a function to "},{"type":"element","tagName":"code","properties":{"className":["language-text"]},"children":[{"type":"text","value":"addListener"}]},{"type":"text","value":". This function will be executed on receiving any new message. The function takes three parameters:"}]},{"type":"element","tagName":"ol","properties":{},"children":[{"type":"element","tagName":"li","properties":{},"children":[{"type":"element","tagName":"strong","properties":{},"children":[{"type":"element","tagName":"strong","properties":{},"children":[{"type":"text","value":"message: "}]}]},{"type":"text","value":"the message sent of type any."}]},{"type":"element","tagName":"li","properties":{},"children":[{"type":"element","tagName":"strong","properties":{},"children":[{"type":"element","tagName":"strong","properties":{},"children":[{"type":"text","value":"sender: "}]}]},{"type":"text","value":"of type "},{"type":"element","tagName":"a","properties":{"href":"https://developer.chrome.com/extensions/runtime#type-MessageSender","rel":["noopener"]},"children":[{"type":"text","value":"MessageSender"}]}]},{"type":"element","tagName":"li","properties":{},"children":[{"type":"element","tagName":"strong","properties":{},"children":[{"type":"element","tagName":"strong","properties":{},"children":[{"type":"text","value":"senderResponse: "}]}]},{"type":"text","value":"Callback function to call (at most once) when you have a response. This function is sent by the sender on sending the message."}]}]},{"type":"element","tagName":"p","properties":{},"children":[{"type":"text","value":"So this is what our "},{"type":"element","tagName":"strong","properties":{},"children":[{"type":"element","tagName":"strong","properties":{},"children":[{"type":"text","value":"background.js "}]}]},{"type":"text","value":"file should look like now:"}]},{"type":"comment","value":"kg-card-begin: html"},{"type":"element","tagName":"script","properties":{"src":"https://gist.github.com/shahednasser/4920b314274d2695b5ac0e771a4cd52c.js"},"children":[]},{"type":"comment","value":"kg-card-end: html"},{"type":"element","tagName":"p","properties":{},"children":[{"type":"text","value":"Now our background script is ready to receive messages, but there are no messages being sent."}]},{"type":"element","tagName":"p","properties":{},"children":[{"type":"text","value":"As we said earlier, each time a page opens, our content script will loop through image elements in the web page, and send a message to the background script to fetch the Pikachu image link."}]},{"type":"element","tagName":"p","properties":{},"children":[{"type":"text","value":"In order to send messages, we will need to use the following method from Chrome’s API:"}]},{"type":"comment","value":"kg-card-begin: html"},{"type":"element","tagName":"script","properties":{"src":"https://gist.github.com/shahednasser/03e371015982ef271518922e56139c19.js"},"children":[]},{"type":"comment","value":"kg-card-end: html"},{"type":"element","tagName":"p","properties":{},"children":[{"type":"text","value":"The parameter’s being sent are as follows:"}]},{"type":"element","tagName":"ol","properties":{},"children":[{"type":"element","tagName":"li","properties":{},"children":[{"type":"element","tagName":"strong","properties":{},"children":[{"type":"element","tagName":"strong","properties":{},"children":[{"type":"text","value":"extensionId: (Optional) "}]}]},{"type":"text","value":"the ID of the extension we are sending the message to. If it’s omitted, the message will be sent to our own extension."}]},{"type":"element","tagName":"li","properties":{},"children":[{"type":"element","tagName":"strong","properties":{},"children":[{"type":"element","tagName":"strong","properties":{},"children":[{"type":"text","value":"message:"}]}]},{"type":"text","value":" The message we are sending of type any."}]},{"type":"element","tagName":"li","properties":{},"children":[{"type":"element","tagName":"strong","properties":{},"children":[{"type":"element","tagName":"strong","properties":{},"children":[{"type":"text","value":"options: Optional"}]}]}]},{"type":"element","tagName":"li","properties":{},"children":[{"type":"element","tagName":"strong","properties":{},"children":[{"type":"element","tagName":"strong","properties":{},"children":[{"type":"text","value":"responseCallback: "}]}]},{"type":"text","value":"The function to call after receiving the message. This is the "},{"type":"element","tagName":"strong","properties":{},"children":[{"type":"element","tagName":"strong","properties":{},"children":[{"type":"text","value":"senderResponse"}]}]},{"type":"text","value":" parameter in the previous method we saw."}]}]},{"type":"element","tagName":"p","properties":{},"children":[{"type":"text","value":"So, we will use this method in our content script to send a message to the background script to fetch the Pikachu URL from the API. Our content script should now look like this:"}]},{"type":"comment","value":"kg-card-begin: html"},{"type":"element","tagName":"script","properties":{"src":"https://gist.github.com/shahednasser/bd4051325006a5f3ca01e882b51eebc5.js"},"children":[]},{"type":"comment","value":"kg-card-end: html"},{"type":"element","tagName":"p","properties":{},"children":[{"type":"text","value":"Here’s what we’re doing in the above code:"}]},{"type":"element","tagName":"ol","properties":{},"children":[{"type":"element","tagName":"li","properties":{},"children":[{"type":"element","tagName":"strong","properties":{},"children":[{"type":"element","tagName":"strong","properties":{},"children":[{"type":"text","value":"Line 1: "}]}]},{"type":"text","value":"get all image elements on the web page."}]},{"type":"element","tagName":"li","properties":{},"children":[{"type":"element","tagName":"strong","properties":{},"children":[{"type":"element","tagName":"strong","properties":{},"children":[{"type":"text","value":"Line 2–6: "}]}]},{"type":"text","value":"Loop through the image elements. On each element, we send a message to the background script. As you can tell, we are omitting the optional "},{"type":"element","tagName":"strong","properties":{},"children":[{"type":"element","tagName":"strong","properties":{},"children":[{"type":"text","value":"extensionId "}]}]},{"type":"text","value":"and "},{"type":"element","tagName":"strong","properties":{},"children":[{"type":"element","tagName":"strong","properties":{},"children":[{"type":"text","value":"options"}]}]},{"type":"text","value":" parameters. The first parameter is the "},{"type":"element","tagName":"strong","properties":{},"children":[{"type":"element","tagName":"strong","properties":{},"children":[{"type":"text","value":"message "}]}]},{"type":"text","value":"we are sending, and the second parameter is the callback function we want the background to execute after it fetches the image’s link."}]}]},{"type":"element","tagName":"p","properties":{},"children":[{"type":"text","value":"We are sending the object "},{"type":"element","tagName":"strong","properties":{},"children":[{"type":"element","tagName":"strong","properties":{},"children":[{"type":"text","value":"{msg: ‘image’, index: i}"}]}]},{"type":"text","value":" to our extension. This object has two properties. The first one is to specify what our message is about. In our extension, it might not be very helpful, but when you have an extension that uses different types of messages, it’s a good idea to differentiate what each message is for. The second property is to specify the index of the image the link is for in the "},{"type":"element","tagName":"strong","properties":{},"children":[{"type":"element","tagName":"strong","properties":{},"children":[{"type":"text","value":"images"}]}]},{"type":"text","value":" array. The reason behind this is that the callback function replacing the image will be called asynchronously, so "},{"type":"element","tagName":"strong","properties":{},"children":[{"type":"element","tagName":"strong","properties":{},"children":[{"type":"text","value":"i "}]}]},{"type":"text","value":"might point at another index at the time it’s being executed."}]},{"type":"element","tagName":"p","properties":{},"children":[{"type":"text","value":"The callback function receives an object that has two properties. "},{"type":"element","tagName":"strong","properties":{},"children":[{"type":"element","tagName":"strong","properties":{},"children":[{"type":"text","value":"data"}]}]},{"type":"text","value":" will be the data received from the API call, and "},{"type":"element","tagName":"strong","properties":{},"children":[{"type":"element","tagName":"strong","properties":{},"children":[{"type":"text","value":"index"}]}]},{"type":"text","value":" will be the index of the image in the array "},{"type":"element","tagName":"strong","properties":{},"children":[{"type":"element","tagName":"strong","properties":{},"children":[{"type":"text","value":"images"}]}]},{"type":"text","value":". Once it’s called, it will set the image at "},{"type":"element","tagName":"strong","properties":{},"children":[{"type":"element","tagName":"strong","properties":{},"children":[{"type":"text","value":"index"}]}]},{"type":"text","value":" in "},{"type":"element","tagName":"strong","properties":{},"children":[{"type":"element","tagName":"strong","properties":{},"children":[{"type":"text","value":"images"}]}]},{"type":"text","value":" to the link property is "},{"type":"element","tagName":"strong","properties":{},"children":[{"type":"element","tagName":"strong","properties":{},"children":[{"type":"text","value":"data"}]}]},{"type":"text","value":"."}]},{"type":"element","tagName":"p","properties":{},"children":[{"type":"text","value":"Now, we need to fetch the data from the API in our background script whenever the content script sends a message. We can do this as follows:"}]},{"type":"comment","value":"kg-card-begin: html"},{"type":"element","tagName":"script","properties":{"src":"https://gist.github.com/shahednasser/9c024a8b40396e7c6a6ae1250fb5d94d.js"},"children":[]},{"type":"comment","value":"kg-card-end: html"},{"type":"element","tagName":"p","properties":{},"children":[{"type":"text","value":"Here’s what’s going on here:"}]},{"type":"element","tagName":"ol","properties":{},"children":[{"type":"element","tagName":"li","properties":{},"children":[{"type":"element","tagName":"strong","properties":{},"children":[{"type":"element","tagName":"strong","properties":{},"children":[{"type":"text","value":"Line 1:"}]}]},{"type":"text","value":" Listen to messages in our extension"}]},{"type":"element","tagName":"li","properties":{},"children":[{"type":"element","tagName":"strong","properties":{},"children":[{"type":"element","tagName":"strong","properties":{},"children":[{"type":"text","value":"Line 2: "}]}]},{"type":"text","value":"Check if the message is to get the image URL. This will use the "},{"type":"element","tagName":"strong","properties":{},"children":[{"type":"element","tagName":"strong","properties":{},"children":[{"type":"text","value":"msg "}]}]},{"type":"text","value":"attribute we passed when we sent the message in "},{"type":"element","tagName":"strong","properties":{},"children":[{"type":"element","tagName":"strong","properties":{},"children":[{"type":"text","value":"contentScript"}]}]}]},{"type":"element","tagName":"li","properties":{},"children":[{"type":"element","tagName":"strong","properties":{},"children":[{"type":"element","tagName":"strong","properties":{},"children":[{"type":"text","value":"Line 3–8:"}]}]},{"type":"text","value":" If the condition in "},{"type":"element","tagName":"strong","properties":{},"children":[{"type":"element","tagName":"strong","properties":{},"children":[{"type":"text","value":"Line 2"}]}]},{"type":"text","value":" is true, fetch the data from the API. In the first "},{"type":"element","tagName":"code","properties":{"className":["language-text"]},"children":[{"type":"text","value":"Promise"}]},{"type":"text","value":" we get the response text, then in the second "},{"type":"element","tagName":"code","properties":{"className":["language-text"]},"children":[{"type":"text","value":"Promise"}]},{"type":"text","value":" we parse the JSON object and call "},{"type":"element","tagName":"strong","properties":{},"children":[{"type":"element","tagName":"strong","properties":{},"children":[{"type":"text","value":"senderResponse"}]}]},{"type":"text","value":" which is the callback function specified by the sender of the message. We pass with it an object with the parameters "},{"type":"element","tagName":"strong","properties":{},"children":[{"type":"element","tagName":"strong","properties":{},"children":[{"type":"text","value":"data "}]}]},{"type":"text","value":"(the data received through the API call) and "},{"type":"element","tagName":"strong","properties":{},"children":[{"type":"element","tagName":"strong","properties":{},"children":[{"type":"text","value":"index"}]}]},{"type":"text","value":" (the index of the image, sent by the sender)."}]},{"type":"element","tagName":"li","properties":{},"children":[{"type":"element","tagName":"strong","properties":{},"children":[{"type":"element","tagName":"strong","properties":{},"children":[{"type":"text","value":"Line 9: "}]}]},{"type":"text","value":"simple handling of any error that might occur."}]},{"type":"element","tagName":"li","properties":{},"children":[{"type":"element","tagName":"strong","properties":{},"children":[{"type":"element","tagName":"strong","properties":{},"children":[{"type":"text","value":"Line 10"}]}]},{"type":"text","value":": If the sender specifies a callback function, it has to be called before the listener stops executing or else it will throw an error. In our case, we have to wait until we receive a response from the API which will happen asynchronously, so we return true to specify that it will be called asynchronously."}]}]},{"type":"element","tagName":"p","properties":{},"children":[{"type":"text","value":"Okay, our scripts are ready. Only one thing left — we need to give permission to the API in our "},{"type":"element","tagName":"strong","properties":{},"children":[{"type":"element","tagName":"strong","properties":{},"children":[{"type":"text","value":"manifest.json:"}]}]}]},{"type":"comment","value":"kg-card-begin: html"},{"type":"element","tagName":"script","properties":{"src":"https://gist.github.com/shahednasser/8d6786f1583a91fe31635715caf44b0c.js"},"children":[]},{"type":"comment","value":"kg-card-end: html"},{"type":"element","tagName":"p","properties":{},"children":[{"type":"text","value":"All you need to do now is reload the extension in chrome://extensions, then open any page and see Pikachu everywhere!"}]},{"type":"element","tagName":"p","properties":{},"children":[{"type":"text","value":"Of course, this extension will not replace background images. This can be done by looping through elements, checking if they have background images, and replacing them the same way."}]}],"data":{"quirksMode":false}},"tableOfContents":[]},"featureImageSharp":{"base":"chrome-extension.gif","publicURL":"/static/498b0f8235a6b5cc5d72fd2244922304/chrome-extension.gif","imageMeta":{"width":500,"height":309},"childImageSharp":null}},"ghostPost":{"id":"Ghost__Post__6127ba1b3ed159214d382e71","title":"Chrome Extension Tutorial — Replace Images in Any Website with Pikachu","slug":"chrome-extension-tutorial-replace-images-in-any-website-with-pikachu","featured":false,"feature_image":"https://res-3.cloudinary.com/hbqmf3mbz/image/upload/q_auto/v1/ghost-blog-images/chrome-extension.gif","excerpt":"What’s a better way to learn how to make a Chrome extension than making the web cuter with Pikachu images?","custom_excerpt":"What’s a better way to learn how to make a Chrome extension than making the web cuter with Pikachu images?","visibility":"public","created_at_pretty":"6 Nov 2020","published_at_pretty":"11 Apr 2019","updated_at_pretty":"26 Aug 2021","created_at":"2020-11-06T18:32:10.000+00:00","published_at":"2019-04-11T18:30:00.000+00:00","updated_at":"2021-08-26T17:55:09.000+00:00","meta_title":null,"meta_description":null,"og_description":null,"og_image":null,"og_title":null,"twitter_description":null,"twitter_image":null,"twitter_title":null,"authors":[{"slug":"shahed","url":"https://backend.shahednasser.com/author/shahed/","name":"Shahed Nasser","bio":null,"cover_image":null,"profile_image":"https://backend.shahednasser.com/content/images/2022/03/IMG_0591.jpg","location":null,"website":null,"twitter":null,"facebook":null,"meta_title":null,"meta_description":null,"coverImageSharp":null,"profileImageSharp":null}],"primary_author":{"slug":"shahed","url":"https://backend.shahednasser.com/author/shahed/","name":"Shahed Nasser","bio":null,"cover_image":null,"profile_image":"https://backend.shahednasser.com/content/images/2022/03/IMG_0591.jpg","location":null,"website":null,"twitter":null,"facebook":null,"meta_title":null,"meta_description":null,"coverImageSharp":null,"profileImageSharp":{"base":"IMG_0591.jpg","publicURL":"/static/ceb49c3c631485453e71e00d7f84b069/IMG_0591.jpg","imageMeta":{"width":1182,"height":1179},"childImageSharp":{"fluid":{"base64":"data:image/jpeg;base64,/9j/2wBDABALDA4MChAODQ4SERATGCgaGBYWGDEjJR0oOjM9PDkzODdASFxOQERXRTc4UG1RV19iZ2hnPk1xeXBkeFxlZ2P/2wBDARESEhgVGC8aGi9jQjhCY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2P/wgARCAAUABQDASIAAhEBAxEB/8QAGAABAQEBAQAAAAAAAAAAAAAAAAMEAQL/xAAWAQEBAQAAAAAAAAAAAAAAAAADBAL/2gAMAwEAAhADEAAAAdXiFM6i0CohUWXoKn//xAAcEAACAgIDAAAAAAAAAAAAAAACAwESBBEhM0H/2gAIAQEAAQUCWySE3WEr7SzbXjAj4iKty+sOQ//EABYRAQEBAAAAAAAAAAAAAAAAAAERIP/aAAgBAwEBPwEhj//EABQRAQAAAAAAAAAAAAAAAAAAACD/2gAIAQIBAT8BH//EAB4QAAIBBAMBAAAAAAAAAAAAAAABIRESMUECECJx/9oACAEBAAY/ApVGWvOjzgtUwLlTZA0sdL4f/8QAHBAAAwACAwEAAAAAAAAAAAAAAAERITFBkbHB/9oACAEBAAE/IahkCy+N2GwZpjQiJHJCspUFY0QrSi+HqiW2rgf/2gAMAwEAAgADAAAAEPw3/wD/xAAYEQEBAAMAAAAAAAAAAAAAAAAAARExQf/aAAgBAwEBPxCtjDqP/8QAFxEBAQEBAAAAAAAAAAAAAAAAAQAxEf/aAAgBAgEBPxBFus6Tt//EAB8QAQEAAgIBBQAAAAAAAAAAAAERACExQWFRcYGR0f/aAAgBAQABPxAuaBPPzkO1wyX7F4wkwXanfZrFQgeqE9JgS14vVOvrERIJomVBKwt2jebAeP0yVa8h1n//2Q==","aspectRatio":1,"src":"/static/ceb49c3c631485453e71e00d7f84b069/31709/IMG_0591.jpg","srcSet":"/static/ceb49c3c631485453e71e00d7f84b069/f340b/IMG_0591.jpg 28w,\n/static/ceb49c3c631485453e71e00d7f84b069/22d64/IMG_0591.jpg 55w,\n/static/ceb49c3c631485453e71e00d7f84b069/31709/IMG_0591.jpg 110w,\n/static/ceb49c3c631485453e71e00d7f84b069/aa249/IMG_0591.jpg 165w,\n/static/ceb49c3c631485453e71e00d7f84b069/0dc33/IMG_0591.jpg 220w,\n/static/ceb49c3c631485453e71e00d7f84b069/d8257/IMG_0591.jpg 1182w","srcWebp":"/static/ceb49c3c631485453e71e00d7f84b069/8678c/IMG_0591.webp","srcSetWebp":"/static/ceb49c3c631485453e71e00d7f84b069/59cda/IMG_0591.webp 28w,\n/static/ceb49c3c631485453e71e00d7f84b069/7da75/IMG_0591.webp 55w,\n/static/ceb49c3c631485453e71e00d7f84b069/8678c/IMG_0591.webp 110w,\n/static/ceb49c3c631485453e71e00d7f84b069/f282e/IMG_0591.webp 165w,\n/static/ceb49c3c631485453e71e00d7f84b069/a7b21/IMG_0591.webp 220w,\n/static/ceb49c3c631485453e71e00d7f84b069/63099/IMG_0591.webp 1182w","sizes":"(max-width: 110px) 100vw, 110px"}}}},"primary_tag":{"slug":"browser-extensions","url":"https://backend.shahednasser.com/tag/browser-extensions/","name":"Browser Extensions","visibility":"public","feature_image":"https://backend.shahednasser.com/content/images/2022/01/photo-1616499370260-485b3e5ed653-2.jpeg","description":"Learn more about Browser Extensions through tutorials, articles, and tips.","meta_title":null,"meta_description":null,"featureImageSharp":{"base":"photo-1616499370260-485b3e5ed653-2.jpeg","publicURL":"/static/17d3062927434b0d9d22f3e45ece68b8/photo-1616499370260-485b3e5ed653-2.jpeg","imageMeta":{"width":2000,"height":1334},"childImageSharp":{"fluid":{"base64":"data:image/jpeg;base64,/9j/2wBDABALDA4MChAODQ4SERATGCgaGBYWGDEjJR0oOjM9PDkzODdASFxOQERXRTc4UG1RV19iZ2hnPk1xeXBkeFxlZ2P/2wBDARESEhgVGC8aGi9jQjhCY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2P/wgARCAANABQDASIAAhEBAxEB/8QAGAAAAgMAAAAAAAAAAAAAAAAAAAMCBAX/xAAVAQEBAAAAAAAAAAAAAAAAAAAAAv/aAAwDAQACEAMQAAABQ+hsRUBAf//EABoQAQADAAMAAAAAAAAAAAAAAAIAAQMEEyL/2gAIAQEAAQUCKpA3nefSo/JALzvlaXP/xAAVEQEBAAAAAAAAAAAAAAAAAAAQEf/aAAgBAwEBPwGH/8QAFREBAQAAAAAAAAAAAAAAAAAAERD/2gAIAQIBAT8BSf/EAB4QAAEDBAMAAAAAAAAAAAAAAAEAAiEDEBEiMVFh/9oACAEBAAY/Ai1x9CG2/VmEcoVCJwogL//EABoQAQEBAQADAAAAAAAAAAAAAAERACFBcYH/2gAIAQEAAT8h6pD2YW6ifbqrwMDk3W6VFUxyFZ4ib//aAAwDAQACAAMAAAAQLB//xAAXEQEAAwAAAAAAAAAAAAAAAAAAAREh/9oACAEDAQE/EJs1/8QAFREBAQAAAAAAAAAAAAAAAAAAABH/2gAIAQIBAT8QFf/EABwQAQEAAwEAAwAAAAAAAAAAAAERACExQVFhgf/aAAgBAQABPxCsM1ex6ZRBIol9InPbigkXRbrDW2E9042KoTWHL8fWeOKEM/Xuf//Z","aspectRatio":1.5028901734104045,"src":"/static/17d3062927434b0d9d22f3e45ece68b8/d5c54/photo-1616499370260-485b3e5ed653-2.jpg","srcSet":"/static/17d3062927434b0d9d22f3e45ece68b8/65d8c/photo-1616499370260-485b3e5ed653-2.jpg 260w,\n/static/17d3062927434b0d9d22f3e45ece68b8/c5f21/photo-1616499370260-485b3e5ed653-2.jpg 520w,\n/static/17d3062927434b0d9d22f3e45ece68b8/d5c54/photo-1616499370260-485b3e5ed653-2.jpg 1040w,\n/static/17d3062927434b0d9d22f3e45ece68b8/81a53/photo-1616499370260-485b3e5ed653-2.jpg 1560w,\n/static/17d3062927434b0d9d22f3e45ece68b8/4e5f3/photo-1616499370260-485b3e5ed653-2.jpg 2000w","srcWebp":"/static/17d3062927434b0d9d22f3e45ece68b8/e4875/photo-1616499370260-485b3e5ed653-2.webp","srcSetWebp":"/static/17d3062927434b0d9d22f3e45ece68b8/dc8f3/photo-1616499370260-485b3e5ed653-2.webp 260w,\n/static/17d3062927434b0d9d22f3e45ece68b8/2db4b/photo-1616499370260-485b3e5ed653-2.webp 520w,\n/static/17d3062927434b0d9d22f3e45ece68b8/e4875/photo-1616499370260-485b3e5ed653-2.webp 1040w,\n/static/17d3062927434b0d9d22f3e45ece68b8/f5845/photo-1616499370260-485b3e5ed653-2.webp 1560w,\n/static/17d3062927434b0d9d22f3e45ece68b8/49d6b/photo-1616499370260-485b3e5ed653-2.webp 2000w","sizes":"(max-width: 1040px) 100vw, 1040px"}}}},"tags":[{"slug":"browser-extensions","url":"https://backend.shahednasser.com/tag/browser-extensions/","name":"Browser Extensions","visibility":"public","feature_image":"https://backend.shahednasser.com/content/images/2022/01/photo-1616499370260-485b3e5ed653-2.jpeg","description":"Learn more about Browser Extensions through tutorials, articles, and tips.","meta_title":null,"meta_description":null,"featureImageSharp":null},{"slug":"beginner","url":"https://backend.shahednasser.com/tag/beginner/","name":"Beginners","visibility":"public","feature_image":"https://backend.shahednasser.com/content/images/2022/01/photo-1521185496955-15097b20c5fe-2.jpeg","description":"Tutorials, articles, and tips to help beginners accelerate their journey in programming.","meta_title":"Beginners","meta_description":"Tutorials, articles, and tips to help beginners accelerate their journey in programming.","featureImageSharp":null},{"slug":"js","url":"https://backend.shahednasser.com/tag/js/","name":"Javascript","visibility":"public","feature_image":"https://backend.shahednasser.com/content/images/2022/01/photo-1592609931095-54a2168ae893-2.jpeg","description":"Learn more about Javascript through tutorials, articles, and tips.","meta_title":null,"meta_description":"Learn more about Javascript through tutorials, articles and tips.","featureImageSharp":null}],"plaintext":"This article was also published on Level Up Coding's gitconnected\n[https://levelup.gitconnected.com/chrome-extension-tutorial-replace-images-in-any-website-with-pikachu-de2a6e3548bb]\n\nWhat’s a better way to learn how to make a Chrome extension than making the web\ncuter with Pikachu images?\n\nIn this tutorial, I will show you how you can create a simple Chrome extension\nthat replaces images on websites with Pikachu images. We will discuss topics\nlike content scripts, background scripts, and messages between scripts in Chrome\nextensions.\n\nThe code for this tutorial is available here\n[https://github.com/shahednasser/pikachu-everywhere].\n\nFirst thing you need to do when creating any chrome extension is start with\ncreating a manifest.json file in the root of the extension’s directory. For now,\nthis file will hold only the following basic information about your extension:\n\nHere is what everything in this file means:\n\n 1. name: The name of the extension. I named the extension “Pikachu Everywhere”\n 2. version: This is the version of the extension. When you publish the\n    extension in the Chrome Web Store and you need to update it, you will have\n    to increase the version number so it’s good to start with a low number.\n 3. description: (Optional) Description of your extension\n 4. manifest_version: As of Chrome 18, developers are required to use manifest\n    version 2, since manifest version 1 is now deprecated.\n 5. icons: (Optional) Here we specify the extension’s icon. This icon will show\n    up in your browser’s toolbar, the Chrome Web Store, and any other place that\n    requires to show it. For that reason, it’s good to include different sizes\n    of the icon. I have used an icon from Free Icons PNG\n    [https://www.freeiconspng.com/img/17360] and resized the icons using \n    https://resizeimage.net/ (You can find the images in the GitHub repository\n    [https://github.com/shahednasser/pikachu-everywhere]). The paths specified\n    for these images is relative to the root of the extension.\n\nWith this, we have the core of any Chrome extension. Next, we’ll add the\nextension to the browser.\n\nGo to chrome://extensions. There, you can toggle developer mode at the top right\ncorner. Once you do that, a new toolbar will show up with three buttons: “Load\nunpacked,” “Pack extension,” and “Update.” Click on “Load unpacked” and choose\nyour extension’s directory.\n\nOnce you do that, you will see that your new chrome extension is now working.\nYes, it does nothing, but it’s working.\n\nIn Chrome extensions, there are two types of scripts you can use. There are\nBackground scripts and Content scripts. Background scripts run (obviously) in\nthe background. They are loaded when needed and unloaded when idle. They are\nused to listen to certain events in your Chrome extension.\n\nContent scripts are scripts loaded once you open a website and injected it into\nit. Once you open a website, they will start running just like any other script\non that website. After you close the website, the content scripts on that\nwebsite will stop running as well.\n\nFor our extension, we want to replace every image on every website with Pikachu\nimages. In order to do that we need to specify a content script that works every\ntime you open a web page.\n\nLet’s create our content script in assets/js/contentScript.js and include the\nfollowing script to test whether our extension is working or not:\n\nOne other thing we need to do is tell Chrome which script do we need to run as a\ncontent script. Add the following lines to manifest.json\n\nHere we are using content_scripts as key with the value as an array. Here’s what\neverything in the array means:\n\n 1. matches: here you can specify which URLs the content script should run on.\n    In our case we want our content script to run on all pages. So, we use the\n    placeholder “<all_urls>” to specify that.\n 2. all_frames: this key is used to allow the extension to specify if JavaScript\n    and CSS files should be injected into all frames matching the specified URL\n    requirements or only into the topmost frame in a tab.\n 3. js: here we specify the script we want to run as a content script. You can\n    specify more than one script, so the value has to be an array\n\nNow we’re ready to test our content script.\n\nBut before we do that we need to do one small step. In order to inform chrome\nthat we updated our chrome extension, we need to go back to chrome://extensions,\ngo to our extension, and press the reload icon.\n\nTo test if our content script is working, go to any web page you want, reload\nthe page if it was opened before, then right-click and press Inspect. In\nDevTools, open the Console tab. You will see in your console as expected:\n\nConsole of a web pageSo, we can verify that our content script is working. Try\nreloading the page, and the content script will execute again. Try opening\nanother web page, and the content script will execute on that page\nindependently.\n\nNow, we know how to inject a script on any web page, and through that script we\ncan do our work.\n\nIn our case, we need to replace every image in any website with random Pikachu\nimages. To do that, we will use this API\n[https://some-random-api.ml/img/pikachu] which will provide us with a JSON\nobject containing the link of a Pikachu image. Each request to that API will\nlook something like this:\n\nResult of API callWith the help of this API, the steps to achieve what we need\nwill be as follows:\n\n 1. Get all image elements on the web page.\n 2. Perform a GET request to the API to retrieve a random Pikachu image link.\n 3. Replace the image elements’ src attributes with the links we retrieve.\n\nThis all sounds good and easy, but there is one complication: content scripts in\nChrome extensions cannot perform Cross-Origin requests.\n\nSo how can we fetch the image URLs from the API? We will need to use a\nBackground script.\n\nAs we specified earlier, Background scripts run in the background in Chrome.\nThey are not dependent on any web page.\n\nScripts in Chrome extensions are isolated from each other. However, they share a\nmean of communication through the Chrome API\n[https://developer.chrome.com/extensions/api_index].\n\nWhat we will do is that we will listen for a message in the background script\nsent from content scripts. Every time a web page opens, our content script will\nrun, send a message to the background script requesting an image URL. The\nbackground script will then perform the asynchronous call to the Pikachu API,\nretrieve the link and send it back to the content script.\n\nThis may sound complicated at first, but it’s actually done in a very simple\nmanner.\n\nLet us first start by creating the background script and see it in action.\nCreate assets/js/background.js with the following content:\n\nNow, we need to tell Chrome that the file we just created will be our background\nscript. In order to do that, we need to go back to our manifest.json and add the\nfollowing:\n\nHere is what everything means:\n\n 1. scripts: An array that holds the paths of the scripts we will use as\n    background scripts. In our case, it’s just one.\n 2. persistent: should always be false unless the extension uses Chrome’s Web\n    Request API to block or modify network requests.\n\nNow, Chrome will know about our background script. It will load it when it’s\nneeded, and unload it when it becomes idle.\n\nTo test our background script, we need to reload our extension as we did\nearlier.\n\nOnce you do that, you will see a new link appear in your extension’s info box\nwith the text “inspect views background page”:\n\nIf you click on it, a new DevTool will open and in the console you will see:\n\nWe can now confirm that our background script is working. Even if you close any\nother web page or reload any web page, nothing will change in the background\nscript.\n\nLet’s go back to our objective again. We now need to listen to messages in our\nbackground script, and on receiving a new message fetch the URL from the Pikachu\nAPI.\n\nIn order to listen to messages, we will use the following method in Chrome’s\nAPI:\n\nWe need to pass a function to addListener. This function will be executed on\nreceiving any new message. The function takes three parameters:\n\n 1. message: the message sent of type any.\n 2. sender: of type MessageSender\n    [https://developer.chrome.com/extensions/runtime#type-MessageSender]\n 3. senderResponse: Callback function to call (at most once) when you have a\n    response. This function is sent by the sender on sending the message.\n\nSo this is what our background.js file should look like now:\n\nNow our background script is ready to receive messages, but there are no\nmessages being sent.\n\nAs we said earlier, each time a page opens, our content script will loop through\nimage elements in the web page, and send a message to the background script to\nfetch the Pikachu image link.\n\nIn order to send messages, we will need to use the following method from\nChrome’s API:\n\nThe parameter’s being sent are as follows:\n\n 1. extensionId: (Optional) the ID of the extension we are sending the message\n    to. If it’s omitted, the message will be sent to our own extension.\n 2. message: The message we are sending of type any.\n 3. options: Optional\n 4. responseCallback: The function to call after receiving the message. This is\n    the senderResponse parameter in the previous method we saw.\n\nSo, we will use this method in our content script to send a message to the\nbackground script to fetch the Pikachu URL from the API. Our content script\nshould now look like this:\n\nHere’s what we’re doing in the above code:\n\n 1. Line 1: get all image elements on the web page.\n 2. Line 2–6: Loop through the image elements. On each element, we send a\n    message to the background script. As you can tell, we are omitting the\n    optional extensionId and options parameters. The first parameter is the \n    message we are sending, and the second parameter is the callback function we\n    want the background to execute after it fetches the image’s link.\n\nWe are sending the object {msg: ‘image’, index: i} to our extension. This object\nhas two properties. The first one is to specify what our message is about. In\nour extension, it might not be very helpful, but when you have an extension that\nuses different types of messages, it’s a good idea to differentiate what each\nmessage is for. The second property is to specify the index of the image the\nlink is for in the images array. The reason behind this is that the callback\nfunction replacing the image will be called asynchronously, so i might point at\nanother index at the time it’s being executed.\n\nThe callback function receives an object that has two properties. data will be\nthe data received from the API call, and index will be the index of the image in\nthe array images. Once it’s called, it will set the image at index in images to\nthe link property is data.\n\nNow, we need to fetch the data from the API in our background script whenever\nthe content script sends a message. We can do this as follows:\n\nHere’s what’s going on here:\n\n 1. Line 1: Listen to messages in our extension\n 2. Line 2: Check if the message is to get the image URL. This will use the msg \n    attribute we passed when we sent the message in contentScript\n 3. Line 3–8: If the condition in Line 2 is true, fetch the data from the API.\n    In the first Promise we get the response text, then in the second Promise we\n    parse the JSON object and call senderResponse which is the callback function\n    specified by the sender of the message. We pass with it an object with the\n    parameters data (the data received through the API call) and index (the\n    index of the image, sent by the sender).\n 4. Line 9: simple handling of any error that might occur.\n 5. Line 10: If the sender specifies a callback function, it has to be called\n    before the listener stops executing or else it will throw an error. In our\n    case, we have to wait until we receive a response from the API which will\n    happen asynchronously, so we return true to specify that it will be called\n    asynchronously.\n\nOkay, our scripts are ready. Only one thing left — we need to give permission to\nthe API in our manifest.json:\n\nAll you need to do now is reload the extension in chrome://extensions, then open\nany page and see Pikachu everywhere!\n\nOf course, this extension will not replace background images. This can be done\nby looping through elements, checking if they have background images, and\nreplacing them the same way.","html":"<p><em>This article was also published on <a href=\"https://levelup.gitconnected.com/chrome-extension-tutorial-replace-images-in-any-website-with-pikachu-de2a6e3548bb\">Level Up Coding's gitconnected</a></em></p><p>What’s a better way to learn how to make a Chrome extension than making the web cuter with Pikachu images?</p><p>In this tutorial, I will show you how you can create a simple Chrome extension that replaces images on websites with Pikachu images. We will discuss topics like content scripts, background scripts, and messages between scripts in Chrome extensions.</p><p>The code for this tutorial is available <a href=\"https://github.com/shahednasser/pikachu-everywhere\" rel=\"noopener\">here</a>.</p><p>First thing you need to do when creating any chrome extension is start with creating a <strong><strong>manifest.json </strong></strong>file in the root of the extension’s directory. For now, this file will hold only the following basic information about your extension:</p><!--kg-card-begin: html--><script src=\"https://gist.github.com/shahednasser/6beec3aa60a2187e99e85479f8806c30.js\"></script><!--kg-card-end: html--><p>Here is what everything in this file means:</p><ol><li><strong><strong>name:</strong></strong> The name of the extension. I named the extension “Pikachu Everywhere”</li><li><strong><strong>version</strong></strong>: This is the version of the extension. When you publish the extension in the Chrome Web Store and you need to update it, you will have to increase the version number so it’s good to start with a low number.</li><li><strong><strong>description: (Optional) </strong></strong>Description of your extension</li><li><strong><strong>manifest_version</strong></strong>: As of Chrome 18, developers are required to use manifest version 2, since manifest version 1 is now deprecated.</li><li><strong><strong>icons: (Optional) </strong></strong>Here we specify the extension’s icon. This icon will show up in your browser’s toolbar, the Chrome Web Store, and any other place that requires to show it. For that reason, it’s good to include different sizes of the icon. I have used an icon from <a href=\"https://www.freeiconspng.com/img/17360\" rel=\"noopener\">Free Icons PNG</a> and resized the icons using <a href=\"https://resizeimage.net/\" rel=\"noopener\">https://resizeimage.net/</a> (You can find the images in the <a href=\"https://github.com/shahednasser/pikachu-everywhere\" rel=\"noopener\">GitHub repository</a>). The paths specified for these images is relative to the root of the extension.</li></ol><p>With this, we have the core of any Chrome extension. Next, we’ll add the extension to the browser.</p><p>Go to chrome://extensions. There, you can toggle developer mode at the top right corner. Once you do that, a new toolbar will show up with three buttons: “Load unpacked,” “Pack extension,” and “Update.” Click on “Load unpacked” and choose your extension’s directory.</p><p>Once you do that, you will see that your new chrome extension is now working. Yes, it does nothing, but it’s working.</p><p>In Chrome extensions, there are two types of scripts you can use. There are Background scripts and Content scripts. Background scripts run (obviously) in the background. They are loaded when needed and unloaded when idle. They are used to listen to certain events in your Chrome extension.</p><p>Content scripts are scripts loaded once you open a website and injected it into it. Once you open a website, they will start running just like any other script on that website. After you close the website, the content scripts on that website will stop running as well.</p><p>For our extension, we want to replace every image on every website with Pikachu images. In order to do that we need to specify a content script that works every time you open a web page.</p><p>Let’s create our content script in <code>assets/js/contentScript.js</code> and include the following script to test whether our extension is working or not:</p><!--kg-card-begin: html--><script src=\"https://gist.github.com/shahednasser/342ba17b0b6d715adaafd334a14416f9.js\"></script><!--kg-card-end: html--><p>One other thing we need to do is tell Chrome which script do we need to run as a content script. Add the following lines to <strong><strong>manifest.json</strong></strong></p><figure class=\"kg-card kg-embed-card\"><iframe src=\"https://levelup.gitconnected.com/media/d35d7df4566f532c2a0948ea540ee302\" allowfullscreen=\"\" frameborder=\"0\" height=\"0\" width=\"0\" title=\"adding content script to manifest.json\" class=\"t u v hm aj\" scrolling=\"auto\" style=\"box-sizing: inherit; position: absolute; top: 0px; left: 0px; width: 680px; height: 0px;\"></iframe></figure><p>Here we are using <code>content_scripts</code> as key with the value as an array. Here’s what everything in the array means:</p><ol><li><strong><strong>matches</strong></strong>: here you can specify which URLs the content script should run on. In our case we want our content script to run on all pages. So, we use the placeholder “&lt;all_urls&gt;” to specify that.</li><li><strong><strong>all_frames</strong></strong>: this key is used to allow the extension to specify if JavaScript and CSS files should be injected into all frames matching the specified URL requirements or only into the topmost frame in a tab.</li><li><strong><strong>js: </strong></strong>here we specify the script we want to run as a content script. You can specify more than one script, so the value has to be an array</li></ol><p>Now we’re ready to test our content script.</p><p>But before we do that we need to do one small step. In order to inform chrome that we updated our chrome extension, we need to go back to chrome://extensions, go to our extension, and press the reload icon.</p><p>To test if our content script is working, go to any web page you want, reload the page if it was opened before, then right-click and press Inspect. In DevTools, open the Console tab. You will see in your console as expected:</p><figure class=\"kg-card kg-image-card kg-card-hascaption\"><img src=\"https://res-3.cloudinary.com/hbqmf3mbz/image/upload/q_auto/v1/ghost-blog-images/chrome-extension-screenshot-1.png\" class=\"kg-image\" alt loading=\"lazy\"><figcaption>Console of a web page</figcaption></figure><p>So, we can verify that our content script is working. Try reloading the page, and the content script will execute again. Try opening another web page, and the content script will execute on that page independently.</p><p>Now, we know how to inject a script on any web page, and through that script we can do our work.</p><p>In our case, we need to replace every image in any website with random Pikachu images. To do that, we will use <a href=\"https://some-random-api.ml/img/pikachu\" rel=\"noopener\">this API</a> which will provide us with a JSON object containing the link of a Pikachu image. Each request to that API will look something like this:</p><figure class=\"kg-card kg-image-card kg-card-hascaption\"><img src=\"https://res-3.cloudinary.com/hbqmf3mbz/image/upload/q_auto/v1/ghost-blog-images/chrome-extension-screenshot-2.png\" class=\"kg-image\" alt loading=\"lazy\"><figcaption>Result of API call</figcaption></figure><p>With the help of this API, the steps to achieve what we need will be as follows:</p><ol><li>Get all image elements on the web page.</li><li>Perform a GET request to the API to retrieve a random Pikachu image link.</li><li>Replace the image elements’ <code>src</code> attributes with the links we retrieve.</li></ol><p>This all sounds good and easy, but there is one complication: content scripts in Chrome extensions cannot perform Cross-Origin requests.</p><p>So how can we fetch the image URLs from the API? We will need to use a Background script.</p><p>As we specified earlier, Background scripts run in the background in Chrome. They are not dependent on any web page.</p><p>Scripts in Chrome extensions are isolated from each other. However, they share a mean of communication through the <a href=\"https://developer.chrome.com/extensions/api_index\" rel=\"noopener\">Chrome API</a>.</p><p>What we will do is that we will listen for a message in the <strong><strong>background</strong></strong> script sent from <strong><strong>content</strong></strong> scripts. Every time a web page opens, our content script will run, send a message to the background script requesting an image URL. The background script will then perform the asynchronous call to the Pikachu API, retrieve the link and send it back to the content script.</p><p>This may sound complicated at first, but it’s actually done in a very simple manner.</p><p>Let us first start by creating the background script and see it in action. Create <code>assets/js/background.js</code> with the following content:</p><!--kg-card-begin: html--><script src=\"https://gist.github.com/shahednasser/b45de97a11eda346d1ea427b88c406f4.js\"></script><!--kg-card-end: html--><p>Now, we need to tell Chrome that the file we just created will be our background script. In order to do that, we need to go back to our <strong><strong>manifest.json</strong></strong> and add the following:</p><!--kg-card-begin: html--><script src=\"https://gist.github.com/shahednasser/c7a26cf5b3b2415110cb97e44f2b3834.js\"></script><!--kg-card-end: html--><p>Here is what everything means:</p><ol><li><strong><strong>scripts: </strong></strong>An array that holds the paths of the scripts we will use as background scripts. In our case, it’s just one.</li><li><strong><strong>persistent</strong></strong>: should always be false unless the extension uses Chrome’s Web Request API to block or modify network requests.</li></ol><p>Now, Chrome will know about our background script. It will load it when it’s needed, and unload it when it becomes idle.</p><p>To test our background script, we need to reload our extension as we did earlier.</p><p>Once you do that, you will see a new link appear in your extension’s info box with the text “inspect views background page”:</p><figure class=\"kg-card kg-image-card\"><img src=\"https://res-5.cloudinary.com/hbqmf3mbz/image/upload/q_auto/v1/ghost-blog-images/chrome-extension-screenshot-3.png\" class=\"kg-image\" alt loading=\"lazy\"></figure><p>If you click on it, a new DevTool will open and in the console you will see:</p><figure class=\"kg-card kg-image-card\"><img src=\"https://res-4.cloudinary.com/hbqmf3mbz/image/upload/q_auto/v1/ghost-blog-images/chrome-extension-screenshot-4.png\" class=\"kg-image\" alt loading=\"lazy\"></figure><p>We can now confirm that our background script is working. Even if you close any other web page or reload any web page, nothing will change in the background script.</p><p>Let’s go back to our objective again. We now need to listen to messages in our background script, and on receiving a new message fetch the URL from the Pikachu API.</p><p>In order to listen to messages, we will use the following method in Chrome’s API:</p><!--kg-card-begin: html--><script src=\"https://gist.github.com/shahednasser/4426ca477880552e4c563be8264b968c.js\"></script><!--kg-card-end: html--><p>We need to pass a function to <code>addListener</code>. This function will be executed on receiving any new message. The function takes three parameters:</p><ol><li><strong><strong>message: </strong></strong>the message sent of type any.</li><li><strong><strong>sender: </strong></strong>of type <a href=\"https://developer.chrome.com/extensions/runtime#type-MessageSender\" rel=\"noopener\">MessageSender</a></li><li><strong><strong>senderResponse: </strong></strong>Callback function to call (at most once) when you have a response. This function is sent by the sender on sending the message.</li></ol><p>So this is what our <strong><strong>background.js </strong></strong>file should look like now:</p><!--kg-card-begin: html--><script src=\"https://gist.github.com/shahednasser/4920b314274d2695b5ac0e771a4cd52c.js\"></script><!--kg-card-end: html--><p>Now our background script is ready to receive messages, but there are no messages being sent.</p><p>As we said earlier, each time a page opens, our content script will loop through image elements in the web page, and send a message to the background script to fetch the Pikachu image link.</p><p>In order to send messages, we will need to use the following method from Chrome’s API:</p><!--kg-card-begin: html--><script src=\"https://gist.github.com/shahednasser/03e371015982ef271518922e56139c19.js\"></script><!--kg-card-end: html--><p>The parameter’s being sent are as follows:</p><ol><li><strong><strong>extensionId: (Optional) </strong></strong>the ID of the extension we are sending the message to. If it’s omitted, the message will be sent to our own extension.</li><li><strong><strong>message:</strong></strong> The message we are sending of type any.</li><li><strong><strong>options: Optional</strong></strong></li><li><strong><strong>responseCallback: </strong></strong>The function to call after receiving the message. This is the <strong><strong>senderResponse</strong></strong> parameter in the previous method we saw.</li></ol><p>So, we will use this method in our content script to send a message to the background script to fetch the Pikachu URL from the API. Our content script should now look like this:</p><!--kg-card-begin: html--><script src=\"https://gist.github.com/shahednasser/bd4051325006a5f3ca01e882b51eebc5.js\"></script><!--kg-card-end: html--><p>Here’s what we’re doing in the above code:</p><ol><li><strong><strong>Line 1: </strong></strong>get all image elements on the web page.</li><li><strong><strong>Line 2–6: </strong></strong>Loop through the image elements. On each element, we send a message to the background script. As you can tell, we are omitting the optional <strong><strong>extensionId </strong></strong>and <strong><strong>options</strong></strong> parameters. The first parameter is the <strong><strong>message </strong></strong>we are sending, and the second parameter is the callback function we want the background to execute after it fetches the image’s link.</li></ol><p>We are sending the object <strong><strong>{msg: ‘image’, index: i}</strong></strong> to our extension. This object has two properties. The first one is to specify what our message is about. In our extension, it might not be very helpful, but when you have an extension that uses different types of messages, it’s a good idea to differentiate what each message is for. The second property is to specify the index of the image the link is for in the <strong><strong>images</strong></strong> array. The reason behind this is that the callback function replacing the image will be called asynchronously, so <strong><strong>i </strong></strong>might point at another index at the time it’s being executed.</p><p>The callback function receives an object that has two properties. <strong><strong>data</strong></strong> will be the data received from the API call, and <strong><strong>index</strong></strong> will be the index of the image in the array <strong><strong>images</strong></strong>. Once it’s called, it will set the image at <strong><strong>index</strong></strong> in <strong><strong>images</strong></strong> to the link property is <strong><strong>data</strong></strong>.</p><p>Now, we need to fetch the data from the API in our background script whenever the content script sends a message. We can do this as follows:</p><!--kg-card-begin: html--><script src=\"https://gist.github.com/shahednasser/9c024a8b40396e7c6a6ae1250fb5d94d.js\"></script><!--kg-card-end: html--><p>Here’s what’s going on here:</p><ol><li><strong><strong>Line 1:</strong></strong> Listen to messages in our extension</li><li><strong><strong>Line 2: </strong></strong>Check if the message is to get the image URL. This will use the <strong><strong>msg </strong></strong>attribute we passed when we sent the message in <strong><strong>contentScript</strong></strong></li><li><strong><strong>Line 3–8:</strong></strong> If the condition in <strong><strong>Line 2</strong></strong> is true, fetch the data from the API. In the first <code>Promise</code> we get the response text, then in the second <code>Promise</code> we parse the JSON object and call <strong><strong>senderResponse</strong></strong> which is the callback function specified by the sender of the message. We pass with it an object with the parameters <strong><strong>data </strong></strong>(the data received through the API call) and <strong><strong>index</strong></strong> (the index of the image, sent by the sender).</li><li><strong><strong>Line 9: </strong></strong>simple handling of any error that might occur.</li><li><strong><strong>Line 10</strong></strong>: If the sender specifies a callback function, it has to be called before the listener stops executing or else it will throw an error. In our case, we have to wait until we receive a response from the API which will happen asynchronously, so we return true to specify that it will be called asynchronously.</li></ol><p>Okay, our scripts are ready. Only one thing left — we need to give permission to the API in our <strong><strong>manifest.json:</strong></strong></p><!--kg-card-begin: html--><script src=\"https://gist.github.com/shahednasser/8d6786f1583a91fe31635715caf44b0c.js\"></script><!--kg-card-end: html--><p>All you need to do now is reload the extension in chrome://extensions, then open any page and see Pikachu everywhere!</p><p>Of course, this extension will not replace background images. This can be done by looping through elements, checking if they have background images, and replacing them the same way.</p>","url":"https://backend.shahednasser.com/chrome-extension-tutorial-replace-images-in-any-website-with-pikachu/","canonical_url":null,"uuid":"d3c062c3-8b05-4b63-919c-4fbbcdb59cc3","codeinjection_foot":null,"codeinjection_head":null,"codeinjection_styles":null,"comment_id":"5fa596aabc1317001e675bff","reading_time":9,"send_email_when_published":null,"email_subject":null,"childHtmlRehype":{"html":"<p><em>This article was also published on <a href=\"https://levelup.gitconnected.com/chrome-extension-tutorial-replace-images-in-any-website-with-pikachu-de2a6e3548bb\">Level Up Coding's gitconnected</a></em></p><p>What’s a better way to learn how to make a Chrome extension than making the web cuter with Pikachu images?</p><p>In this tutorial, I will show you how you can create a simple Chrome extension that replaces images on websites with Pikachu images. We will discuss topics like content scripts, background scripts, and messages between scripts in Chrome extensions.</p><p>The code for this tutorial is available <a href=\"https://github.com/shahednasser/pikachu-everywhere\" rel=\"noopener\">here</a>.</p><p>First thing you need to do when creating any chrome extension is start with creating a <strong><strong>manifest.json </strong></strong>file in the root of the extension’s directory. For now, this file will hold only the following basic information about your extension:</p><!--kg-card-begin: html--><script src=\"https://gist.github.com/shahednasser/6beec3aa60a2187e99e85479f8806c30.js\"></script><!--kg-card-end: html--><p>Here is what everything in this file means:</p><ol><li><strong><strong>name:</strong></strong> The name of the extension. I named the extension “Pikachu Everywhere”</li><li><strong><strong>version</strong></strong>: This is the version of the extension. When you publish the extension in the Chrome Web Store and you need to update it, you will have to increase the version number so it’s good to start with a low number.</li><li><strong><strong>description: (Optional) </strong></strong>Description of your extension</li><li><strong><strong>manifest_version</strong></strong>: As of Chrome 18, developers are required to use manifest version 2, since manifest version 1 is now deprecated.</li><li><strong><strong>icons: (Optional) </strong></strong>Here we specify the extension’s icon. This icon will show up in your browser’s toolbar, the Chrome Web Store, and any other place that requires to show it. For that reason, it’s good to include different sizes of the icon. I have used an icon from <a href=\"https://www.freeiconspng.com/img/17360\" rel=\"noopener\">Free Icons PNG</a> and resized the icons using <a href=\"https://resizeimage.net/\" rel=\"noopener\">https://resizeimage.net/</a> (You can find the images in the <a href=\"https://github.com/shahednasser/pikachu-everywhere\" rel=\"noopener\">GitHub repository</a>). The paths specified for these images is relative to the root of the extension.</li></ol><p>With this, we have the core of any Chrome extension. Next, we’ll add the extension to the browser.</p><p>Go to chrome://extensions. There, you can toggle developer mode at the top right corner. Once you do that, a new toolbar will show up with three buttons: “Load unpacked,” “Pack extension,” and “Update.” Click on “Load unpacked” and choose your extension’s directory.</p><p>Once you do that, you will see that your new chrome extension is now working. Yes, it does nothing, but it’s working.</p><p>In Chrome extensions, there are two types of scripts you can use. There are Background scripts and Content scripts. Background scripts run (obviously) in the background. They are loaded when needed and unloaded when idle. They are used to listen to certain events in your Chrome extension.</p><p>Content scripts are scripts loaded once you open a website and injected it into it. Once you open a website, they will start running just like any other script on that website. After you close the website, the content scripts on that website will stop running as well.</p><p>For our extension, we want to replace every image on every website with Pikachu images. In order to do that we need to specify a content script that works every time you open a web page.</p><p>Let’s create our content script in <code class=\"language-text\">assets/js/contentScript.js</code> and include the following script to test whether our extension is working or not:</p><!--kg-card-begin: html--><script src=\"https://gist.github.com/shahednasser/342ba17b0b6d715adaafd334a14416f9.js\"></script><!--kg-card-end: html--><p>One other thing we need to do is tell Chrome which script do we need to run as a content script. Add the following lines to <strong><strong>manifest.json</strong></strong></p><figure class=\"kg-card kg-embed-card\"><iframe src=\"https://levelup.gitconnected.com/media/d35d7df4566f532c2a0948ea540ee302\" allowfullscreen frameborder=\"0\" height=\"0\" width=\"0\" title=\"adding content script to manifest.json\" class=\"t u v hm aj\" scrolling=\"auto\" style=\"box-sizing: inherit; position: absolute; top: 0px; left: 0px; width: 680px; height: 0px;\"></iframe></figure><p>Here we are using <code class=\"language-text\">content_scripts</code> as key with the value as an array. Here’s what everything in the array means:</p><ol><li><strong><strong>matches</strong></strong>: here you can specify which URLs the content script should run on. In our case we want our content script to run on all pages. So, we use the placeholder “&#x3C;all_urls>” to specify that.</li><li><strong><strong>all_frames</strong></strong>: this key is used to allow the extension to specify if JavaScript and CSS files should be injected into all frames matching the specified URL requirements or only into the topmost frame in a tab.</li><li><strong><strong>js: </strong></strong>here we specify the script we want to run as a content script. You can specify more than one script, so the value has to be an array</li></ol><p>Now we’re ready to test our content script.</p><p>But before we do that we need to do one small step. In order to inform chrome that we updated our chrome extension, we need to go back to chrome://extensions, go to our extension, and press the reload icon.</p><p>To test if our content script is working, go to any web page you want, reload the page if it was opened before, then right-click and press Inspect. In DevTools, open the Console tab. You will see in your console as expected:</p><figure class=\"kg-card kg-image-card kg-card-hascaption\"><img src=\"https://res-3.cloudinary.com/hbqmf3mbz/image/upload/q_auto/v1/ghost-blog-images/chrome-extension-screenshot-1.png\" class=\"kg-image\" alt=\"\" loading=\"lazy\"><figcaption>Console of a web page</figcaption></figure><p>So, we can verify that our content script is working. Try reloading the page, and the content script will execute again. Try opening another web page, and the content script will execute on that page independently.</p><p>Now, we know how to inject a script on any web page, and through that script we can do our work.</p><p>In our case, we need to replace every image in any website with random Pikachu images. To do that, we will use <a href=\"https://some-random-api.ml/img/pikachu\" rel=\"noopener\">this API</a> which will provide us with a JSON object containing the link of a Pikachu image. Each request to that API will look something like this:</p><figure class=\"kg-card kg-image-card kg-card-hascaption\"><img src=\"https://res-3.cloudinary.com/hbqmf3mbz/image/upload/q_auto/v1/ghost-blog-images/chrome-extension-screenshot-2.png\" class=\"kg-image\" alt=\"\" loading=\"lazy\"><figcaption>Result of API call</figcaption></figure><p>With the help of this API, the steps to achieve what we need will be as follows:</p><ol><li>Get all image elements on the web page.</li><li>Perform a GET request to the API to retrieve a random Pikachu image link.</li><li>Replace the image elements’ <code class=\"language-text\">src</code> attributes with the links we retrieve.</li></ol><p>This all sounds good and easy, but there is one complication: content scripts in Chrome extensions cannot perform Cross-Origin requests.</p><p>So how can we fetch the image URLs from the API? We will need to use a Background script.</p><p>As we specified earlier, Background scripts run in the background in Chrome. They are not dependent on any web page.</p><p>Scripts in Chrome extensions are isolated from each other. However, they share a mean of communication through the <a href=\"https://developer.chrome.com/extensions/api_index\" rel=\"noopener\">Chrome API</a>.</p><p>What we will do is that we will listen for a message in the <strong><strong>background</strong></strong> script sent from <strong><strong>content</strong></strong> scripts. Every time a web page opens, our content script will run, send a message to the background script requesting an image URL. The background script will then perform the asynchronous call to the Pikachu API, retrieve the link and send it back to the content script.</p><p>This may sound complicated at first, but it’s actually done in a very simple manner.</p><p>Let us first start by creating the background script and see it in action. Create <code class=\"language-text\">assets/js/background.js</code> with the following content:</p><!--kg-card-begin: html--><script src=\"https://gist.github.com/shahednasser/b45de97a11eda346d1ea427b88c406f4.js\"></script><!--kg-card-end: html--><p>Now, we need to tell Chrome that the file we just created will be our background script. In order to do that, we need to go back to our <strong><strong>manifest.json</strong></strong> and add the following:</p><!--kg-card-begin: html--><script src=\"https://gist.github.com/shahednasser/c7a26cf5b3b2415110cb97e44f2b3834.js\"></script><!--kg-card-end: html--><p>Here is what everything means:</p><ol><li><strong><strong>scripts: </strong></strong>An array that holds the paths of the scripts we will use as background scripts. In our case, it’s just one.</li><li><strong><strong>persistent</strong></strong>: should always be false unless the extension uses Chrome’s Web Request API to block or modify network requests.</li></ol><p>Now, Chrome will know about our background script. It will load it when it’s needed, and unload it when it becomes idle.</p><p>To test our background script, we need to reload our extension as we did earlier.</p><p>Once you do that, you will see a new link appear in your extension’s info box with the text “inspect views background page”:</p><figure class=\"kg-card kg-image-card\"><img src=\"https://res-5.cloudinary.com/hbqmf3mbz/image/upload/q_auto/v1/ghost-blog-images/chrome-extension-screenshot-3.png\" class=\"kg-image\" alt=\"\" loading=\"lazy\"></figure><p>If you click on it, a new DevTool will open and in the console you will see:</p><figure class=\"kg-card kg-image-card\"><img src=\"https://res-4.cloudinary.com/hbqmf3mbz/image/upload/q_auto/v1/ghost-blog-images/chrome-extension-screenshot-4.png\" class=\"kg-image\" alt=\"\" loading=\"lazy\"></figure><p>We can now confirm that our background script is working. Even if you close any other web page or reload any web page, nothing will change in the background script.</p><p>Let’s go back to our objective again. We now need to listen to messages in our background script, and on receiving a new message fetch the URL from the Pikachu API.</p><p>In order to listen to messages, we will use the following method in Chrome’s API:</p><!--kg-card-begin: html--><script src=\"https://gist.github.com/shahednasser/4426ca477880552e4c563be8264b968c.js\"></script><!--kg-card-end: html--><p>We need to pass a function to <code class=\"language-text\">addListener</code>. This function will be executed on receiving any new message. The function takes three parameters:</p><ol><li><strong><strong>message: </strong></strong>the message sent of type any.</li><li><strong><strong>sender: </strong></strong>of type <a href=\"https://developer.chrome.com/extensions/runtime#type-MessageSender\" rel=\"noopener\">MessageSender</a></li><li><strong><strong>senderResponse: </strong></strong>Callback function to call (at most once) when you have a response. This function is sent by the sender on sending the message.</li></ol><p>So this is what our <strong><strong>background.js </strong></strong>file should look like now:</p><!--kg-card-begin: html--><script src=\"https://gist.github.com/shahednasser/4920b314274d2695b5ac0e771a4cd52c.js\"></script><!--kg-card-end: html--><p>Now our background script is ready to receive messages, but there are no messages being sent.</p><p>As we said earlier, each time a page opens, our content script will loop through image elements in the web page, and send a message to the background script to fetch the Pikachu image link.</p><p>In order to send messages, we will need to use the following method from Chrome’s API:</p><!--kg-card-begin: html--><script src=\"https://gist.github.com/shahednasser/03e371015982ef271518922e56139c19.js\"></script><!--kg-card-end: html--><p>The parameter’s being sent are as follows:</p><ol><li><strong><strong>extensionId: (Optional) </strong></strong>the ID of the extension we are sending the message to. If it’s omitted, the message will be sent to our own extension.</li><li><strong><strong>message:</strong></strong> The message we are sending of type any.</li><li><strong><strong>options: Optional</strong></strong></li><li><strong><strong>responseCallback: </strong></strong>The function to call after receiving the message. This is the <strong><strong>senderResponse</strong></strong> parameter in the previous method we saw.</li></ol><p>So, we will use this method in our content script to send a message to the background script to fetch the Pikachu URL from the API. Our content script should now look like this:</p><!--kg-card-begin: html--><script src=\"https://gist.github.com/shahednasser/bd4051325006a5f3ca01e882b51eebc5.js\"></script><!--kg-card-end: html--><p>Here’s what we’re doing in the above code:</p><ol><li><strong><strong>Line 1: </strong></strong>get all image elements on the web page.</li><li><strong><strong>Line 2–6: </strong></strong>Loop through the image elements. On each element, we send a message to the background script. As you can tell, we are omitting the optional <strong><strong>extensionId </strong></strong>and <strong><strong>options</strong></strong> parameters. The first parameter is the <strong><strong>message </strong></strong>we are sending, and the second parameter is the callback function we want the background to execute after it fetches the image’s link.</li></ol><p>We are sending the object <strong><strong>{msg: ‘image’, index: i}</strong></strong> to our extension. This object has two properties. The first one is to specify what our message is about. In our extension, it might not be very helpful, but when you have an extension that uses different types of messages, it’s a good idea to differentiate what each message is for. The second property is to specify the index of the image the link is for in the <strong><strong>images</strong></strong> array. The reason behind this is that the callback function replacing the image will be called asynchronously, so <strong><strong>i </strong></strong>might point at another index at the time it’s being executed.</p><p>The callback function receives an object that has two properties. <strong><strong>data</strong></strong> will be the data received from the API call, and <strong><strong>index</strong></strong> will be the index of the image in the array <strong><strong>images</strong></strong>. Once it’s called, it will set the image at <strong><strong>index</strong></strong> in <strong><strong>images</strong></strong> to the link property is <strong><strong>data</strong></strong>.</p><p>Now, we need to fetch the data from the API in our background script whenever the content script sends a message. We can do this as follows:</p><!--kg-card-begin: html--><script src=\"https://gist.github.com/shahednasser/9c024a8b40396e7c6a6ae1250fb5d94d.js\"></script><!--kg-card-end: html--><p>Here’s what’s going on here:</p><ol><li><strong><strong>Line 1:</strong></strong> Listen to messages in our extension</li><li><strong><strong>Line 2: </strong></strong>Check if the message is to get the image URL. This will use the <strong><strong>msg </strong></strong>attribute we passed when we sent the message in <strong><strong>contentScript</strong></strong></li><li><strong><strong>Line 3–8:</strong></strong> If the condition in <strong><strong>Line 2</strong></strong> is true, fetch the data from the API. In the first <code class=\"language-text\">Promise</code> we get the response text, then in the second <code class=\"language-text\">Promise</code> we parse the JSON object and call <strong><strong>senderResponse</strong></strong> which is the callback function specified by the sender of the message. We pass with it an object with the parameters <strong><strong>data </strong></strong>(the data received through the API call) and <strong><strong>index</strong></strong> (the index of the image, sent by the sender).</li><li><strong><strong>Line 9: </strong></strong>simple handling of any error that might occur.</li><li><strong><strong>Line 10</strong></strong>: If the sender specifies a callback function, it has to be called before the listener stops executing or else it will throw an error. In our case, we have to wait until we receive a response from the API which will happen asynchronously, so we return true to specify that it will be called asynchronously.</li></ol><p>Okay, our scripts are ready. Only one thing left — we need to give permission to the API in our <strong><strong>manifest.json:</strong></strong></p><!--kg-card-begin: html--><script src=\"https://gist.github.com/shahednasser/8d6786f1583a91fe31635715caf44b0c.js\"></script><!--kg-card-end: html--><p>All you need to do now is reload the extension in chrome://extensions, then open any page and see Pikachu everywhere!</p><p>Of course, this extension will not replace background images. This can be done by looping through elements, checking if they have background images, and replacing them the same way.</p>","htmlAst":{"type":"root","children":[{"type":"element","tagName":"p","properties":{},"children":[{"type":"element","tagName":"em","properties":{},"children":[{"type":"text","value":"This article was also published on "},{"type":"element","tagName":"a","properties":{"href":"https://levelup.gitconnected.com/chrome-extension-tutorial-replace-images-in-any-website-with-pikachu-de2a6e3548bb"},"children":[{"type":"text","value":"Level Up Coding's gitconnected"}]}]}]},{"type":"element","tagName":"p","properties":{},"children":[{"type":"text","value":"What’s a better way to learn how to make a Chrome extension than making the web cuter with Pikachu images?"}]},{"type":"element","tagName":"p","properties":{},"children":[{"type":"text","value":"In this tutorial, I will show you how you can create a simple Chrome extension that replaces images on websites with Pikachu images. We will discuss topics like content scripts, background scripts, and messages between scripts in Chrome extensions."}]},{"type":"element","tagName":"p","properties":{},"children":[{"type":"text","value":"The code for this tutorial is available "},{"type":"element","tagName":"a","properties":{"href":"https://github.com/shahednasser/pikachu-everywhere","rel":["noopener"]},"children":[{"type":"text","value":"here"}]},{"type":"text","value":"."}]},{"type":"element","tagName":"p","properties":{},"children":[{"type":"text","value":"First thing you need to do when creating any chrome extension is start with creating a "},{"type":"element","tagName":"strong","properties":{},"children":[{"type":"element","tagName":"strong","properties":{},"children":[{"type":"text","value":"manifest.json "}]}]},{"type":"text","value":"file in the root of the extension’s directory. For now, this file will hold only the following basic information about your extension:"}]},{"type":"comment","value":"kg-card-begin: html"},{"type":"element","tagName":"script","properties":{"src":"https://gist.github.com/shahednasser/6beec3aa60a2187e99e85479f8806c30.js"},"children":[]},{"type":"comment","value":"kg-card-end: html"},{"type":"element","tagName":"p","properties":{},"children":[{"type":"text","value":"Here is what everything in this file means:"}]},{"type":"element","tagName":"ol","properties":{},"children":[{"type":"element","tagName":"li","properties":{},"children":[{"type":"element","tagName":"strong","properties":{},"children":[{"type":"element","tagName":"strong","properties":{},"children":[{"type":"text","value":"name:"}]}]},{"type":"text","value":" The name of the extension. I named the extension “Pikachu Everywhere”"}]},{"type":"element","tagName":"li","properties":{},"children":[{"type":"element","tagName":"strong","properties":{},"children":[{"type":"element","tagName":"strong","properties":{},"children":[{"type":"text","value":"version"}]}]},{"type":"text","value":": This is the version of the extension. When you publish the extension in the Chrome Web Store and you need to update it, you will have to increase the version number so it’s good to start with a low number."}]},{"type":"element","tagName":"li","properties":{},"children":[{"type":"element","tagName":"strong","properties":{},"children":[{"type":"element","tagName":"strong","properties":{},"children":[{"type":"text","value":"description: (Optional) "}]}]},{"type":"text","value":"Description of your extension"}]},{"type":"element","tagName":"li","properties":{},"children":[{"type":"element","tagName":"strong","properties":{},"children":[{"type":"element","tagName":"strong","properties":{},"children":[{"type":"text","value":"manifest_version"}]}]},{"type":"text","value":": As of Chrome 18, developers are required to use manifest version 2, since manifest version 1 is now deprecated."}]},{"type":"element","tagName":"li","properties":{},"children":[{"type":"element","tagName":"strong","properties":{},"children":[{"type":"element","tagName":"strong","properties":{},"children":[{"type":"text","value":"icons: (Optional) "}]}]},{"type":"text","value":"Here we specify the extension’s icon. This icon will show up in your browser’s toolbar, the Chrome Web Store, and any other place that requires to show it. For that reason, it’s good to include different sizes of the icon. I have used an icon from "},{"type":"element","tagName":"a","properties":{"href":"https://www.freeiconspng.com/img/17360","rel":["noopener"]},"children":[{"type":"text","value":"Free Icons PNG"}]},{"type":"text","value":" and resized the icons using "},{"type":"element","tagName":"a","properties":{"href":"https://resizeimage.net/","rel":["noopener"]},"children":[{"type":"text","value":"https://resizeimage.net/"}]},{"type":"text","value":" (You can find the images in the "},{"type":"element","tagName":"a","properties":{"href":"https://github.com/shahednasser/pikachu-everywhere","rel":["noopener"]},"children":[{"type":"text","value":"GitHub repository"}]},{"type":"text","value":"). The paths specified for these images is relative to the root of the extension."}]}]},{"type":"element","tagName":"p","properties":{},"children":[{"type":"text","value":"With this, we have the core of any Chrome extension. Next, we’ll add the extension to the browser."}]},{"type":"element","tagName":"p","properties":{},"children":[{"type":"text","value":"Go to chrome://extensions. There, you can toggle developer mode at the top right corner. Once you do that, a new toolbar will show up with three buttons: “Load unpacked,” “Pack extension,” and “Update.” Click on “Load unpacked” and choose your extension’s directory."}]},{"type":"element","tagName":"p","properties":{},"children":[{"type":"text","value":"Once you do that, you will see that your new chrome extension is now working. Yes, it does nothing, but it’s working."}]},{"type":"element","tagName":"p","properties":{},"children":[{"type":"text","value":"In Chrome extensions, there are two types of scripts you can use. There are Background scripts and Content scripts. Background scripts run (obviously) in the background. They are loaded when needed and unloaded when idle. They are used to listen to certain events in your Chrome extension."}]},{"type":"element","tagName":"p","properties":{},"children":[{"type":"text","value":"Content scripts are scripts loaded once you open a website and injected it into it. Once you open a website, they will start running just like any other script on that website. After you close the website, the content scripts on that website will stop running as well."}]},{"type":"element","tagName":"p","properties":{},"children":[{"type":"text","value":"For our extension, we want to replace every image on every website with Pikachu images. In order to do that we need to specify a content script that works every time you open a web page."}]},{"type":"element","tagName":"p","properties":{},"children":[{"type":"text","value":"Let’s create our content script in "},{"type":"element","tagName":"code","properties":{"className":["language-text"]},"children":[{"type":"text","value":"assets/js/contentScript.js"}]},{"type":"text","value":" and include the following script to test whether our extension is working or not:"}]},{"type":"comment","value":"kg-card-begin: html"},{"type":"element","tagName":"script","properties":{"src":"https://gist.github.com/shahednasser/342ba17b0b6d715adaafd334a14416f9.js"},"children":[]},{"type":"comment","value":"kg-card-end: html"},{"type":"element","tagName":"p","properties":{},"children":[{"type":"text","value":"One other thing we need to do is tell Chrome which script do we need to run as a content script. Add the following lines to "},{"type":"element","tagName":"strong","properties":{},"children":[{"type":"element","tagName":"strong","properties":{},"children":[{"type":"text","value":"manifest.json"}]}]}]},{"type":"element","tagName":"figure","properties":{"className":["kg-card","kg-embed-card"]},"children":[{"type":"element","tagName":"iframe","properties":{"src":"https://levelup.gitconnected.com/media/d35d7df4566f532c2a0948ea540ee302","allowFullScreen":true,"frameBorder":"0","height":0,"width":0,"title":"adding content script to manifest.json","className":["t","u","v","hm","aj"],"scrolling":"auto","style":"box-sizing: inherit; position: absolute; top: 0px; left: 0px; width: 680px; height: 0px;"},"children":[]}]},{"type":"element","tagName":"p","properties":{},"children":[{"type":"text","value":"Here we are using "},{"type":"element","tagName":"code","properties":{"className":["language-text"]},"children":[{"type":"text","value":"content_scripts"}]},{"type":"text","value":" as key with the value as an array. Here’s what everything in the array means:"}]},{"type":"element","tagName":"ol","properties":{},"children":[{"type":"element","tagName":"li","properties":{},"children":[{"type":"element","tagName":"strong","properties":{},"children":[{"type":"element","tagName":"strong","properties":{},"children":[{"type":"text","value":"matches"}]}]},{"type":"text","value":": here you can specify which URLs the content script should run on. In our case we want our content script to run on all pages. So, we use the placeholder “<all_urls>” to specify that."}]},{"type":"element","tagName":"li","properties":{},"children":[{"type":"element","tagName":"strong","properties":{},"children":[{"type":"element","tagName":"strong","properties":{},"children":[{"type":"text","value":"all_frames"}]}]},{"type":"text","value":": this key is used to allow the extension to specify if JavaScript and CSS files should be injected into all frames matching the specified URL requirements or only into the topmost frame in a tab."}]},{"type":"element","tagName":"li","properties":{},"children":[{"type":"element","tagName":"strong","properties":{},"children":[{"type":"element","tagName":"strong","properties":{},"children":[{"type":"text","value":"js: "}]}]},{"type":"text","value":"here we specify the script we want to run as a content script. You can specify more than one script, so the value has to be an array"}]}]},{"type":"element","tagName":"p","properties":{},"children":[{"type":"text","value":"Now we’re ready to test our content script."}]},{"type":"element","tagName":"p","properties":{},"children":[{"type":"text","value":"But before we do that we need to do one small step. In order to inform chrome that we updated our chrome extension, we need to go back to chrome://extensions, go to our extension, and press the reload icon."}]},{"type":"element","tagName":"p","properties":{},"children":[{"type":"text","value":"To test if our content script is working, go to any web page you want, reload the page if it was opened before, then right-click and press Inspect. In DevTools, open the Console tab. You will see in your console as expected:"}]},{"type":"element","tagName":"figure","properties":{"className":["kg-card","kg-image-card","kg-card-hascaption"]},"children":[{"type":"element","tagName":"img","properties":{"src":"https://res-3.cloudinary.com/hbqmf3mbz/image/upload/q_auto/v1/ghost-blog-images/chrome-extension-screenshot-1.png","className":["kg-image"],"alt":"","loading":"lazy"},"children":[]},{"type":"element","tagName":"figcaption","properties":{},"children":[{"type":"text","value":"Console of a web page"}]}]},{"type":"element","tagName":"p","properties":{},"children":[{"type":"text","value":"So, we can verify that our content script is working. Try reloading the page, and the content script will execute again. Try opening another web page, and the content script will execute on that page independently."}]},{"type":"element","tagName":"p","properties":{},"children":[{"type":"text","value":"Now, we know how to inject a script on any web page, and through that script we can do our work."}]},{"type":"element","tagName":"p","properties":{},"children":[{"type":"text","value":"In our case, we need to replace every image in any website with random Pikachu images. To do that, we will use "},{"type":"element","tagName":"a","properties":{"href":"https://some-random-api.ml/img/pikachu","rel":["noopener"]},"children":[{"type":"text","value":"this API"}]},{"type":"text","value":" which will provide us with a JSON object containing the link of a Pikachu image. Each request to that API will look something like this:"}]},{"type":"element","tagName":"figure","properties":{"className":["kg-card","kg-image-card","kg-card-hascaption"]},"children":[{"type":"element","tagName":"img","properties":{"src":"https://res-3.cloudinary.com/hbqmf3mbz/image/upload/q_auto/v1/ghost-blog-images/chrome-extension-screenshot-2.png","className":["kg-image"],"alt":"","loading":"lazy"},"children":[]},{"type":"element","tagName":"figcaption","properties":{},"children":[{"type":"text","value":"Result of API call"}]}]},{"type":"element","tagName":"p","properties":{},"children":[{"type":"text","value":"With the help of this API, the steps to achieve what we need will be as follows:"}]},{"type":"element","tagName":"ol","properties":{},"children":[{"type":"element","tagName":"li","properties":{},"children":[{"type":"text","value":"Get all image elements on the web page."}]},{"type":"element","tagName":"li","properties":{},"children":[{"type":"text","value":"Perform a GET request to the API to retrieve a random Pikachu image link."}]},{"type":"element","tagName":"li","properties":{},"children":[{"type":"text","value":"Replace the image elements’ "},{"type":"element","tagName":"code","properties":{"className":["language-text"]},"children":[{"type":"text","value":"src"}]},{"type":"text","value":" attributes with the links we retrieve."}]}]},{"type":"element","tagName":"p","properties":{},"children":[{"type":"text","value":"This all sounds good and easy, but there is one complication: content scripts in Chrome extensions cannot perform Cross-Origin requests."}]},{"type":"element","tagName":"p","properties":{},"children":[{"type":"text","value":"So how can we fetch the image URLs from the API? We will need to use a Background script."}]},{"type":"element","tagName":"p","properties":{},"children":[{"type":"text","value":"As we specified earlier, Background scripts run in the background in Chrome. They are not dependent on any web page."}]},{"type":"element","tagName":"p","properties":{},"children":[{"type":"text","value":"Scripts in Chrome extensions are isolated from each other. However, they share a mean of communication through the "},{"type":"element","tagName":"a","properties":{"href":"https://developer.chrome.com/extensions/api_index","rel":["noopener"]},"children":[{"type":"text","value":"Chrome API"}]},{"type":"text","value":"."}]},{"type":"element","tagName":"p","properties":{},"children":[{"type":"text","value":"What we will do is that we will listen for a message in the "},{"type":"element","tagName":"strong","properties":{},"children":[{"type":"element","tagName":"strong","properties":{},"children":[{"type":"text","value":"background"}]}]},{"type":"text","value":" script sent from "},{"type":"element","tagName":"strong","properties":{},"children":[{"type":"element","tagName":"strong","properties":{},"children":[{"type":"text","value":"content"}]}]},{"type":"text","value":" scripts. Every time a web page opens, our content script will run, send a message to the background script requesting an image URL. The background script will then perform the asynchronous call to the Pikachu API, retrieve the link and send it back to the content script."}]},{"type":"element","tagName":"p","properties":{},"children":[{"type":"text","value":"This may sound complicated at first, but it’s actually done in a very simple manner."}]},{"type":"element","tagName":"p","properties":{},"children":[{"type":"text","value":"Let us first start by creating the background script and see it in action. Create "},{"type":"element","tagName":"code","properties":{"className":["language-text"]},"children":[{"type":"text","value":"assets/js/background.js"}]},{"type":"text","value":" with the following content:"}]},{"type":"comment","value":"kg-card-begin: html"},{"type":"element","tagName":"script","properties":{"src":"https://gist.github.com/shahednasser/b45de97a11eda346d1ea427b88c406f4.js"},"children":[]},{"type":"comment","value":"kg-card-end: html"},{"type":"element","tagName":"p","properties":{},"children":[{"type":"text","value":"Now, we need to tell Chrome that the file we just created will be our background script. In order to do that, we need to go back to our "},{"type":"element","tagName":"strong","properties":{},"children":[{"type":"element","tagName":"strong","properties":{},"children":[{"type":"text","value":"manifest.json"}]}]},{"type":"text","value":" and add the following:"}]},{"type":"comment","value":"kg-card-begin: html"},{"type":"element","tagName":"script","properties":{"src":"https://gist.github.com/shahednasser/c7a26cf5b3b2415110cb97e44f2b3834.js"},"children":[]},{"type":"comment","value":"kg-card-end: html"},{"type":"element","tagName":"p","properties":{},"children":[{"type":"text","value":"Here is what everything means:"}]},{"type":"element","tagName":"ol","properties":{},"children":[{"type":"element","tagName":"li","properties":{},"children":[{"type":"element","tagName":"strong","properties":{},"children":[{"type":"element","tagName":"strong","properties":{},"children":[{"type":"text","value":"scripts: "}]}]},{"type":"text","value":"An array that holds the paths of the scripts we will use as background scripts. In our case, it’s just one."}]},{"type":"element","tagName":"li","properties":{},"children":[{"type":"element","tagName":"strong","properties":{},"children":[{"type":"element","tagName":"strong","properties":{},"children":[{"type":"text","value":"persistent"}]}]},{"type":"text","value":": should always be false unless the extension uses Chrome’s Web Request API to block or modify network requests."}]}]},{"type":"element","tagName":"p","properties":{},"children":[{"type":"text","value":"Now, Chrome will know about our background script. It will load it when it’s needed, and unload it when it becomes idle."}]},{"type":"element","tagName":"p","properties":{},"children":[{"type":"text","value":"To test our background script, we need to reload our extension as we did earlier."}]},{"type":"element","tagName":"p","properties":{},"children":[{"type":"text","value":"Once you do that, you will see a new link appear in your extension’s info box with the text “inspect views background page”:"}]},{"type":"element","tagName":"figure","properties":{"className":["kg-card","kg-image-card"]},"children":[{"type":"element","tagName":"img","properties":{"src":"https://res-5.cloudinary.com/hbqmf3mbz/image/upload/q_auto/v1/ghost-blog-images/chrome-extension-screenshot-3.png","className":["kg-image"],"alt":"","loading":"lazy"},"children":[]}]},{"type":"element","tagName":"p","properties":{},"children":[{"type":"text","value":"If you click on it, a new DevTool will open and in the console you will see:"}]},{"type":"element","tagName":"figure","properties":{"className":["kg-card","kg-image-card"]},"children":[{"type":"element","tagName":"img","properties":{"src":"https://res-4.cloudinary.com/hbqmf3mbz/image/upload/q_auto/v1/ghost-blog-images/chrome-extension-screenshot-4.png","className":["kg-image"],"alt":"","loading":"lazy"},"children":[]}]},{"type":"element","tagName":"p","properties":{},"children":[{"type":"text","value":"We can now confirm that our background script is working. Even if you close any other web page or reload any web page, nothing will change in the background script."}]},{"type":"element","tagName":"p","properties":{},"children":[{"type":"text","value":"Let’s go back to our objective again. We now need to listen to messages in our background script, and on receiving a new message fetch the URL from the Pikachu API."}]},{"type":"element","tagName":"p","properties":{},"children":[{"type":"text","value":"In order to listen to messages, we will use the following method in Chrome’s API:"}]},{"type":"comment","value":"kg-card-begin: html"},{"type":"element","tagName":"script","properties":{"src":"https://gist.github.com/shahednasser/4426ca477880552e4c563be8264b968c.js"},"children":[]},{"type":"comment","value":"kg-card-end: html"},{"type":"element","tagName":"p","properties":{},"children":[{"type":"text","value":"We need to pass a function to "},{"type":"element","tagName":"code","properties":{"className":["language-text"]},"children":[{"type":"text","value":"addListener"}]},{"type":"text","value":". This function will be executed on receiving any new message. The function takes three parameters:"}]},{"type":"element","tagName":"ol","properties":{},"children":[{"type":"element","tagName":"li","properties":{},"children":[{"type":"element","tagName":"strong","properties":{},"children":[{"type":"element","tagName":"strong","properties":{},"children":[{"type":"text","value":"message: "}]}]},{"type":"text","value":"the message sent of type any."}]},{"type":"element","tagName":"li","properties":{},"children":[{"type":"element","tagName":"strong","properties":{},"children":[{"type":"element","tagName":"strong","properties":{},"children":[{"type":"text","value":"sender: "}]}]},{"type":"text","value":"of type "},{"type":"element","tagName":"a","properties":{"href":"https://developer.chrome.com/extensions/runtime#type-MessageSender","rel":["noopener"]},"children":[{"type":"text","value":"MessageSender"}]}]},{"type":"element","tagName":"li","properties":{},"children":[{"type":"element","tagName":"strong","properties":{},"children":[{"type":"element","tagName":"strong","properties":{},"children":[{"type":"text","value":"senderResponse: "}]}]},{"type":"text","value":"Callback function to call (at most once) when you have a response. This function is sent by the sender on sending the message."}]}]},{"type":"element","tagName":"p","properties":{},"children":[{"type":"text","value":"So this is what our "},{"type":"element","tagName":"strong","properties":{},"children":[{"type":"element","tagName":"strong","properties":{},"children":[{"type":"text","value":"background.js "}]}]},{"type":"text","value":"file should look like now:"}]},{"type":"comment","value":"kg-card-begin: html"},{"type":"element","tagName":"script","properties":{"src":"https://gist.github.com/shahednasser/4920b314274d2695b5ac0e771a4cd52c.js"},"children":[]},{"type":"comment","value":"kg-card-end: html"},{"type":"element","tagName":"p","properties":{},"children":[{"type":"text","value":"Now our background script is ready to receive messages, but there are no messages being sent."}]},{"type":"element","tagName":"p","properties":{},"children":[{"type":"text","value":"As we said earlier, each time a page opens, our content script will loop through image elements in the web page, and send a message to the background script to fetch the Pikachu image link."}]},{"type":"element","tagName":"p","properties":{},"children":[{"type":"text","value":"In order to send messages, we will need to use the following method from Chrome’s API:"}]},{"type":"comment","value":"kg-card-begin: html"},{"type":"element","tagName":"script","properties":{"src":"https://gist.github.com/shahednasser/03e371015982ef271518922e56139c19.js"},"children":[]},{"type":"comment","value":"kg-card-end: html"},{"type":"element","tagName":"p","properties":{},"children":[{"type":"text","value":"The parameter’s being sent are as follows:"}]},{"type":"element","tagName":"ol","properties":{},"children":[{"type":"element","tagName":"li","properties":{},"children":[{"type":"element","tagName":"strong","properties":{},"children":[{"type":"element","tagName":"strong","properties":{},"children":[{"type":"text","value":"extensionId: (Optional) "}]}]},{"type":"text","value":"the ID of the extension we are sending the message to. If it’s omitted, the message will be sent to our own extension."}]},{"type":"element","tagName":"li","properties":{},"children":[{"type":"element","tagName":"strong","properties":{},"children":[{"type":"element","tagName":"strong","properties":{},"children":[{"type":"text","value":"message:"}]}]},{"type":"text","value":" The message we are sending of type any."}]},{"type":"element","tagName":"li","properties":{},"children":[{"type":"element","tagName":"strong","properties":{},"children":[{"type":"element","tagName":"strong","properties":{},"children":[{"type":"text","value":"options: Optional"}]}]}]},{"type":"element","tagName":"li","properties":{},"children":[{"type":"element","tagName":"strong","properties":{},"children":[{"type":"element","tagName":"strong","properties":{},"children":[{"type":"text","value":"responseCallback: "}]}]},{"type":"text","value":"The function to call after receiving the message. This is the "},{"type":"element","tagName":"strong","properties":{},"children":[{"type":"element","tagName":"strong","properties":{},"children":[{"type":"text","value":"senderResponse"}]}]},{"type":"text","value":" parameter in the previous method we saw."}]}]},{"type":"element","tagName":"p","properties":{},"children":[{"type":"text","value":"So, we will use this method in our content script to send a message to the background script to fetch the Pikachu URL from the API. Our content script should now look like this:"}]},{"type":"comment","value":"kg-card-begin: html"},{"type":"element","tagName":"script","properties":{"src":"https://gist.github.com/shahednasser/bd4051325006a5f3ca01e882b51eebc5.js"},"children":[]},{"type":"comment","value":"kg-card-end: html"},{"type":"element","tagName":"p","properties":{},"children":[{"type":"text","value":"Here’s what we’re doing in the above code:"}]},{"type":"element","tagName":"ol","properties":{},"children":[{"type":"element","tagName":"li","properties":{},"children":[{"type":"element","tagName":"strong","properties":{},"children":[{"type":"element","tagName":"strong","properties":{},"children":[{"type":"text","value":"Line 1: "}]}]},{"type":"text","value":"get all image elements on the web page."}]},{"type":"element","tagName":"li","properties":{},"children":[{"type":"element","tagName":"strong","properties":{},"children":[{"type":"element","tagName":"strong","properties":{},"children":[{"type":"text","value":"Line 2–6: "}]}]},{"type":"text","value":"Loop through the image elements. On each element, we send a message to the background script. As you can tell, we are omitting the optional "},{"type":"element","tagName":"strong","properties":{},"children":[{"type":"element","tagName":"strong","properties":{},"children":[{"type":"text","value":"extensionId "}]}]},{"type":"text","value":"and "},{"type":"element","tagName":"strong","properties":{},"children":[{"type":"element","tagName":"strong","properties":{},"children":[{"type":"text","value":"options"}]}]},{"type":"text","value":" parameters. The first parameter is the "},{"type":"element","tagName":"strong","properties":{},"children":[{"type":"element","tagName":"strong","properties":{},"children":[{"type":"text","value":"message "}]}]},{"type":"text","value":"we are sending, and the second parameter is the callback function we want the background to execute after it fetches the image’s link."}]}]},{"type":"element","tagName":"p","properties":{},"children":[{"type":"text","value":"We are sending the object "},{"type":"element","tagName":"strong","properties":{},"children":[{"type":"element","tagName":"strong","properties":{},"children":[{"type":"text","value":"{msg: ‘image’, index: i}"}]}]},{"type":"text","value":" to our extension. This object has two properties. The first one is to specify what our message is about. In our extension, it might not be very helpful, but when you have an extension that uses different types of messages, it’s a good idea to differentiate what each message is for. The second property is to specify the index of the image the link is for in the "},{"type":"element","tagName":"strong","properties":{},"children":[{"type":"element","tagName":"strong","properties":{},"children":[{"type":"text","value":"images"}]}]},{"type":"text","value":" array. The reason behind this is that the callback function replacing the image will be called asynchronously, so "},{"type":"element","tagName":"strong","properties":{},"children":[{"type":"element","tagName":"strong","properties":{},"children":[{"type":"text","value":"i "}]}]},{"type":"text","value":"might point at another index at the time it’s being executed."}]},{"type":"element","tagName":"p","properties":{},"children":[{"type":"text","value":"The callback function receives an object that has two properties. "},{"type":"element","tagName":"strong","properties":{},"children":[{"type":"element","tagName":"strong","properties":{},"children":[{"type":"text","value":"data"}]}]},{"type":"text","value":" will be the data received from the API call, and "},{"type":"element","tagName":"strong","properties":{},"children":[{"type":"element","tagName":"strong","properties":{},"children":[{"type":"text","value":"index"}]}]},{"type":"text","value":" will be the index of the image in the array "},{"type":"element","tagName":"strong","properties":{},"children":[{"type":"element","tagName":"strong","properties":{},"children":[{"type":"text","value":"images"}]}]},{"type":"text","value":". Once it’s called, it will set the image at "},{"type":"element","tagName":"strong","properties":{},"children":[{"type":"element","tagName":"strong","properties":{},"children":[{"type":"text","value":"index"}]}]},{"type":"text","value":" in "},{"type":"element","tagName":"strong","properties":{},"children":[{"type":"element","tagName":"strong","properties":{},"children":[{"type":"text","value":"images"}]}]},{"type":"text","value":" to the link property is "},{"type":"element","tagName":"strong","properties":{},"children":[{"type":"element","tagName":"strong","properties":{},"children":[{"type":"text","value":"data"}]}]},{"type":"text","value":"."}]},{"type":"element","tagName":"p","properties":{},"children":[{"type":"text","value":"Now, we need to fetch the data from the API in our background script whenever the content script sends a message. We can do this as follows:"}]},{"type":"comment","value":"kg-card-begin: html"},{"type":"element","tagName":"script","properties":{"src":"https://gist.github.com/shahednasser/9c024a8b40396e7c6a6ae1250fb5d94d.js"},"children":[]},{"type":"comment","value":"kg-card-end: html"},{"type":"element","tagName":"p","properties":{},"children":[{"type":"text","value":"Here’s what’s going on here:"}]},{"type":"element","tagName":"ol","properties":{},"children":[{"type":"element","tagName":"li","properties":{},"children":[{"type":"element","tagName":"strong","properties":{},"children":[{"type":"element","tagName":"strong","properties":{},"children":[{"type":"text","value":"Line 1:"}]}]},{"type":"text","value":" Listen to messages in our extension"}]},{"type":"element","tagName":"li","properties":{},"children":[{"type":"element","tagName":"strong","properties":{},"children":[{"type":"element","tagName":"strong","properties":{},"children":[{"type":"text","value":"Line 2: "}]}]},{"type":"text","value":"Check if the message is to get the image URL. This will use the "},{"type":"element","tagName":"strong","properties":{},"children":[{"type":"element","tagName":"strong","properties":{},"children":[{"type":"text","value":"msg "}]}]},{"type":"text","value":"attribute we passed when we sent the message in "},{"type":"element","tagName":"strong","properties":{},"children":[{"type":"element","tagName":"strong","properties":{},"children":[{"type":"text","value":"contentScript"}]}]}]},{"type":"element","tagName":"li","properties":{},"children":[{"type":"element","tagName":"strong","properties":{},"children":[{"type":"element","tagName":"strong","properties":{},"children":[{"type":"text","value":"Line 3–8:"}]}]},{"type":"text","value":" If the condition in "},{"type":"element","tagName":"strong","properties":{},"children":[{"type":"element","tagName":"strong","properties":{},"children":[{"type":"text","value":"Line 2"}]}]},{"type":"text","value":" is true, fetch the data from the API. In the first "},{"type":"element","tagName":"code","properties":{"className":["language-text"]},"children":[{"type":"text","value":"Promise"}]},{"type":"text","value":" we get the response text, then in the second "},{"type":"element","tagName":"code","properties":{"className":["language-text"]},"children":[{"type":"text","value":"Promise"}]},{"type":"text","value":" we parse the JSON object and call "},{"type":"element","tagName":"strong","properties":{},"children":[{"type":"element","tagName":"strong","properties":{},"children":[{"type":"text","value":"senderResponse"}]}]},{"type":"text","value":" which is the callback function specified by the sender of the message. We pass with it an object with the parameters "},{"type":"element","tagName":"strong","properties":{},"children":[{"type":"element","tagName":"strong","properties":{},"children":[{"type":"text","value":"data "}]}]},{"type":"text","value":"(the data received through the API call) and "},{"type":"element","tagName":"strong","properties":{},"children":[{"type":"element","tagName":"strong","properties":{},"children":[{"type":"text","value":"index"}]}]},{"type":"text","value":" (the index of the image, sent by the sender)."}]},{"type":"element","tagName":"li","properties":{},"children":[{"type":"element","tagName":"strong","properties":{},"children":[{"type":"element","tagName":"strong","properties":{},"children":[{"type":"text","value":"Line 9: "}]}]},{"type":"text","value":"simple handling of any error that might occur."}]},{"type":"element","tagName":"li","properties":{},"children":[{"type":"element","tagName":"strong","properties":{},"children":[{"type":"element","tagName":"strong","properties":{},"children":[{"type":"text","value":"Line 10"}]}]},{"type":"text","value":": If the sender specifies a callback function, it has to be called before the listener stops executing or else it will throw an error. In our case, we have to wait until we receive a response from the API which will happen asynchronously, so we return true to specify that it will be called asynchronously."}]}]},{"type":"element","tagName":"p","properties":{},"children":[{"type":"text","value":"Okay, our scripts are ready. Only one thing left — we need to give permission to the API in our "},{"type":"element","tagName":"strong","properties":{},"children":[{"type":"element","tagName":"strong","properties":{},"children":[{"type":"text","value":"manifest.json:"}]}]}]},{"type":"comment","value":"kg-card-begin: html"},{"type":"element","tagName":"script","properties":{"src":"https://gist.github.com/shahednasser/8d6786f1583a91fe31635715caf44b0c.js"},"children":[]},{"type":"comment","value":"kg-card-end: html"},{"type":"element","tagName":"p","properties":{},"children":[{"type":"text","value":"All you need to do now is reload the extension in chrome://extensions, then open any page and see Pikachu everywhere!"}]},{"type":"element","tagName":"p","properties":{},"children":[{"type":"text","value":"Of course, this extension will not replace background images. This can be done by looping through elements, checking if they have background images, and replacing them the same way."}]}],"data":{"quirksMode":false}},"tableOfContents":[]},"featureImageSharp":{"base":"chrome-extension.gif","publicURL":"/static/498b0f8235a6b5cc5d72fd2244922304/chrome-extension.gif","imageMeta":{"width":500,"height":309},"childImageSharp":null}},"prev":{"id":"Ghost__Post__6127ba1b3ed159214d382e72","title":"React Hooks Tutorial — Create a Number Trivia Generator Website","slug":"react-hooks-tutorial-create-a-number-trivia-generator-website","featured":false,"feature_image":"https://res-1.cloudinary.com/hbqmf3mbz/image/upload/q_auto/v1/ghost-blog-images/1_-Ijet6kVJqGgul6adezDLQ.png","excerpt":"In this tutorial, you’ll learn how to exactly use hooks in React. We will build a simple website that generates number trivia.","custom_excerpt":"In this tutorial, you’ll learn how to exactly use hooks in React. We will build a simple website that generates number trivia.","visibility":"public","created_at_pretty":"10 Nov 2020","published_at_pretty":"16 Apr 2019","updated_at_pretty":"10 Nov 2020","created_at":"2020-11-10T07:34:40.000+00:00","published_at":"2019-04-16T07:34:00.000+00:00","updated_at":"2020-11-10T08:20:26.000+00:00","meta_title":null,"meta_description":null,"og_description":null,"og_image":null,"og_title":null,"twitter_description":null,"twitter_image":null,"twitter_title":null,"authors":[{"slug":"shahed","url":"https://backend.shahednasser.com/author/shahed/","name":"Shahed Nasser","bio":null,"cover_image":null,"profile_image":"https://backend.shahednasser.com/content/images/2022/03/IMG_0591.jpg","location":null,"website":null,"twitter":null,"facebook":null,"meta_title":null,"meta_description":null,"coverImageSharp":null,"profileImageSharp":null}],"primary_author":{"slug":"shahed","url":"https://backend.shahednasser.com/author/shahed/","name":"Shahed Nasser","bio":null,"cover_image":null,"profile_image":"https://backend.shahednasser.com/content/images/2022/03/IMG_0591.jpg","location":null,"website":null,"twitter":null,"facebook":null,"meta_title":null,"meta_description":null,"coverImageSharp":null,"profileImageSharp":{"base":"IMG_0591.jpg","publicURL":"/static/ceb49c3c631485453e71e00d7f84b069/IMG_0591.jpg","imageMeta":{"width":1182,"height":1179},"childImageSharp":{"fluid":{"base64":"data:image/jpeg;base64,/9j/2wBDABALDA4MChAODQ4SERATGCgaGBYWGDEjJR0oOjM9PDkzODdASFxOQERXRTc4UG1RV19iZ2hnPk1xeXBkeFxlZ2P/2wBDARESEhgVGC8aGi9jQjhCY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2P/wgARCAAUABQDASIAAhEBAxEB/8QAGAABAQEBAQAAAAAAAAAAAAAAAAMEAQL/xAAWAQEBAQAAAAAAAAAAAAAAAAADBAL/2gAMAwEAAhADEAAAAdXiFM6i0CohUWXoKn//xAAcEAACAgIDAAAAAAAAAAAAAAACAwESBBEhM0H/2gAIAQEAAQUCWySE3WEr7SzbXjAj4iKty+sOQ//EABYRAQEBAAAAAAAAAAAAAAAAAAERIP/aAAgBAwEBPwEhj//EABQRAQAAAAAAAAAAAAAAAAAAACD/2gAIAQIBAT8BH//EAB4QAAIBBAMBAAAAAAAAAAAAAAABIRESMUECECJx/9oACAEBAAY/ApVGWvOjzgtUwLlTZA0sdL4f/8QAHBAAAwACAwEAAAAAAAAAAAAAAAERITFBkbHB/9oACAEBAAE/IahkCy+N2GwZpjQiJHJCspUFY0QrSi+HqiW2rgf/2gAMAwEAAgADAAAAEPw3/wD/xAAYEQEBAAMAAAAAAAAAAAAAAAAAARExQf/aAAgBAwEBPxCtjDqP/8QAFxEBAQEBAAAAAAAAAAAAAAAAAQAxEf/aAAgBAgEBPxBFus6Tt//EAB8QAQEAAgIBBQAAAAAAAAAAAAERACExQWFRcYGR0f/aAAgBAQABPxAuaBPPzkO1wyX7F4wkwXanfZrFQgeqE9JgS14vVOvrERIJomVBKwt2jebAeP0yVa8h1n//2Q==","aspectRatio":1,"src":"/static/ceb49c3c631485453e71e00d7f84b069/31709/IMG_0591.jpg","srcSet":"/static/ceb49c3c631485453e71e00d7f84b069/f340b/IMG_0591.jpg 28w,\n/static/ceb49c3c631485453e71e00d7f84b069/22d64/IMG_0591.jpg 55w,\n/static/ceb49c3c631485453e71e00d7f84b069/31709/IMG_0591.jpg 110w,\n/static/ceb49c3c631485453e71e00d7f84b069/aa249/IMG_0591.jpg 165w,\n/static/ceb49c3c631485453e71e00d7f84b069/0dc33/IMG_0591.jpg 220w,\n/static/ceb49c3c631485453e71e00d7f84b069/d8257/IMG_0591.jpg 1182w","srcWebp":"/static/ceb49c3c631485453e71e00d7f84b069/8678c/IMG_0591.webp","srcSetWebp":"/static/ceb49c3c631485453e71e00d7f84b069/59cda/IMG_0591.webp 28w,\n/static/ceb49c3c631485453e71e00d7f84b069/7da75/IMG_0591.webp 55w,\n/static/ceb49c3c631485453e71e00d7f84b069/8678c/IMG_0591.webp 110w,\n/static/ceb49c3c631485453e71e00d7f84b069/f282e/IMG_0591.webp 165w,\n/static/ceb49c3c631485453e71e00d7f84b069/a7b21/IMG_0591.webp 220w,\n/static/ceb49c3c631485453e71e00d7f84b069/63099/IMG_0591.webp 1182w","sizes":"(max-width: 110px) 100vw, 110px"}}}},"primary_tag":null,"tags":[],"plaintext":"Hooks have been officially added to React as of React 16.8. One of the main\nbenefits of Hooks in React is that you can use state and effect (which is\ngenerally a combination of different life cycles into one) in a component\nwithout creating a class.\n\nIn this tutorial, you’ll learn how to exactly use hooks in React. We will build\na simple website that gives the user some options to pick from in order to\ngenerate some random number trivia.\n\nYou can check out the demo for the website here\n[http://numbers-trivia.surge.sh/], and the source code on the GitHub repository\n[https://github.com/shahednasser/numbers-trivia].\n\nLet’s first start by creating a react app. Since our website will be very\nsimple, we will use the create-react-app command. If you don’t have it\ninstalled, you can install it using npm.\n\nRun the following in your terminal or CMD:\n\nnpm i -g create-react-app\n\nThis will install create-react-app globally.\n\nNow we can create our React app:\n\ncreate-react-app numbers-trivia\n\nRunning this command will create a directory in the working directory with the\nname you supply for the React app. I named it numbers-trivia but you can call it\nwhatever you want.\n\nInside that directory, it will also install all the packages and files needed to\nrun the website. It will install packages like react, react-dom, react-scripts \nand more.\n\nOnce it’s done, change into the newly created directory and start the server:\n\ncd numbers-trivia\nnpm start\n\nOnce you start the server, a web page of your website in your favorite browser\nwill open. You will see a page with just the logo and a link to learn React.\n\nBefore we start changing the page, if you are not familiar with React, let’s\ntake a look at the structure of our React app. It should look something like\nthis:\n\nIn our root directory, we will find a bunch of directories. node_modules holds\nall the packages React needs to run and any package you might add. public holds\nthe files our server serves.\n\nThe directory that we will spend most of our time in is the src directory. This\ndirectory will hold all of our React components.\n\nAs you can see there are a bunch of files in that directory. index.js is\nbasically the file that renders our highest React component.\n\nWe only have one React component right now which is in App.js. If you open it,\nyou will find the code that renders the logo with the link to learn React that\nyou currently see on the website.\n\nSo, in order to see how changing our App component will change the content of\nthe website, let’s modify the code by removing the current content and replacing\nit with our favorite phrase: “Hello, World!”\n\nimport React, { Component } from 'react' \nimport logo from './logo.svg' \nimport './App.css'  \nclass App extends Component {\n   render() {\n     return (\n       <div className=\"App\">\n         <header className=\"App-header\">\n           <h1>Hello, World!</h1>\n         </header>\n       </div>\n     )\n   }\n }\nexport default App\n\nWe just replaced the content of the header element. Now, if your server is still\nrunning you will see that your page updated without you refreshing it, and you\nwill see that the previous content is replaced with Hello, World!\n\nSo now we know how and where we should edit our React components in order to get\nthe result we want. We can go ahead and start with our objective.\n\nWhat we’ll do for this website is the following:\n\n 1. Show a welcome message the first time the user opens the website, then\n    replace it with a message prompting the user to try the generator.\n 2. Render a form with text and select inputs. The text input is where the user\n    can enter the number they want to see trivia about, and the select input\n    will provide the user with options related to the trivia.\n 3. On submitting the form, send a request to this API [http://numbersapi.com/] \n    to fetch the trivia we need.\n 4. Render the trivia for the user to see it.\n\nLet’s start by organizing our directory structure first. In React it’s good\npractice to create a directory inside src holding all the components. We’ll\ncreate a directory called components. Once you create the directory, move App.js \ninto there. We will also create a directory called styles and move App.css and \nindex.css into it.\n\nWhen you do that, you will need to change the imports in your files as\nfollowing:\n\n 1. in index.js:\n\nimport React from 'react'; \nimport ReactDOM from 'react-dom'; \nimport '../styles/index.css'; \nimport App from './components/App'; \nimport  as serviceWorker from './serviceWorker';\n\n2. in App.js:\n\nimport React, { Component } from 'react'; \nimport logo from './logo.svg'; \nimport '../styles/App.css';\n\nOur directory structure should look like this now:\n\nWe will go ahead now and start building our webpage.\n\nThe first thing in our objectives list is showing a welcome message when the\nuser first opens the webpage. It will show up for 3 seconds, and then changes to\nanother message that will prompt the user to try out the trivia generator.\n\nWithout hooks, this could be done by using React’s lifecycle method \ncomponentDidMount which runs right after the component first renders.\n\nNow, we can use the effect hook [https://reactjs.org/docs/hooks-effect.html] \ninstead. It will look something like this:\n\nuseEffect(() => {\n  //perform something post render\n});\n\nThe function you pass to useEffect will be executed after every render. This\ncombines the lifecycles methods componentDidMount and componentDidUpdate into\none.\n\nWhat if you want to do something just after the first time the component\nrenders, like in componentDidMount? You can do this by passing a second\nparameter to useEffect.\n\nuseEffect accepts an array as a second parameter. This parameter is used as a\ncondition on when to perform the passed function. So, let’s say you want to\nchange a counter only after the variable counter changes, you can do it like so:\n\nuseEffect(() => document.title = `You have clicked ${counter} times`, [counter]);\n\nThis way, the function passed to useEffect will run after render only if the\nvalue for counter changes.\n\nIf we want the function to run only after the first render, we can do that by\npassing an empty array as the second parameter.\n\nLet’s come back now to what we want to do. We want to show a welcome message\nwhen the user first opens the page, then change that message after 3 seconds. We\ncan do that by adding the following inside App.js:\n\nimport React, {useEffect} from 'react'; \nimport Form from './Form'; \nimport '../styles/App.css'; \n \nfunction App() {\n   useEffect(() => {\n     setTimeout(() => {\n       let welcomeMessage = document.getElementById(\"welcomeMessage\");\n       welcomeMessage.innerHTML = \"Try Out Our Trivia Generator!\";\n     }, 3000);\n   }, []);\n   return (\n     <div className=\"App\">\n       <header className=\"App-header\">\n         <h1 id=\"welcomeMessage\">Welcome to Numbers Trivia!</h1>\n         <Form />\n       </header>\n     </div>\n   ); }  \nexport default App;\n\nHere’s what we’re doing:\n\n 1. Line 1: We added an import for useEffect\n 2. Line 4: We changed our class component into a function component\n 3. Line 5–10: we added an effect to our function component. This effect sets a\n    timer after 3 seconds that will change the text in the element with the id \n    welcomeMessage. Because we passed an empty array to useEffect, this effect\n    will only run once.\n 4. Line 11–17: We replaced the previous code in App.js to render an h1 element\n    having the id welcomeMessage, which is our target element.\n\nOkay, now go to our web page and see the changes. At first, the welcome message\n“Welcome to Numbers Trivia!” will show up, then 3 seconds later it will change\ninto “Try Out Our Trivia Generator!” We just used a React Hook!\n\nNext, let’s create our form input elements. To do that, we will create a new\nReact component called Form. Create in the directory components the file \nForm.js. For now, it will just include the following:\n\nimport React from 'react';  \nfunction Form(props){\n}\nexport default Form;\n\nThis will create the new React component. We’re just importing React, then we’re\ncreating a function called Form. As we said earlier in the tutorial, with the\nuse of hooks we can now create components as stateful functions rather than\nclasses. And in the last line, we’re exporting Form in order to import it in\nother files.\n\nIn the form, we will have a text input and select elements. This is based on the\nAPI we’re using. In the API, two parameters can be sent:\n\n 1. number: the number you want to get the trivia for. It can be an integer, a\n    date of the form month/day, or the keyword random which will retrieve facts\n    about a random number.\n 2. type: the type of information you want to get. There are a few types: math,\n    date, year, or, the default, trivia.\n\nWe will consider the text input element as optional. If the user does not enter\na number or a date, we will send the keyword random for the number element.\n\nLet’s add the following code inside the Form function in order to render our\nform:\n\nfunction Form(props){\n  return (<form>\n             <div>\n              <input type=\"text\" name=\"number\" placeholder=\"Enter a number (Optional)\" />\n             </div>\n             <div>\n              <select name=\"type\">\n               <option value=\"trivia\">Trivia</option>\n               <option value=\"math\">Math</option>\n               <option value=\"date\">Date</option>\n               <option value=\"year\">Year</option>\n              </select>\n             </div>\n             <button type=\"submit\">Generate</button>\n            </form>); \n}\n\nThis will create the form with the text input and select and button elements.\n\nAfter that, we need to import and render the Form component in our App \ncomponent:\n\nimport React, {useEffect} from 'react';\nimport Form from './Form';\nimport '../styles/App.css';  \nfunction App() {\n   useEffect(() => {\n     setTimeout(() => {\n       let welcomeMessage = document.getElementById(\"welcomeMessage\");\n       welcomeMessage.innerHTML = \"Try Out Our Trivia Generator!\";\n     }, 3000);\n   }, []);\n   return (\n     <div className=\"App\">\n       <header className=\"App-header\">\n         <h1 id=\"welcomeMessage\">Welcome to Numbers Trivia!</h1>\n         <Form />\n       </header>\n     </div>\n   );\n }\nexport default App;\n\nWe have changed the imports to import our Form component, and we added <Form /> \nto render the form.\n\nLet’s also add more styles just to make our form look a little better. Add the\nfollowing at the end of App.css:\n\nform {\n   font-size: 15px;\n}  \nform input, form select {\n   padding: 5px;\n}  \nform select {\n   width: 100%; \n}  \nform div {\n   margin-bottom: 8px;\n}\n\nIf you refresh the page now, you will find it has changed and now it’s showing\nour form.\n\nNow, we need to add some logic to our form. On Submit, we need to get the values\nof our input elements, then call the API to retrieve the results.\n\nTo handle form elements and their values, you need to use the state of the\ncomponent. You make the value of the element equal to a property in the state of\nthe component.\n\nBefore hooks, in order to get the value in the state you would have to use \nthis.state.value, and then to set the state, you will need to call this.setState\n.\n\nNow, you can use the state hook. The state hook is the useState function. It\naccepts one argument, which is the initial value, and it returns a pair of\nvalues: the current state and a function that updates it. Then, you will be able\nto access the current state using the first returned value, and update it using\nthe second returned value which is the function.\n\nHere’s an example:\n\nconst [count, setCount] = useState(0);\n\nin this example, we call the useState hook and pass it 0, and we set the\nreturned value equal to count and setCount. This means that we have created\nstate variable called count. Its initial value is 0, and to change its value we\ncan use setCount.\n\nFor our Form component, we need two state variables, one for the text input\nwhich we will call number, and one for the select input which we will call type.\nThen, on change event for these two input elements, we will change the values\nfor number and type using the function returned by setState.\n\nOpen our Form component and change it to the following:\n\nimport React, { useState } from 'react';\nfunction Form(props){\n  let [number, setNumber] = useState(\"random\");\n  let [type, setType] = useState(\"trivia\");\n  function onNumberChanged(e){\n    let value = e.target.value.trim();\n    if(!value.length){\n      setNumber(\"random\"); //default value\n    } else {\n      setNumber(value);\n    }\n  }\n  function onTypeChanged(e){\n    let value = e.target.value.trim();\n    if(!value.length){\n      setType(\"trivia\"); //default value\n    } else {\n      setType(value);\n    }\n  }\n  return (<form>\n            <div>\n              <input type=\"text\" name=\"number\" placeholder=\"Enter a number (Optional)\" value={number} onChange={onNumberChanged} />\n            </div>\n            <div>\n              <select name=\"type\" value={type} onChange={onTypeChanged}>\n                <option value=\"trivia\">Trivia</option>\n                <option value=\"math\">Math</option>\n                <option value=\"date\">Date</option>\n                <option value=\"year\">Year</option>\n              </select>\n            </div>\n            <button type=\"submit\">Generate</button>\n</form>);\n}\nexport default Form;\n\n 1. Line 1: add an import for useState hook.\n 2. Line 3–4: create two state variables number and type using useState . Here\n    we pass random as the initial value for number, and trivia as initial value\n    for type because they are the default values for the parameters in the API.\n 3. Line 5–10: implement input change handler functions for both text and select\n    inputs, where we change the value of the state variables using the functions\n    returned by useState . If the value is unset, we automatically change the\n    values to the default value.\n 4. Line 13: pass the onNumberChanged function to onChange event for text input.\n 5. Line 16: pass the onTypeChanged function to onChange event for select input.\n\nIn addition, let’s go back to our App component to modify it and use states.\nInstead of modifying our welcome message by changing the innerHTML of the\nelement, we will use a state. Our App component should now be like this:\n\nimport React, {useEffect, useState} from 'react'; \nimport Form from './Form'; \nimport '../styles/App.css'; \n \nfunction App() {\n  const [ welcomeMessage, setWelcomeMessage ] = useState(\n    \"Welcome to Numbers Trivia!\",\n  );\nuseEffect(() => {\n    setTimeout(() => {\n      setWelcomeMessage(\"Try Out Our Trivia Generator!\");\n    }, 3000);\n  }, []);\nreturn (\n    <div className=\"App\">\n      <header className=\"App-header\">\n        <h1>{welcomeMessage}</h1>\n      </header>\n      <Form/>\n    </div>\n  );\n}\nexport default App;\n\nNow, we are using useState to declare and initialize our welcome message. It\nwill return welcomeMessage , our state variable, and setWelcomeMessage , which\nwe will use to change the value of welcomeMessage after 3 seconds from “Welcome\nto Numbers Trivia!” to “Try Out Our Trivia Generator!”\n\nWhat’s left now is to add a function to handle the form’s onSubmit event. On\nsubmit, we will send a request to the API with our parameters, then display the\nresult.\n\nIn order to perform the request, we need to install axios\n[https://github.com/axios/axios]:\n\nnpm i axios\n\nThen, require axios at the beginning of Form.js:\n\nconst axios = require('axios');\n\nNow, add the following function below onTypeChanged in our Form component:\n\nfunction onSubmit(e){\n  e.preventDefault();\n  axios.get('http://numbersapi.com/' + number + '/' + type)\n       .then(function(response){\n         let elm = document.getElementById('result');\n         elm.innerHTML = response.data;\n       }).catch(function(e){\n         console.log(\"error\", e); //simple error handling\n       });\n}\n\nWe’re just performing a request to the API, passing the number and type then\ndisplaying the result in an element of id result (which we will add in a\nminute). In case of an error, we’re just displaying it in the console just as a\nsimple error handling.\n\nNow, let’s add this function as the handler for the form onSubmit event in the\nreturn statement in Form.js:\n\n<form onSubmit={onSubmit}>\n\nThe only thing left is to add the #result element. We can add it in App.js \nbefore <Form /> :\n\n<div id=\"result\" style={{marginBottom: '15px'}}></div>\n\nAlright, now go to your website and discover all new trivia about numbers!\n\n\n--------------------------------------------------------------------------------\n\nConclusion\nYou just created a React app and used some React Hooks! Of course, there’s still\nmore to explore and learn, so be sure to head to the docs\n[https://reactjs.org/docs/hooks-intro.html] to learn more about React Hooks.","html":"<p>Hooks have been officially added to React as of React 16.8. One of the main benefits of Hooks in React is that you can use state and effect (which is generally a combination of different life cycles into one) in a component without creating a class.</p><p>In this tutorial, you’ll learn how to exactly use hooks in React. We will build a simple website that gives the user some options to pick from in order to generate some random number trivia.</p><p>You can check out the demo for the website <a href=\"http://numbers-trivia.surge.sh/\" rel=\"noopener nofollow\">here</a>, and the source code on the <a href=\"https://github.com/shahednasser/numbers-trivia\" rel=\"noopener nofollow\">GitHub repository</a>.</p><p>Let’s first start by creating a react app. Since our website will be very simple, we will use the <code>create-react-app</code> command. If you don’t have it installed, you can install it using <code>npm</code><strong><strong>.</strong></strong></p><p>Run the following in your terminal or CMD:</p><pre><code class=\"language-Shell\">npm i -g create-react-app</code></pre><p>This will install <code>create-react-app</code> globally.</p><p>Now we can create our React app:</p><pre><code class=\"language-Shell\">create-react-app numbers-trivia</code></pre><p>Running this command will create a directory in the working directory with the name you supply for the React app. I named it <strong><strong>numbers-trivia</strong></strong> but you can call it whatever you want.</p><p>Inside that directory, it will also install all the packages and files needed to run the website. It will install packages like <strong><strong>react</strong></strong>, <strong><strong>react-dom</strong></strong>, <strong><strong>react-scripts</strong></strong> and more.</p><p>Once it’s done, change into the newly created directory and start the server:</p><pre><code class=\"language-Shell\">cd numbers-trivia\nnpm start</code></pre><p>Once you start the server, a web page of your website in your favorite browser will open. You will see a page with just the logo and a link to learn React.</p><p>Before we start changing the page, if you are not familiar with React, let’s take a look at the structure of our React app. It should look something like this:</p><figure class=\"kg-card kg-image-card\"><img src=\"https://res-3.cloudinary.com/hbqmf3mbz/image/upload/q_auto/v1/ghost-blog-images/1_R1irPlPj9UsboCM_KI5CgA.png\" class=\"kg-image\" alt loading=\"lazy\"></figure><p>In our root directory, we will find a bunch of directories. <strong><strong>node_modules </strong></strong>holds all the packages React needs to run and any package you might add. <strong><strong>public </strong></strong>holds the files our server serves.</p><p>The directory that we will spend most of our time in is the <strong><strong>src</strong></strong> directory. This directory will hold all of our React components.</p><p>As you can see there are a bunch of files in that directory. <strong><strong>index.js</strong></strong> is basically the file that renders our highest React component.</p><p>We only have one React component right now which is in <strong><strong>App.js. </strong></strong>If you open it, you will find the code that renders the logo with the link to learn React that you currently see on the website.</p><p>So, in order to see how changing our App component will change the content of the website, let’s modify the code by removing the current content and replacing it with our favorite phrase: “Hello, World!”</p><pre><code class=\"language-JS\">import React, { Component } from 'react' \nimport logo from './logo.svg' \nimport './App.css'  \nclass App extends Component {\n   render() {\n     return (\n       &lt;div className=\"App\"&gt;\n         &lt;header className=\"App-header\"&gt;\n           &lt;h1&gt;Hello, World!&lt;/h1&gt;\n         &lt;/header&gt;\n       &lt;/div&gt;\n     )\n   }\n }\nexport default App</code></pre><p>We just replaced the content of the <strong><strong>header</strong></strong> element. Now, if your server is still running you will see that your page updated without you refreshing it, and you will see that the previous content is replaced with <strong><strong>Hello, World!</strong></strong></p><p>So now we know how and where we should edit our React components in order to get the result we want. We can go ahead and start with our objective.</p><p>What we’ll do for this website is the following:</p><ol><li>Show a welcome message the first time the user opens the website, then replace it with a message prompting the user to try the generator.</li><li>Render a form with <strong><strong>text </strong></strong>and <strong><strong>select</strong></strong> inputs. The <strong><strong>text </strong></strong>input is where the user can enter the number they want to see trivia about, and the <strong><strong>select </strong></strong>input will provide the user with options related to the trivia.</li><li>On submitting the form, send a request to <a href=\"http://numbersapi.com/\" rel=\"noopener nofollow\">this API</a> to fetch the trivia we need.</li><li>Render the trivia for the user to see it.</li></ol><p>Let’s start by organizing our directory structure first. In React it’s good practice to create a directory inside <strong><strong>src</strong></strong> holding all the components. We’ll create a directory called <strong><strong>components</strong></strong>. Once you create the directory, move <strong><strong>App.js</strong></strong> into there. We will also create a directory called <strong><strong>styles</strong></strong> and move <strong><strong>App.css </strong></strong>and <strong><strong>index.css </strong></strong>into it.</p><p>When you do that, you will need to change the imports in your files as following:</p><ol><li>in <strong><strong>index.js:</strong></strong></li></ol><pre><code class=\"language-JS\">import React from 'react'; \nimport ReactDOM from 'react-dom'; \nimport '../styles/index.css'; \nimport App from './components/App'; \nimport  as serviceWorker from './serviceWorker';</code></pre><p>2. in <strong><strong>App.js:</strong></strong></p><pre><code class=\"language-JS\">import React, { Component } from 'react'; \nimport logo from './logo.svg'; \nimport '../styles/App.css';</code></pre><p>Our directory structure should look like this now:</p><figure class=\"kg-card kg-image-card\"><img src=\"https://res-4.cloudinary.com/hbqmf3mbz/image/upload/q_auto/v1/ghost-blog-images/1_Aqgelb3qbhJiESUbGGNUoQ.png\" class=\"kg-image\" alt loading=\"lazy\"></figure><p>We will go ahead now and start building our webpage.</p><p>The first thing in our objectives list is showing a welcome message when the user first opens the webpage. It will show up for 3 seconds, and then changes to another message that will prompt the user to try out the trivia generator.</p><p>Without hooks, this could be done by using React’s lifecycle method <code>componentDidMount</code><em><em> </em></em>which runs right after the component first renders.</p><p>Now, we can use the <a href=\"https://reactjs.org/docs/hooks-effect.html\" rel=\"noopener nofollow\"><em><em>effect hook</em></em></a><em><em> </em></em>instead. It will look something like this:</p><pre><code class=\"language-JS\">useEffect(() =&gt; {\n  //perform something post render\n});</code></pre><p>The function you pass to <code>useEffect</code> will be executed after every render. This combines the lifecycles methods <code>componentDidMount</code><strong><strong> </strong></strong>and <code>componentDidUpdate</code> into one.</p><p>What if you want to do something just after the first time the component renders, like in <code>componentDidMount</code>? You can do this by passing a second parameter to <code>useEffect</code>.</p><p><code>useEffect</code><strong><strong> </strong></strong>accepts an array as a second parameter. This parameter is used as a condition on when to perform the passed function. So, let’s say you want to change a counter only after the variable <strong><strong>counter </strong></strong>changes, you can do it like so:</p><pre><code class=\"language-JS\">useEffect(() =&gt; document.title = `You have clicked ${counter} times`, [counter]);</code></pre><p>This way, the function passed to <code>useEffect</code> will run after render only if the value for <code>counter</code><strong><strong> </strong></strong>changes.</p><p>If we want the function to run only after the first render, we can do that by passing an <strong><strong>empty</strong></strong> array as the second parameter.</p><p>Let’s come back now to what we want to do. We want to show a welcome message when the user first opens the page, then change that message after 3 seconds. We can do that by adding the following inside <strong><strong>App.js</strong></strong>:</p><pre><code class=\"language-JS\">import React, {useEffect} from 'react'; \nimport Form from './Form'; \nimport '../styles/App.css'; \n \nfunction App() {\n   useEffect(() =&gt; {\n     setTimeout(() =&gt; {\n       let welcomeMessage = document.getElementById(\"welcomeMessage\");\n       welcomeMessage.innerHTML = \"Try Out Our Trivia Generator!\";\n     }, 3000);\n   }, []);\n   return (\n     &lt;div className=\"App\"&gt;\n       &lt;header className=\"App-header\"&gt;\n         &lt;h1 id=\"welcomeMessage\"&gt;Welcome to Numbers Trivia!&lt;/h1&gt;\n         &lt;Form /&gt;\n       &lt;/header&gt;\n     &lt;/div&gt;\n   ); }  \nexport default App;</code></pre><p>Here’s what we’re doing:</p><ol><li><strong><strong>Line 1:</strong></strong> We added an import for <code>useEffect</code></li><li><strong><strong>Line 4: </strong></strong>We changed our class component into a function component</li><li><strong><strong>Line 5–10: </strong></strong>we added an effect to our function component. This effect sets a timer after 3 seconds that will change the text in the element with the id <code>welcomeMessage</code><strong><strong>. </strong></strong>Because we passed an <strong><strong>empty</strong></strong> array to <code>useEffect</code>, this effect will only run once.</li><li><strong><strong>Line 11–17: </strong></strong>We replaced the previous code in <strong><strong>App.js </strong></strong>to render an <strong><strong>h1 </strong></strong>element having the id <code>welcomeMessage</code><strong><strong>, </strong></strong>which is our target element.</li></ol><p>Okay, now go to our web page and see the changes. At first, the welcome message “<strong><strong>Welcome to Numbers Trivia!</strong></strong>” will show up, then 3 seconds later it will change into “<strong><strong>Try Out Our Trivia Generator!</strong></strong>” We just used a React Hook!</p><p>Next, let’s create our form input elements. To do that, we will create a new React component called <code>Form</code><strong><strong>. </strong></strong>Create in the directory <strong><strong>components</strong></strong> the file <strong><strong>Form.js. </strong></strong>For now, it will just include the following:</p><pre><code class=\"language-JS\">import React from 'react';  \nfunction Form(props){\n}\nexport default Form;</code></pre><p>This will create the new React component. We’re just importing React, then we’re creating a function called Form. As we said earlier in the tutorial, with the use of hooks we can now create components as stateful functions rather than classes. And in the last line, we’re exporting <strong><strong>Form</strong></strong> in order to import it in other files.</p><p>In the form, we will have a <strong><strong>text </strong></strong>input and <strong><strong>select </strong></strong>elements. This is based on the API we’re using. In the API, two parameters can be sent:</p><ol><li><strong><strong>number: </strong></strong>the number you want to get the trivia for. It can be an <strong><strong>integer</strong></strong>, a date of the form <strong><strong>month/day, </strong></strong>or the keyword <strong><strong>random </strong></strong>which will retrieve facts about a random number.</li><li><strong><strong>type: </strong></strong>the type of information you want to get. There are a few types: <strong><strong>math, date, year, </strong></strong>or, the default, <strong><strong>trivia.</strong></strong></li></ol><p>We will consider the text input element as optional. If the user does not enter a number or a date, we will send the keyword <strong><strong>random</strong></strong> for the number element.</p><p>Let’s add the following code inside the <strong><strong>Form</strong></strong> function in order to render our form:</p><pre><code class=\"language-JS\">function Form(props){\n  return (&lt;form&gt;\n             &lt;div&gt;\n              &lt;input type=\"text\" name=\"number\" placeholder=\"Enter a number (Optional)\" /&gt;\n             &lt;/div&gt;\n             &lt;div&gt;\n              &lt;select name=\"type\"&gt;\n               &lt;option value=\"trivia\"&gt;Trivia&lt;/option&gt;\n               &lt;option value=\"math\"&gt;Math&lt;/option&gt;\n               &lt;option value=\"date\"&gt;Date&lt;/option&gt;\n               &lt;option value=\"year\"&gt;Year&lt;/option&gt;\n              &lt;/select&gt;\n             &lt;/div&gt;\n             &lt;button type=\"submit\"&gt;Generate&lt;/button&gt;\n            &lt;/form&gt;); \n}</code></pre><p>This will create the form with the <strong><strong>text </strong></strong>input and <strong><strong>select </strong></strong>and <strong><strong>button </strong></strong>elements.</p><p>After that, we need to import and render the <strong><strong>Form</strong></strong> component in our <strong><strong>App </strong></strong>component:</p><pre><code class=\"language-JS\">import React, {useEffect} from 'react';\nimport Form from './Form';\nimport '../styles/App.css';  \nfunction App() {\n   useEffect(() =&gt; {\n     setTimeout(() =&gt; {\n       let welcomeMessage = document.getElementById(\"welcomeMessage\");\n       welcomeMessage.innerHTML = \"Try Out Our Trivia Generator!\";\n     }, 3000);\n   }, []);\n   return (\n     &lt;div className=\"App\"&gt;\n       &lt;header className=\"App-header\"&gt;\n         &lt;h1 id=\"welcomeMessage\"&gt;Welcome to Numbers Trivia!&lt;/h1&gt;\n         &lt;Form /&gt;\n       &lt;/header&gt;\n     &lt;/div&gt;\n   );\n }\nexport default App;</code></pre><p>We have changed the imports to import our <code>Form</code><strong><strong> </strong></strong>component, and we added <code>&lt;Form /&gt;</code><strong><strong> </strong></strong>to render the form.</p><p>Let’s also add more styles just to make our form look a little better. Add the following at the end of <strong><strong>App.css</strong></strong>:</p><pre><code class=\"language-CSS\">form {\n   font-size: 15px;\n}  \nform input, form select {\n   padding: 5px;\n}  \nform select {\n   width: 100%; \n}  \nform div {\n   margin-bottom: 8px;\n}</code></pre><p>If you refresh the page now, you will find it has changed and now it’s showing our form.</p><p>Now, we need to add some logic to our form. <strong><strong>On Submit</strong></strong>, we need to get the values of our input elements, then call the API to retrieve the results.</p><p>To handle form elements and their values, you need to use the state of the component. You make the value of the element equal to a property in the state of the component.</p><p>Before hooks, in order to get the value in the state you would have to use <code>this.state.value</code><strong><strong>, </strong></strong>and then to set the state, you will need to call <code>this.setState</code>.</p><p>Now, you can use the state hook. The state hook is the <code>useState</code><strong><strong> </strong></strong>function. It accepts one argument, which is the initial value, and it returns a pair of values: the <strong><strong>current state</strong></strong> and a <strong><strong>function</strong></strong> that updates it. Then, you will be able to access the current state using the first returned value, and update it using the second returned value which is the function.</p><p>Here’s an example:</p><pre><code class=\"language-JS\">const [count, setCount] = useState(0);</code></pre><p>in this example, we call the <code>useState</code><strong><strong> </strong></strong>hook and pass it 0, and we set the returned value equal to <code>count</code> and <code>setCount</code>. This means that we have created state variable called <code>count</code>. Its initial value is 0, and to change its value we can use <code>setCount</code>.</p><p>For our <code>Form</code><strong><strong> </strong></strong>component, we need two state variables, one for the text<strong><strong> </strong></strong>input which we will call <code>number</code>, and one for the select<strong><strong> </strong></strong>input which we will call <code>type</code>. Then, on change event for these two input elements, we will change the values for <code>number</code><strong><strong> </strong></strong>and <code>type</code> using the function returned by <code>setState</code><strong><strong>.</strong></strong></p><p>Open our <code>Form</code><strong><strong> </strong></strong>component and change it to the following:</p><pre><code class=\"language-JS\">import React, { useState } from 'react';\nfunction Form(props){\n  let [number, setNumber] = useState(\"random\");\n  let [type, setType] = useState(\"trivia\");\n  function onNumberChanged(e){\n    let value = e.target.value.trim();\n    if(!value.length){\n      setNumber(\"random\"); //default value\n    } else {\n      setNumber(value);\n    }\n  }\n  function onTypeChanged(e){\n    let value = e.target.value.trim();\n    if(!value.length){\n      setType(\"trivia\"); //default value\n    } else {\n      setType(value);\n    }\n  }\n  return (&lt;form&gt;\n            &lt;div&gt;\n              &lt;input type=\"text\" name=\"number\" placeholder=\"Enter a number (Optional)\" value={number} onChange={onNumberChanged} /&gt;\n            &lt;/div&gt;\n            &lt;div&gt;\n              &lt;select name=\"type\" value={type} onChange={onTypeChanged}&gt;\n                &lt;option value=\"trivia\"&gt;Trivia&lt;/option&gt;\n                &lt;option value=\"math\"&gt;Math&lt;/option&gt;\n                &lt;option value=\"date\"&gt;Date&lt;/option&gt;\n                &lt;option value=\"year\"&gt;Year&lt;/option&gt;\n              &lt;/select&gt;\n            &lt;/div&gt;\n            &lt;button type=\"submit\"&gt;Generate&lt;/button&gt;\n&lt;/form&gt;);\n}\nexport default Form;</code></pre><ol><li><strong><strong>Line 1: </strong></strong>add an import for <code>useState</code> hook.</li><li><strong><strong>Line 3–4: </strong></strong>create two state variables <code>number</code> and <code>type</code> using <code>useState</code> . Here we pass <strong><strong>random </strong></strong>as the initial value for number, and <strong><strong>trivia </strong></strong>as initial value for type because they are the default values for the parameters in the API.</li><li><strong><strong>Line 5–10: </strong></strong>implement input change handler functions for both text and select inputs, where we change the value of the state variables using the functions returned by <code>useState</code> . If the value is unset, we automatically change the values to the default value.</li><li><strong><strong>Line 13: </strong></strong>pass the <code>onNumberChanged</code> function to <code>onChange</code> event for text input.</li><li><strong><strong>Line 16: </strong></strong>pass the <code>onTypeChanged</code> function to <code>onChange</code> event for select input.</li></ol><p>In addition, let’s go back to our <code>App</code> component to modify it and use states. Instead of modifying our welcome message by changing the <code>innerHTML</code> of the element, we will use a state. Our App component should now be like this:</p><pre><code>import React, {useEffect, useState} from 'react'; \nimport Form from './Form'; \nimport '../styles/App.css'; \n \nfunction App() {\n  const [ welcomeMessage, setWelcomeMessage ] = useState(\n    \"Welcome to Numbers Trivia!\",\n  );\nuseEffect(() =&gt; {\n    setTimeout(() =&gt; {\n      setWelcomeMessage(\"Try Out Our Trivia Generator!\");\n    }, 3000);\n  }, []);\nreturn (\n    &lt;div className=\"App\"&gt;\n      &lt;header className=\"App-header\"&gt;\n        &lt;h1&gt;{welcomeMessage}&lt;/h1&gt;\n      &lt;/header&gt;\n      &lt;Form/&gt;\n    &lt;/div&gt;\n  );\n}\nexport default App;</code></pre><p>Now, we are using <code>useState</code> to declare and initialize our welcome message. It will return <code>welcomeMessage</code> , our state variable, and <code>setWelcomeMessage</code> , which we will use to change the value of <code>welcomeMessage</code> after 3 seconds from “<strong><strong>Welcome to Numbers Trivia!</strong></strong>” to “<strong><strong>Try Out Our Trivia Generator!</strong></strong>”</p><p>What’s left now is to add a function to handle the form’s <code>onSubmit</code> event. On submit, we will send a request to the API with our parameters, then display the result.</p><p>In order to perform the request, we need to install <a href=\"https://github.com/axios/axios\" rel=\"noopener nofollow\">axios</a>:</p><pre><code class=\"language-Shell\">npm i axios</code></pre><p>Then, require axios at the beginning of <strong><strong>Form.js:</strong></strong></p><pre><code class=\"language-JS\">const axios = require('axios');</code></pre><p>Now, add the following function below <code>onTypeChanged</code> in our <strong><strong>Form</strong></strong> component:</p><pre><code class=\"language-JS\">function onSubmit(e){\n  e.preventDefault();\n  axios.get('http://numbersapi.com/' + number + '/' + type)\n       .then(function(response){\n         let elm = document.getElementById('result');\n         elm.innerHTML = response.data;\n       }).catch(function(e){\n         console.log(\"error\", e); //simple error handling\n       });\n}</code></pre><p>We’re just performing a request to the API, passing the <code>number</code> and <code>type</code> then displaying the result in an element of id <code>result</code> (which we will add in a minute). In case of an error, we’re just displaying it in the console just as a simple error handling.</p><p>Now, let’s add this function as the handler for the form <code>onSubmit</code> event in the return statement in Form.js:</p><pre><code class=\"language-JSX\">&lt;form onSubmit={onSubmit}&gt;</code></pre><p>The only thing left is to add the <code>#result</code> element. We can add it in <strong><strong>App.js </strong></strong>before <code>&lt;Form /&gt;</code> :</p><pre><code class=\"language-JSX\">&lt;div id=\"result\" style={{marginBottom: '15px'}}&gt;&lt;/div&gt;</code></pre><p>Alright, now go to your website and discover all new trivia about numbers!</p><hr><h2 id=\"conclusion\">Conclusion</h2><p>You just created a React app and used some React Hooks! Of course, there’s still more to explore and learn, so be sure to head to the <a href=\"https://reactjs.org/docs/hooks-intro.html\" rel=\"noopener nofollow\">docs</a> to learn more about React Hooks.</p>","url":"https://backend.shahednasser.com/react-hooks-tutorial-create-a-number-trivia-generator-website/","canonical_url":null,"uuid":"033786bf-592f-454b-b640-42920b676fb8","codeinjection_foot":null,"codeinjection_head":null,"codeinjection_styles":null,"comment_id":"5faa42902280bc001e2ccc95","reading_time":11,"send_email_when_published":null,"email_subject":null,"childHtmlRehype":{"html":"<p>Hooks have been officially added to React as of React 16.8. One of the main benefits of Hooks in React is that you can use state and effect (which is generally a combination of different life cycles into one) in a component without creating a class.</p><p>In this tutorial, you’ll learn how to exactly use hooks in React. We will build a simple website that gives the user some options to pick from in order to generate some random number trivia.</p><p>You can check out the demo for the website <a href=\"http://numbers-trivia.surge.sh/\" rel=\"noopener nofollow\">here</a>, and the source code on the <a href=\"https://github.com/shahednasser/numbers-trivia\" rel=\"noopener nofollow\">GitHub repository</a>.</p><p>Let’s first start by creating a react app. Since our website will be very simple, we will use the <code class=\"language-text\">create-react-app</code> command. If you don’t have it installed, you can install it using <code class=\"language-text\">npm</code><strong><strong>.</strong></strong></p><p>Run the following in your terminal or CMD:</p><div class=\"kg-card kg-code-card gatsby-highlight\" data-language=\"shell\"><pre class=\"language-shell\"><code class=\"language-shell\"><span class=\"token function\">npm</span> i -g create-react-app</code></pre></div><p>This will install <code class=\"language-text\">create-react-app</code> globally.</p><p>Now we can create our React app:</p><div class=\"kg-card kg-code-card gatsby-highlight\" data-language=\"shell\"><pre class=\"language-shell\"><code class=\"language-shell\">create-react-app numbers-trivia</code></pre></div><p>Running this command will create a directory in the working directory with the name you supply for the React app. I named it <strong><strong>numbers-trivia</strong></strong> but you can call it whatever you want.</p><p>Inside that directory, it will also install all the packages and files needed to run the website. It will install packages like <strong><strong>react</strong></strong>, <strong><strong>react-dom</strong></strong>, <strong><strong>react-scripts</strong></strong> and more.</p><p>Once it’s done, change into the newly created directory and start the server:</p><div class=\"kg-card kg-code-card gatsby-highlight\" data-language=\"shell\"><pre class=\"language-shell\"><code class=\"language-shell\"><span class=\"token builtin class-name\">cd</span> numbers-trivia\n<span class=\"token function\">npm</span> start</code></pre></div><p>Once you start the server, a web page of your website in your favorite browser will open. You will see a page with just the logo and a link to learn React.</p><p>Before we start changing the page, if you are not familiar with React, let’s take a look at the structure of our React app. It should look something like this:</p><figure class=\"kg-card kg-image-card\"><img src=\"https://res-3.cloudinary.com/hbqmf3mbz/image/upload/q_auto/v1/ghost-blog-images/1_R1irPlPj9UsboCM_KI5CgA.png\" class=\"kg-image\" alt=\"\" loading=\"lazy\"></figure><p>In our root directory, we will find a bunch of directories. <strong><strong>node_modules </strong></strong>holds all the packages React needs to run and any package you might add. <strong><strong>public </strong></strong>holds the files our server serves.</p><p>The directory that we will spend most of our time in is the <strong><strong>src</strong></strong> directory. This directory will hold all of our React components.</p><p>As you can see there are a bunch of files in that directory. <strong><strong>index.js</strong></strong> is basically the file that renders our highest React component.</p><p>We only have one React component right now which is in <strong><strong>App.js. </strong></strong>If you open it, you will find the code that renders the logo with the link to learn React that you currently see on the website.</p><p>So, in order to see how changing our App component will change the content of the website, let’s modify the code by removing the current content and replacing it with our favorite phrase: “Hello, World!”</p><div class=\"kg-card kg-code-card gatsby-highlight\" data-language=\"js\"><pre class=\"language-js\"><code class=\"language-js\"><span class=\"token keyword\">import</span> React<span class=\"token punctuation\">,</span> <span class=\"token punctuation\">{</span> Component <span class=\"token punctuation\">}</span> <span class=\"token keyword\">from</span> <span class=\"token string\">'react'</span> \n<span class=\"token keyword\">import</span> logo <span class=\"token keyword\">from</span> <span class=\"token string\">'./logo.svg'</span> \n<span class=\"token keyword\">import</span> <span class=\"token string\">'./App.css'</span>  \n<span class=\"token keyword\">class</span> <span class=\"token class-name\">App</span> <span class=\"token keyword\">extends</span> <span class=\"token class-name\">Component</span> <span class=\"token punctuation\">{</span>\n   <span class=\"token function\">render</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n     <span class=\"token keyword\">return</span> <span class=\"token punctuation\">(</span>\n       <span class=\"token operator\">&#x3C;</span>div className<span class=\"token operator\">=</span><span class=\"token string\">\"App\"</span><span class=\"token operator\">></span>\n         <span class=\"token operator\">&#x3C;</span>header className<span class=\"token operator\">=</span><span class=\"token string\">\"App-header\"</span><span class=\"token operator\">></span>\n           <span class=\"token operator\">&#x3C;</span>h1<span class=\"token operator\">></span>Hello<span class=\"token punctuation\">,</span> World<span class=\"token operator\">!</span><span class=\"token operator\">&#x3C;</span><span class=\"token operator\">/</span>h1<span class=\"token operator\">></span>\n         <span class=\"token operator\">&#x3C;</span><span class=\"token operator\">/</span>header<span class=\"token operator\">></span>\n       <span class=\"token operator\">&#x3C;</span><span class=\"token operator\">/</span>div<span class=\"token operator\">></span>\n     <span class=\"token punctuation\">)</span>\n   <span class=\"token punctuation\">}</span>\n <span class=\"token punctuation\">}</span>\n<span class=\"token keyword\">export</span> <span class=\"token keyword\">default</span> App</code></pre></div><p>We just replaced the content of the <strong><strong>header</strong></strong> element. Now, if your server is still running you will see that your page updated without you refreshing it, and you will see that the previous content is replaced with <strong><strong>Hello, World!</strong></strong></p><p>So now we know how and where we should edit our React components in order to get the result we want. We can go ahead and start with our objective.</p><p>What we’ll do for this website is the following:</p><ol><li>Show a welcome message the first time the user opens the website, then replace it with a message prompting the user to try the generator.</li><li>Render a form with <strong><strong>text </strong></strong>and <strong><strong>select</strong></strong> inputs. The <strong><strong>text </strong></strong>input is where the user can enter the number they want to see trivia about, and the <strong><strong>select </strong></strong>input will provide the user with options related to the trivia.</li><li>On submitting the form, send a request to <a href=\"http://numbersapi.com/\" rel=\"noopener nofollow\">this API</a> to fetch the trivia we need.</li><li>Render the trivia for the user to see it.</li></ol><p>Let’s start by organizing our directory structure first. In React it’s good practice to create a directory inside <strong><strong>src</strong></strong> holding all the components. We’ll create a directory called <strong><strong>components</strong></strong>. Once you create the directory, move <strong><strong>App.js</strong></strong> into there. We will also create a directory called <strong><strong>styles</strong></strong> and move <strong><strong>App.css </strong></strong>and <strong><strong>index.css </strong></strong>into it.</p><p>When you do that, you will need to change the imports in your files as following:</p><ol><li>in <strong><strong>index.js:</strong></strong></li></ol><div class=\"kg-card kg-code-card gatsby-highlight\" data-language=\"js\"><pre class=\"language-js\"><code class=\"language-js\"><span class=\"token keyword\">import</span> React <span class=\"token keyword\">from</span> <span class=\"token string\">'react'</span><span class=\"token punctuation\">;</span> \n<span class=\"token keyword\">import</span> ReactDOM <span class=\"token keyword\">from</span> <span class=\"token string\">'react-dom'</span><span class=\"token punctuation\">;</span> \n<span class=\"token keyword\">import</span> <span class=\"token string\">'../styles/index.css'</span><span class=\"token punctuation\">;</span> \n<span class=\"token keyword\">import</span> App <span class=\"token keyword\">from</span> <span class=\"token string\">'./components/App'</span><span class=\"token punctuation\">;</span> \n<span class=\"token keyword\">import</span>  <span class=\"token keyword\">as</span> serviceWorker <span class=\"token keyword\">from</span> <span class=\"token string\">'./serviceWorker'</span><span class=\"token punctuation\">;</span></code></pre></div><p>2. in <strong><strong>App.js:</strong></strong></p><div class=\"kg-card kg-code-card gatsby-highlight\" data-language=\"js\"><pre class=\"language-js\"><code class=\"language-js\"><span class=\"token keyword\">import</span> React<span class=\"token punctuation\">,</span> <span class=\"token punctuation\">{</span> Component <span class=\"token punctuation\">}</span> <span class=\"token keyword\">from</span> <span class=\"token string\">'react'</span><span class=\"token punctuation\">;</span> \n<span class=\"token keyword\">import</span> logo <span class=\"token keyword\">from</span> <span class=\"token string\">'./logo.svg'</span><span class=\"token punctuation\">;</span> \n<span class=\"token keyword\">import</span> <span class=\"token string\">'../styles/App.css'</span><span class=\"token punctuation\">;</span></code></pre></div><p>Our directory structure should look like this now:</p><figure class=\"kg-card kg-image-card\"><img src=\"https://res-4.cloudinary.com/hbqmf3mbz/image/upload/q_auto/v1/ghost-blog-images/1_Aqgelb3qbhJiESUbGGNUoQ.png\" class=\"kg-image\" alt=\"\" loading=\"lazy\"></figure><p>We will go ahead now and start building our webpage.</p><p>The first thing in our objectives list is showing a welcome message when the user first opens the webpage. It will show up for 3 seconds, and then changes to another message that will prompt the user to try out the trivia generator.</p><p>Without hooks, this could be done by using React’s lifecycle method <code class=\"language-text\">componentDidMount</code><em><em> </em></em>which runs right after the component first renders.</p><p>Now, we can use the <a href=\"https://reactjs.org/docs/hooks-effect.html\" rel=\"noopener nofollow\"><em><em>effect hook</em></em></a><em><em> </em></em>instead. It will look something like this:</p><div class=\"kg-card kg-code-card gatsby-highlight\" data-language=\"js\"><pre class=\"language-js\"><code class=\"language-js\"><span class=\"token function\">useEffect</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">=></span> <span class=\"token punctuation\">{</span>\n  <span class=\"token comment\">//perform something post render</span>\n<span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span></code></pre></div><p>The function you pass to <code class=\"language-text\">useEffect</code> will be executed after every render. This combines the lifecycles methods <code class=\"language-text\">componentDidMount</code><strong><strong> </strong></strong>and <code class=\"language-text\">componentDidUpdate</code> into one.</p><p>What if you want to do something just after the first time the component renders, like in <code class=\"language-text\">componentDidMount</code>? You can do this by passing a second parameter to <code class=\"language-text\">useEffect</code>.</p><p><code class=\"language-text\">useEffect</code><strong><strong> </strong></strong>accepts an array as a second parameter. This parameter is used as a condition on when to perform the passed function. So, let’s say you want to change a counter only after the variable <strong><strong>counter </strong></strong>changes, you can do it like so:</p><div class=\"kg-card kg-code-card gatsby-highlight\" data-language=\"js\"><pre class=\"language-js\"><code class=\"language-js\"><span class=\"token function\">useEffect</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">=></span> document<span class=\"token punctuation\">.</span>title <span class=\"token operator\">=</span> <span class=\"token template-string\"><span class=\"token template-punctuation string\">`</span><span class=\"token string\">You have clicked </span><span class=\"token interpolation\"><span class=\"token interpolation-punctuation punctuation\">${</span>counter<span class=\"token interpolation-punctuation punctuation\">}</span></span><span class=\"token string\"> times</span><span class=\"token template-punctuation string\">`</span></span><span class=\"token punctuation\">,</span> <span class=\"token punctuation\">[</span>counter<span class=\"token punctuation\">]</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span></code></pre></div><p>This way, the function passed to <code class=\"language-text\">useEffect</code> will run after render only if the value for <code class=\"language-text\">counter</code><strong><strong> </strong></strong>changes.</p><p>If we want the function to run only after the first render, we can do that by passing an <strong><strong>empty</strong></strong> array as the second parameter.</p><p>Let’s come back now to what we want to do. We want to show a welcome message when the user first opens the page, then change that message after 3 seconds. We can do that by adding the following inside <strong><strong>App.js</strong></strong>:</p><div class=\"kg-card kg-code-card gatsby-highlight\" data-language=\"js\"><pre class=\"language-js\"><code class=\"language-js\"><span class=\"token keyword\">import</span> React<span class=\"token punctuation\">,</span> <span class=\"token punctuation\">{</span>useEffect<span class=\"token punctuation\">}</span> <span class=\"token keyword\">from</span> <span class=\"token string\">'react'</span><span class=\"token punctuation\">;</span> \n<span class=\"token keyword\">import</span> Form <span class=\"token keyword\">from</span> <span class=\"token string\">'./Form'</span><span class=\"token punctuation\">;</span> \n<span class=\"token keyword\">import</span> <span class=\"token string\">'../styles/App.css'</span><span class=\"token punctuation\">;</span> \n \n<span class=\"token keyword\">function</span> <span class=\"token function\">App</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n   <span class=\"token function\">useEffect</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">=></span> <span class=\"token punctuation\">{</span>\n     <span class=\"token function\">setTimeout</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">=></span> <span class=\"token punctuation\">{</span>\n       <span class=\"token keyword\">let</span> welcomeMessage <span class=\"token operator\">=</span> document<span class=\"token punctuation\">.</span><span class=\"token function\">getElementById</span><span class=\"token punctuation\">(</span><span class=\"token string\">\"welcomeMessage\"</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n       welcomeMessage<span class=\"token punctuation\">.</span>innerHTML <span class=\"token operator\">=</span> <span class=\"token string\">\"Try Out Our Trivia Generator!\"</span><span class=\"token punctuation\">;</span>\n     <span class=\"token punctuation\">}</span><span class=\"token punctuation\">,</span> <span class=\"token number\">3000</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n   <span class=\"token punctuation\">}</span><span class=\"token punctuation\">,</span> <span class=\"token punctuation\">[</span><span class=\"token punctuation\">]</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n   <span class=\"token keyword\">return</span> <span class=\"token punctuation\">(</span>\n     <span class=\"token operator\">&#x3C;</span>div className<span class=\"token operator\">=</span><span class=\"token string\">\"App\"</span><span class=\"token operator\">></span>\n       <span class=\"token operator\">&#x3C;</span>header className<span class=\"token operator\">=</span><span class=\"token string\">\"App-header\"</span><span class=\"token operator\">></span>\n         <span class=\"token operator\">&#x3C;</span>h1 id<span class=\"token operator\">=</span><span class=\"token string\">\"welcomeMessage\"</span><span class=\"token operator\">></span>Welcome to Numbers Trivia<span class=\"token operator\">!</span><span class=\"token operator\">&#x3C;</span><span class=\"token operator\">/</span>h1<span class=\"token operator\">></span>\n         <span class=\"token operator\">&#x3C;</span>Form <span class=\"token operator\">/</span><span class=\"token operator\">></span>\n       <span class=\"token operator\">&#x3C;</span><span class=\"token operator\">/</span>header<span class=\"token operator\">></span>\n     <span class=\"token operator\">&#x3C;</span><span class=\"token operator\">/</span>div<span class=\"token operator\">></span>\n   <span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span> <span class=\"token punctuation\">}</span>  \n<span class=\"token keyword\">export</span> <span class=\"token keyword\">default</span> App<span class=\"token punctuation\">;</span></code></pre></div><p>Here’s what we’re doing:</p><ol><li><strong><strong>Line 1:</strong></strong> We added an import for <code class=\"language-text\">useEffect</code></li><li><strong><strong>Line 4: </strong></strong>We changed our class component into a function component</li><li><strong><strong>Line 5–10: </strong></strong>we added an effect to our function component. This effect sets a timer after 3 seconds that will change the text in the element with the id <code class=\"language-text\">welcomeMessage</code><strong><strong>. </strong></strong>Because we passed an <strong><strong>empty</strong></strong> array to <code class=\"language-text\">useEffect</code>, this effect will only run once.</li><li><strong><strong>Line 11–17: </strong></strong>We replaced the previous code in <strong><strong>App.js </strong></strong>to render an <strong><strong>h1 </strong></strong>element having the id <code class=\"language-text\">welcomeMessage</code><strong><strong>, </strong></strong>which is our target element.</li></ol><p>Okay, now go to our web page and see the changes. At first, the welcome message “<strong><strong>Welcome to Numbers Trivia!</strong></strong>” will show up, then 3 seconds later it will change into “<strong><strong>Try Out Our Trivia Generator!</strong></strong>” We just used a React Hook!</p><p>Next, let’s create our form input elements. To do that, we will create a new React component called <code class=\"language-text\">Form</code><strong><strong>. </strong></strong>Create in the directory <strong><strong>components</strong></strong> the file <strong><strong>Form.js. </strong></strong>For now, it will just include the following:</p><div class=\"kg-card kg-code-card gatsby-highlight\" data-language=\"js\"><pre class=\"language-js\"><code class=\"language-js\"><span class=\"token keyword\">import</span> React <span class=\"token keyword\">from</span> <span class=\"token string\">'react'</span><span class=\"token punctuation\">;</span>  \n<span class=\"token keyword\">function</span> <span class=\"token function\">Form</span><span class=\"token punctuation\">(</span><span class=\"token parameter\">props</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">{</span>\n<span class=\"token punctuation\">}</span>\n<span class=\"token keyword\">export</span> <span class=\"token keyword\">default</span> Form<span class=\"token punctuation\">;</span></code></pre></div><p>This will create the new React component. We’re just importing React, then we’re creating a function called Form. As we said earlier in the tutorial, with the use of hooks we can now create components as stateful functions rather than classes. And in the last line, we’re exporting <strong><strong>Form</strong></strong> in order to import it in other files.</p><p>In the form, we will have a <strong><strong>text </strong></strong>input and <strong><strong>select </strong></strong>elements. This is based on the API we’re using. In the API, two parameters can be sent:</p><ol><li><strong><strong>number: </strong></strong>the number you want to get the trivia for. It can be an <strong><strong>integer</strong></strong>, a date of the form <strong><strong>month/day, </strong></strong>or the keyword <strong><strong>random </strong></strong>which will retrieve facts about a random number.</li><li><strong><strong>type: </strong></strong>the type of information you want to get. There are a few types: <strong><strong>math, date, year, </strong></strong>or, the default, <strong><strong>trivia.</strong></strong></li></ol><p>We will consider the text input element as optional. If the user does not enter a number or a date, we will send the keyword <strong><strong>random</strong></strong> for the number element.</p><p>Let’s add the following code inside the <strong><strong>Form</strong></strong> function in order to render our form:</p><div class=\"kg-card kg-code-card gatsby-highlight\" data-language=\"js\"><pre class=\"language-js\"><code class=\"language-js\"><span class=\"token keyword\">function</span> <span class=\"token function\">Form</span><span class=\"token punctuation\">(</span><span class=\"token parameter\">props</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">{</span>\n  <span class=\"token keyword\">return</span> <span class=\"token punctuation\">(</span><span class=\"token operator\">&#x3C;</span>form<span class=\"token operator\">></span>\n             <span class=\"token operator\">&#x3C;</span>div<span class=\"token operator\">></span>\n              <span class=\"token operator\">&#x3C;</span>input type<span class=\"token operator\">=</span><span class=\"token string\">\"text\"</span> name<span class=\"token operator\">=</span><span class=\"token string\">\"number\"</span> placeholder<span class=\"token operator\">=</span><span class=\"token string\">\"Enter a number (Optional)\"</span> <span class=\"token operator\">/</span><span class=\"token operator\">></span>\n             <span class=\"token operator\">&#x3C;</span><span class=\"token operator\">/</span>div<span class=\"token operator\">></span>\n             <span class=\"token operator\">&#x3C;</span>div<span class=\"token operator\">></span>\n              <span class=\"token operator\">&#x3C;</span>select name<span class=\"token operator\">=</span><span class=\"token string\">\"type\"</span><span class=\"token operator\">></span>\n               <span class=\"token operator\">&#x3C;</span>option value<span class=\"token operator\">=</span><span class=\"token string\">\"trivia\"</span><span class=\"token operator\">></span>Trivia<span class=\"token operator\">&#x3C;</span><span class=\"token operator\">/</span>option<span class=\"token operator\">></span>\n               <span class=\"token operator\">&#x3C;</span>option value<span class=\"token operator\">=</span><span class=\"token string\">\"math\"</span><span class=\"token operator\">></span>Math<span class=\"token operator\">&#x3C;</span><span class=\"token operator\">/</span>option<span class=\"token operator\">></span>\n               <span class=\"token operator\">&#x3C;</span>option value<span class=\"token operator\">=</span><span class=\"token string\">\"date\"</span><span class=\"token operator\">></span>Date<span class=\"token operator\">&#x3C;</span><span class=\"token operator\">/</span>option<span class=\"token operator\">></span>\n               <span class=\"token operator\">&#x3C;</span>option value<span class=\"token operator\">=</span><span class=\"token string\">\"year\"</span><span class=\"token operator\">></span>Year<span class=\"token operator\">&#x3C;</span><span class=\"token operator\">/</span>option<span class=\"token operator\">></span>\n              <span class=\"token operator\">&#x3C;</span><span class=\"token operator\">/</span>select<span class=\"token operator\">></span>\n             <span class=\"token operator\">&#x3C;</span><span class=\"token operator\">/</span>div<span class=\"token operator\">></span>\n             <span class=\"token operator\">&#x3C;</span>button type<span class=\"token operator\">=</span><span class=\"token string\">\"submit\"</span><span class=\"token operator\">></span>Generate<span class=\"token operator\">&#x3C;</span><span class=\"token operator\">/</span>button<span class=\"token operator\">></span>\n            <span class=\"token operator\">&#x3C;</span><span class=\"token operator\">/</span>form<span class=\"token operator\">></span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span> \n<span class=\"token punctuation\">}</span></code></pre></div><p>This will create the form with the <strong><strong>text </strong></strong>input and <strong><strong>select </strong></strong>and <strong><strong>button </strong></strong>elements.</p><p>After that, we need to import and render the <strong><strong>Form</strong></strong> component in our <strong><strong>App </strong></strong>component:</p><div class=\"kg-card kg-code-card gatsby-highlight\" data-language=\"js\"><pre class=\"language-js\"><code class=\"language-js\"><span class=\"token keyword\">import</span> React<span class=\"token punctuation\">,</span> <span class=\"token punctuation\">{</span>useEffect<span class=\"token punctuation\">}</span> <span class=\"token keyword\">from</span> <span class=\"token string\">'react'</span><span class=\"token punctuation\">;</span>\n<span class=\"token keyword\">import</span> Form <span class=\"token keyword\">from</span> <span class=\"token string\">'./Form'</span><span class=\"token punctuation\">;</span>\n<span class=\"token keyword\">import</span> <span class=\"token string\">'../styles/App.css'</span><span class=\"token punctuation\">;</span>  \n<span class=\"token keyword\">function</span> <span class=\"token function\">App</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n   <span class=\"token function\">useEffect</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">=></span> <span class=\"token punctuation\">{</span>\n     <span class=\"token function\">setTimeout</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">=></span> <span class=\"token punctuation\">{</span>\n       <span class=\"token keyword\">let</span> welcomeMessage <span class=\"token operator\">=</span> document<span class=\"token punctuation\">.</span><span class=\"token function\">getElementById</span><span class=\"token punctuation\">(</span><span class=\"token string\">\"welcomeMessage\"</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n       welcomeMessage<span class=\"token punctuation\">.</span>innerHTML <span class=\"token operator\">=</span> <span class=\"token string\">\"Try Out Our Trivia Generator!\"</span><span class=\"token punctuation\">;</span>\n     <span class=\"token punctuation\">}</span><span class=\"token punctuation\">,</span> <span class=\"token number\">3000</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n   <span class=\"token punctuation\">}</span><span class=\"token punctuation\">,</span> <span class=\"token punctuation\">[</span><span class=\"token punctuation\">]</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n   <span class=\"token keyword\">return</span> <span class=\"token punctuation\">(</span>\n     <span class=\"token operator\">&#x3C;</span>div className<span class=\"token operator\">=</span><span class=\"token string\">\"App\"</span><span class=\"token operator\">></span>\n       <span class=\"token operator\">&#x3C;</span>header className<span class=\"token operator\">=</span><span class=\"token string\">\"App-header\"</span><span class=\"token operator\">></span>\n         <span class=\"token operator\">&#x3C;</span>h1 id<span class=\"token operator\">=</span><span class=\"token string\">\"welcomeMessage\"</span><span class=\"token operator\">></span>Welcome to Numbers Trivia<span class=\"token operator\">!</span><span class=\"token operator\">&#x3C;</span><span class=\"token operator\">/</span>h1<span class=\"token operator\">></span>\n         <span class=\"token operator\">&#x3C;</span>Form <span class=\"token operator\">/</span><span class=\"token operator\">></span>\n       <span class=\"token operator\">&#x3C;</span><span class=\"token operator\">/</span>header<span class=\"token operator\">></span>\n     <span class=\"token operator\">&#x3C;</span><span class=\"token operator\">/</span>div<span class=\"token operator\">></span>\n   <span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n <span class=\"token punctuation\">}</span>\n<span class=\"token keyword\">export</span> <span class=\"token keyword\">default</span> App<span class=\"token punctuation\">;</span></code></pre></div><p>We have changed the imports to import our <code class=\"language-text\">Form</code><strong><strong> </strong></strong>component, and we added <code class=\"language-text\">&#x3C;Form /></code><strong><strong> </strong></strong>to render the form.</p><p>Let’s also add more styles just to make our form look a little better. Add the following at the end of <strong><strong>App.css</strong></strong>:</p><div class=\"kg-card kg-code-card gatsby-highlight\" data-language=\"css\"><pre class=\"language-css\"><code class=\"language-css\"><span class=\"token selector\">form</span> <span class=\"token punctuation\">{</span>\n   <span class=\"token property\">font-size</span><span class=\"token punctuation\">:</span> 15px<span class=\"token punctuation\">;</span>\n<span class=\"token punctuation\">}</span>  \n<span class=\"token selector\">form input, form select</span> <span class=\"token punctuation\">{</span>\n   <span class=\"token property\">padding</span><span class=\"token punctuation\">:</span> 5px<span class=\"token punctuation\">;</span>\n<span class=\"token punctuation\">}</span>  \n<span class=\"token selector\">form select</span> <span class=\"token punctuation\">{</span>\n   <span class=\"token property\">width</span><span class=\"token punctuation\">:</span> 100%<span class=\"token punctuation\">;</span> \n<span class=\"token punctuation\">}</span>  \n<span class=\"token selector\">form div</span> <span class=\"token punctuation\">{</span>\n   <span class=\"token property\">margin-bottom</span><span class=\"token punctuation\">:</span> 8px<span class=\"token punctuation\">;</span>\n<span class=\"token punctuation\">}</span></code></pre></div><p>If you refresh the page now, you will find it has changed and now it’s showing our form.</p><p>Now, we need to add some logic to our form. <strong><strong>On Submit</strong></strong>, we need to get the values of our input elements, then call the API to retrieve the results.</p><p>To handle form elements and their values, you need to use the state of the component. You make the value of the element equal to a property in the state of the component.</p><p>Before hooks, in order to get the value in the state you would have to use <code class=\"language-text\">this.state.value</code><strong><strong>, </strong></strong>and then to set the state, you will need to call <code class=\"language-text\">this.setState</code>.</p><p>Now, you can use the state hook. The state hook is the <code class=\"language-text\">useState</code><strong><strong> </strong></strong>function. It accepts one argument, which is the initial value, and it returns a pair of values: the <strong><strong>current state</strong></strong> and a <strong><strong>function</strong></strong> that updates it. Then, you will be able to access the current state using the first returned value, and update it using the second returned value which is the function.</p><p>Here’s an example:</p><div class=\"kg-card kg-code-card gatsby-highlight\" data-language=\"js\"><pre class=\"language-js\"><code class=\"language-js\"><span class=\"token keyword\">const</span> <span class=\"token punctuation\">[</span>count<span class=\"token punctuation\">,</span> setCount<span class=\"token punctuation\">]</span> <span class=\"token operator\">=</span> <span class=\"token function\">useState</span><span class=\"token punctuation\">(</span><span class=\"token number\">0</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span></code></pre></div><p>in this example, we call the <code class=\"language-text\">useState</code><strong><strong> </strong></strong>hook and pass it 0, and we set the returned value equal to <code class=\"language-text\">count</code> and <code class=\"language-text\">setCount</code>. This means that we have created state variable called <code class=\"language-text\">count</code>. Its initial value is 0, and to change its value we can use <code class=\"language-text\">setCount</code>.</p><p>For our <code class=\"language-text\">Form</code><strong><strong> </strong></strong>component, we need two state variables, one for the text<strong><strong> </strong></strong>input which we will call <code class=\"language-text\">number</code>, and one for the select<strong><strong> </strong></strong>input which we will call <code class=\"language-text\">type</code>. Then, on change event for these two input elements, we will change the values for <code class=\"language-text\">number</code><strong><strong> </strong></strong>and <code class=\"language-text\">type</code> using the function returned by <code class=\"language-text\">setState</code><strong><strong>.</strong></strong></p><p>Open our <code class=\"language-text\">Form</code><strong><strong> </strong></strong>component and change it to the following:</p><div class=\"kg-card kg-code-card gatsby-highlight\" data-language=\"js\"><pre class=\"language-js\"><code class=\"language-js\"><span class=\"token keyword\">import</span> React<span class=\"token punctuation\">,</span> <span class=\"token punctuation\">{</span> useState <span class=\"token punctuation\">}</span> <span class=\"token keyword\">from</span> <span class=\"token string\">'react'</span><span class=\"token punctuation\">;</span>\n<span class=\"token keyword\">function</span> <span class=\"token function\">Form</span><span class=\"token punctuation\">(</span><span class=\"token parameter\">props</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">{</span>\n  <span class=\"token keyword\">let</span> <span class=\"token punctuation\">[</span>number<span class=\"token punctuation\">,</span> setNumber<span class=\"token punctuation\">]</span> <span class=\"token operator\">=</span> <span class=\"token function\">useState</span><span class=\"token punctuation\">(</span><span class=\"token string\">\"random\"</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  <span class=\"token keyword\">let</span> <span class=\"token punctuation\">[</span>type<span class=\"token punctuation\">,</span> setType<span class=\"token punctuation\">]</span> <span class=\"token operator\">=</span> <span class=\"token function\">useState</span><span class=\"token punctuation\">(</span><span class=\"token string\">\"trivia\"</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  <span class=\"token keyword\">function</span> <span class=\"token function\">onNumberChanged</span><span class=\"token punctuation\">(</span><span class=\"token parameter\">e</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">{</span>\n    <span class=\"token keyword\">let</span> value <span class=\"token operator\">=</span> e<span class=\"token punctuation\">.</span>target<span class=\"token punctuation\">.</span>value<span class=\"token punctuation\">.</span><span class=\"token function\">trim</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n    <span class=\"token keyword\">if</span><span class=\"token punctuation\">(</span><span class=\"token operator\">!</span>value<span class=\"token punctuation\">.</span>length<span class=\"token punctuation\">)</span><span class=\"token punctuation\">{</span>\n      <span class=\"token function\">setNumber</span><span class=\"token punctuation\">(</span><span class=\"token string\">\"random\"</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span> <span class=\"token comment\">//default value</span>\n    <span class=\"token punctuation\">}</span> <span class=\"token keyword\">else</span> <span class=\"token punctuation\">{</span>\n      <span class=\"token function\">setNumber</span><span class=\"token punctuation\">(</span>value<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n    <span class=\"token punctuation\">}</span>\n  <span class=\"token punctuation\">}</span>\n  <span class=\"token keyword\">function</span> <span class=\"token function\">onTypeChanged</span><span class=\"token punctuation\">(</span><span class=\"token parameter\">e</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">{</span>\n    <span class=\"token keyword\">let</span> value <span class=\"token operator\">=</span> e<span class=\"token punctuation\">.</span>target<span class=\"token punctuation\">.</span>value<span class=\"token punctuation\">.</span><span class=\"token function\">trim</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n    <span class=\"token keyword\">if</span><span class=\"token punctuation\">(</span><span class=\"token operator\">!</span>value<span class=\"token punctuation\">.</span>length<span class=\"token punctuation\">)</span><span class=\"token punctuation\">{</span>\n      <span class=\"token function\">setType</span><span class=\"token punctuation\">(</span><span class=\"token string\">\"trivia\"</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span> <span class=\"token comment\">//default value</span>\n    <span class=\"token punctuation\">}</span> <span class=\"token keyword\">else</span> <span class=\"token punctuation\">{</span>\n      <span class=\"token function\">setType</span><span class=\"token punctuation\">(</span>value<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n    <span class=\"token punctuation\">}</span>\n  <span class=\"token punctuation\">}</span>\n  <span class=\"token keyword\">return</span> <span class=\"token punctuation\">(</span><span class=\"token operator\">&#x3C;</span>form<span class=\"token operator\">></span>\n            <span class=\"token operator\">&#x3C;</span>div<span class=\"token operator\">></span>\n              <span class=\"token operator\">&#x3C;</span>input type<span class=\"token operator\">=</span><span class=\"token string\">\"text\"</span> name<span class=\"token operator\">=</span><span class=\"token string\">\"number\"</span> placeholder<span class=\"token operator\">=</span><span class=\"token string\">\"Enter a number (Optional)\"</span> value<span class=\"token operator\">=</span><span class=\"token punctuation\">{</span>number<span class=\"token punctuation\">}</span> onChange<span class=\"token operator\">=</span><span class=\"token punctuation\">{</span>onNumberChanged<span class=\"token punctuation\">}</span> <span class=\"token operator\">/</span><span class=\"token operator\">></span>\n            <span class=\"token operator\">&#x3C;</span><span class=\"token operator\">/</span>div<span class=\"token operator\">></span>\n            <span class=\"token operator\">&#x3C;</span>div<span class=\"token operator\">></span>\n              <span class=\"token operator\">&#x3C;</span>select name<span class=\"token operator\">=</span><span class=\"token string\">\"type\"</span> value<span class=\"token operator\">=</span><span class=\"token punctuation\">{</span>type<span class=\"token punctuation\">}</span> onChange<span class=\"token operator\">=</span><span class=\"token punctuation\">{</span>onTypeChanged<span class=\"token punctuation\">}</span><span class=\"token operator\">></span>\n                <span class=\"token operator\">&#x3C;</span>option value<span class=\"token operator\">=</span><span class=\"token string\">\"trivia\"</span><span class=\"token operator\">></span>Trivia<span class=\"token operator\">&#x3C;</span><span class=\"token operator\">/</span>option<span class=\"token operator\">></span>\n                <span class=\"token operator\">&#x3C;</span>option value<span class=\"token operator\">=</span><span class=\"token string\">\"math\"</span><span class=\"token operator\">></span>Math<span class=\"token operator\">&#x3C;</span><span class=\"token operator\">/</span>option<span class=\"token operator\">></span>\n                <span class=\"token operator\">&#x3C;</span>option value<span class=\"token operator\">=</span><span class=\"token string\">\"date\"</span><span class=\"token operator\">></span>Date<span class=\"token operator\">&#x3C;</span><span class=\"token operator\">/</span>option<span class=\"token operator\">></span>\n                <span class=\"token operator\">&#x3C;</span>option value<span class=\"token operator\">=</span><span class=\"token string\">\"year\"</span><span class=\"token operator\">></span>Year<span class=\"token operator\">&#x3C;</span><span class=\"token operator\">/</span>option<span class=\"token operator\">></span>\n              <span class=\"token operator\">&#x3C;</span><span class=\"token operator\">/</span>select<span class=\"token operator\">></span>\n            <span class=\"token operator\">&#x3C;</span><span class=\"token operator\">/</span>div<span class=\"token operator\">></span>\n            <span class=\"token operator\">&#x3C;</span>button type<span class=\"token operator\">=</span><span class=\"token string\">\"submit\"</span><span class=\"token operator\">></span>Generate<span class=\"token operator\">&#x3C;</span><span class=\"token operator\">/</span>button<span class=\"token operator\">></span>\n<span class=\"token operator\">&#x3C;</span><span class=\"token operator\">/</span>form<span class=\"token operator\">></span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n<span class=\"token punctuation\">}</span>\n<span class=\"token keyword\">export</span> <span class=\"token keyword\">default</span> Form<span class=\"token punctuation\">;</span></code></pre></div><ol><li><strong><strong>Line 1: </strong></strong>add an import for <code class=\"language-text\">useState</code> hook.</li><li><strong><strong>Line 3–4: </strong></strong>create two state variables <code class=\"language-text\">number</code> and <code class=\"language-text\">type</code> using <code class=\"language-text\">useState</code> . Here we pass <strong><strong>random </strong></strong>as the initial value for number, and <strong><strong>trivia </strong></strong>as initial value for type because they are the default values for the parameters in the API.</li><li><strong><strong>Line 5–10: </strong></strong>implement input change handler functions for both text and select inputs, where we change the value of the state variables using the functions returned by <code class=\"language-text\">useState</code> . If the value is unset, we automatically change the values to the default value.</li><li><strong><strong>Line 13: </strong></strong>pass the <code class=\"language-text\">onNumberChanged</code> function to <code class=\"language-text\">onChange</code> event for text input.</li><li><strong><strong>Line 16: </strong></strong>pass the <code class=\"language-text\">onTypeChanged</code> function to <code class=\"language-text\">onChange</code> event for select input.</li></ol><p>In addition, let’s go back to our <code class=\"language-text\">App</code> component to modify it and use states. Instead of modifying our welcome message by changing the <code class=\"language-text\">innerHTML</code> of the element, we will use a state. Our App component should now be like this:</p><div class=\"kg-card kg-code-card gatsby-highlight\" data-language=\"text\"><pre class=\"language-text\"><code class=\"language-text\">import React, {useEffect, useState} from 'react'; \nimport Form from './Form'; \nimport '../styles/App.css'; \n \nfunction App() {\n  const [ welcomeMessage, setWelcomeMessage ] = useState(\n    \"Welcome to Numbers Trivia!\",\n  );\nuseEffect(() => {\n    setTimeout(() => {\n      setWelcomeMessage(\"Try Out Our Trivia Generator!\");\n    }, 3000);\n  }, []);\nreturn (\n    &#x3C;div className=\"App\">\n      &#x3C;header className=\"App-header\">\n        &#x3C;h1>{welcomeMessage}&#x3C;/h1>\n      &#x3C;/header>\n      &#x3C;Form/>\n    &#x3C;/div>\n  );\n}\nexport default App;</code></pre></div><p>Now, we are using <code class=\"language-text\">useState</code> to declare and initialize our welcome message. It will return <code class=\"language-text\">welcomeMessage</code> , our state variable, and <code class=\"language-text\">setWelcomeMessage</code> , which we will use to change the value of <code class=\"language-text\">welcomeMessage</code> after 3 seconds from “<strong><strong>Welcome to Numbers Trivia!</strong></strong>” to “<strong><strong>Try Out Our Trivia Generator!</strong></strong>”</p><p>What’s left now is to add a function to handle the form’s <code class=\"language-text\">onSubmit</code> event. On submit, we will send a request to the API with our parameters, then display the result.</p><p>In order to perform the request, we need to install <a href=\"https://github.com/axios/axios\" rel=\"noopener nofollow\">axios</a>:</p><div class=\"kg-card kg-code-card gatsby-highlight\" data-language=\"shell\"><pre class=\"language-shell\"><code class=\"language-shell\"><span class=\"token function\">npm</span> i axios</code></pre></div><p>Then, require axios at the beginning of <strong><strong>Form.js:</strong></strong></p><div class=\"kg-card kg-code-card gatsby-highlight\" data-language=\"js\"><pre class=\"language-js\"><code class=\"language-js\"><span class=\"token keyword\">const</span> axios <span class=\"token operator\">=</span> <span class=\"token function\">require</span><span class=\"token punctuation\">(</span><span class=\"token string\">'axios'</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span></code></pre></div><p>Now, add the following function below <code class=\"language-text\">onTypeChanged</code> in our <strong><strong>Form</strong></strong> component:</p><div class=\"kg-card kg-code-card gatsby-highlight\" data-language=\"js\"><pre class=\"language-js\"><code class=\"language-js\"><span class=\"token keyword\">function</span> <span class=\"token function\">onSubmit</span><span class=\"token punctuation\">(</span><span class=\"token parameter\">e</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">{</span>\n  e<span class=\"token punctuation\">.</span><span class=\"token function\">preventDefault</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  axios<span class=\"token punctuation\">.</span><span class=\"token function\">get</span><span class=\"token punctuation\">(</span><span class=\"token string\">'http://numbersapi.com/'</span> <span class=\"token operator\">+</span> number <span class=\"token operator\">+</span> <span class=\"token string\">'/'</span> <span class=\"token operator\">+</span> type<span class=\"token punctuation\">)</span>\n       <span class=\"token punctuation\">.</span><span class=\"token function\">then</span><span class=\"token punctuation\">(</span><span class=\"token keyword\">function</span><span class=\"token punctuation\">(</span><span class=\"token parameter\">response</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">{</span>\n         <span class=\"token keyword\">let</span> elm <span class=\"token operator\">=</span> document<span class=\"token punctuation\">.</span><span class=\"token function\">getElementById</span><span class=\"token punctuation\">(</span><span class=\"token string\">'result'</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n         elm<span class=\"token punctuation\">.</span>innerHTML <span class=\"token operator\">=</span> response<span class=\"token punctuation\">.</span>data<span class=\"token punctuation\">;</span>\n       <span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">.</span><span class=\"token function\">catch</span><span class=\"token punctuation\">(</span><span class=\"token keyword\">function</span><span class=\"token punctuation\">(</span><span class=\"token parameter\">e</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">{</span>\n         console<span class=\"token punctuation\">.</span><span class=\"token function\">log</span><span class=\"token punctuation\">(</span><span class=\"token string\">\"error\"</span><span class=\"token punctuation\">,</span> e<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span> <span class=\"token comment\">//simple error handling</span>\n       <span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n<span class=\"token punctuation\">}</span></code></pre></div><p>We’re just performing a request to the API, passing the <code class=\"language-text\">number</code> and <code class=\"language-text\">type</code> then displaying the result in an element of id <code class=\"language-text\">result</code> (which we will add in a minute). In case of an error, we’re just displaying it in the console just as a simple error handling.</p><p>Now, let’s add this function as the handler for the form <code class=\"language-text\">onSubmit</code> event in the return statement in Form.js:</p><div class=\"kg-card kg-code-card gatsby-highlight\" data-language=\"jsx\"><pre class=\"language-jsx\"><code class=\"language-jsx\"><span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&#x3C;</span>form</span> <span class=\"token attr-name\">onSubmit</span><span class=\"token script language-javascript\"><span class=\"token script-punctuation punctuation\">=</span><span class=\"token punctuation\">{</span>onSubmit<span class=\"token punctuation\">}</span></span><span class=\"token punctuation\">></span></span></code></pre></div><p>The only thing left is to add the <code class=\"language-text\">#result</code> element. We can add it in <strong><strong>App.js </strong></strong>before <code class=\"language-text\">&#x3C;Form /></code> :</p><div class=\"kg-card kg-code-card gatsby-highlight\" data-language=\"jsx\"><pre class=\"language-jsx\"><code class=\"language-jsx\"><span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&#x3C;</span>div</span> <span class=\"token attr-name\">id</span><span class=\"token attr-value\"><span class=\"token punctuation attr-equals\">=</span><span class=\"token punctuation\">\"</span>result<span class=\"token punctuation\">\"</span></span> <span class=\"token attr-name\">style</span><span class=\"token script language-javascript\"><span class=\"token script-punctuation punctuation\">=</span><span class=\"token punctuation\">{</span><span class=\"token punctuation\">{</span><span class=\"token literal-property property\">marginBottom</span><span class=\"token operator\">:</span> <span class=\"token string\">'15px'</span><span class=\"token punctuation\">}</span><span class=\"token punctuation\">}</span></span><span class=\"token punctuation\">></span></span><span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&#x3C;/</span>div</span><span class=\"token punctuation\">></span></span></code></pre></div><p>Alright, now go to your website and discover all new trivia about numbers!</p><hr><h2 id=\"conclusion\">Conclusion</h2><p>You just created a React app and used some React Hooks! Of course, there’s still more to explore and learn, so be sure to head to the <a href=\"https://reactjs.org/docs/hooks-intro.html\" rel=\"noopener nofollow\">docs</a> to learn more about React Hooks.</p>","htmlAst":{"type":"root","children":[{"type":"element","tagName":"p","properties":{},"children":[{"type":"text","value":"Hooks have been officially added to React as of React 16.8. One of the main benefits of Hooks in React is that you can use state and effect (which is generally a combination of different life cycles into one) in a component without creating a class."}]},{"type":"element","tagName":"p","properties":{},"children":[{"type":"text","value":"In this tutorial, you’ll learn how to exactly use hooks in React. We will build a simple website that gives the user some options to pick from in order to generate some random number trivia."}]},{"type":"element","tagName":"p","properties":{},"children":[{"type":"text","value":"You can check out the demo for the website "},{"type":"element","tagName":"a","properties":{"href":"http://numbers-trivia.surge.sh/","rel":["noopener","nofollow"]},"children":[{"type":"text","value":"here"}]},{"type":"text","value":", and the source code on the "},{"type":"element","tagName":"a","properties":{"href":"https://github.com/shahednasser/numbers-trivia","rel":["noopener","nofollow"]},"children":[{"type":"text","value":"GitHub repository"}]},{"type":"text","value":"."}]},{"type":"element","tagName":"p","properties":{},"children":[{"type":"text","value":"Let’s first start by creating a react app. Since our website will be very simple, we will use the "},{"type":"element","tagName":"code","properties":{"className":["language-text"]},"children":[{"type":"text","value":"create-react-app"}]},{"type":"text","value":" command. If you don’t have it installed, you can install it using "},{"type":"element","tagName":"code","properties":{"className":["language-text"]},"children":[{"type":"text","value":"npm"}]},{"type":"element","tagName":"strong","properties":{},"children":[{"type":"element","tagName":"strong","properties":{},"children":[{"type":"text","value":"."}]}]}]},{"type":"element","tagName":"p","properties":{},"children":[{"type":"text","value":"Run the following in your terminal or CMD:"}]},{"type":"element","tagName":"div","properties":{"className":["kg-card","kg-code-card","gatsby-highlight"],"dataLanguage":"shell"},"children":[{"type":"element","tagName":"pre","properties":{"className":["language-shell"]},"children":[{"type":"element","tagName":"code","properties":{"className":["language-shell"]},"children":[{"type":"element","tagName":"span","properties":{"className":["token","function"]},"children":[{"type":"text","value":"npm"}]},{"type":"text","value":" i -g create-react-app"}]}]}]},{"type":"element","tagName":"p","properties":{},"children":[{"type":"text","value":"This will install "},{"type":"element","tagName":"code","properties":{"className":["language-text"]},"children":[{"type":"text","value":"create-react-app"}]},{"type":"text","value":" globally."}]},{"type":"element","tagName":"p","properties":{},"children":[{"type":"text","value":"Now we can create our React app:"}]},{"type":"element","tagName":"div","properties":{"className":["kg-card","kg-code-card","gatsby-highlight"],"dataLanguage":"shell"},"children":[{"type":"element","tagName":"pre","properties":{"className":["language-shell"]},"children":[{"type":"element","tagName":"code","properties":{"className":["language-shell"]},"children":[{"type":"text","value":"create-react-app numbers-trivia"}]}]}]},{"type":"element","tagName":"p","properties":{},"children":[{"type":"text","value":"Running this command will create a directory in the working directory with the name you supply for the React app. I named it "},{"type":"element","tagName":"strong","properties":{},"children":[{"type":"element","tagName":"strong","properties":{},"children":[{"type":"text","value":"numbers-trivia"}]}]},{"type":"text","value":" but you can call it whatever you want."}]},{"type":"element","tagName":"p","properties":{},"children":[{"type":"text","value":"Inside that directory, it will also install all the packages and files needed to run the website. It will install packages like "},{"type":"element","tagName":"strong","properties":{},"children":[{"type":"element","tagName":"strong","properties":{},"children":[{"type":"text","value":"react"}]}]},{"type":"text","value":", "},{"type":"element","tagName":"strong","properties":{},"children":[{"type":"element","tagName":"strong","properties":{},"children":[{"type":"text","value":"react-dom"}]}]},{"type":"text","value":", "},{"type":"element","tagName":"strong","properties":{},"children":[{"type":"element","tagName":"strong","properties":{},"children":[{"type":"text","value":"react-scripts"}]}]},{"type":"text","value":" and more."}]},{"type":"element","tagName":"p","properties":{},"children":[{"type":"text","value":"Once it’s done, change into the newly created directory and start the server:"}]},{"type":"element","tagName":"div","properties":{"className":["kg-card","kg-code-card","gatsby-highlight"],"dataLanguage":"shell"},"children":[{"type":"element","tagName":"pre","properties":{"className":["language-shell"]},"children":[{"type":"element","tagName":"code","properties":{"className":["language-shell"]},"children":[{"type":"element","tagName":"span","properties":{"className":["token","builtin","class-name"]},"children":[{"type":"text","value":"cd"}]},{"type":"text","value":" numbers-trivia\n"},{"type":"element","tagName":"span","properties":{"className":["token","function"]},"children":[{"type":"text","value":"npm"}]},{"type":"text","value":" start"}]}]}]},{"type":"element","tagName":"p","properties":{},"children":[{"type":"text","value":"Once you start the server, a web page of your website in your favorite browser will open. You will see a page with just the logo and a link to learn React."}]},{"type":"element","tagName":"p","properties":{},"children":[{"type":"text","value":"Before we start changing the page, if you are not familiar with React, let’s take a look at the structure of our React app. It should look something like this:"}]},{"type":"element","tagName":"figure","properties":{"className":["kg-card","kg-image-card"]},"children":[{"type":"element","tagName":"img","properties":{"src":"https://res-3.cloudinary.com/hbqmf3mbz/image/upload/q_auto/v1/ghost-blog-images/1_R1irPlPj9UsboCM_KI5CgA.png","className":["kg-image"],"alt":"","loading":"lazy"},"children":[]}]},{"type":"element","tagName":"p","properties":{},"children":[{"type":"text","value":"In our root directory, we will find a bunch of directories. "},{"type":"element","tagName":"strong","properties":{},"children":[{"type":"element","tagName":"strong","properties":{},"children":[{"type":"text","value":"node_modules "}]}]},{"type":"text","value":"holds all the packages React needs to run and any package you might add. "},{"type":"element","tagName":"strong","properties":{},"children":[{"type":"element","tagName":"strong","properties":{},"children":[{"type":"text","value":"public "}]}]},{"type":"text","value":"holds the files our server serves."}]},{"type":"element","tagName":"p","properties":{},"children":[{"type":"text","value":"The directory that we will spend most of our time in is the "},{"type":"element","tagName":"strong","properties":{},"children":[{"type":"element","tagName":"strong","properties":{},"children":[{"type":"text","value":"src"}]}]},{"type":"text","value":" directory. This directory will hold all of our React components."}]},{"type":"element","tagName":"p","properties":{},"children":[{"type":"text","value":"As you can see there are a bunch of files in that directory. "},{"type":"element","tagName":"strong","properties":{},"children":[{"type":"element","tagName":"strong","properties":{},"children":[{"type":"text","value":"index.js"}]}]},{"type":"text","value":" is basically the file that renders our highest React component."}]},{"type":"element","tagName":"p","properties":{},"children":[{"type":"text","value":"We only have one React component right now which is in "},{"type":"element","tagName":"strong","properties":{},"children":[{"type":"element","tagName":"strong","properties":{},"children":[{"type":"text","value":"App.js. "}]}]},{"type":"text","value":"If you open it, you will find the code that renders the logo with the link to learn React that you currently see on the website."}]},{"type":"element","tagName":"p","properties":{},"children":[{"type":"text","value":"So, in order to see how changing our App component will change the content of the website, let’s modify the code by removing the current content and replacing it with our favorite phrase: “Hello, World!”"}]},{"type":"element","tagName":"div","properties":{"className":["kg-card","kg-code-card","gatsby-highlight"],"dataLanguage":"js"},"children":[{"type":"element","tagName":"pre","properties":{"className":["language-js"]},"children":[{"type":"element","tagName":"code","properties":{"className":["language-js"]},"children":[{"type":"element","tagName":"span","properties":{"className":["token","keyword"]},"children":[{"type":"text","value":"import"}]},{"type":"text","value":" React"},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":","}]},{"type":"text","value":" "},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"{"}]},{"type":"text","value":" Component "},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"}"}]},{"type":"text","value":" "},{"type":"element","tagName":"span","properties":{"className":["token","keyword"]},"children":[{"type":"text","value":"from"}]},{"type":"text","value":" "},{"type":"element","tagName":"span","properties":{"className":["token","string"]},"children":[{"type":"text","value":"'react'"}]},{"type":"text","value":" \n"},{"type":"element","tagName":"span","properties":{"className":["token","keyword"]},"children":[{"type":"text","value":"import"}]},{"type":"text","value":" logo "},{"type":"element","tagName":"span","properties":{"className":["token","keyword"]},"children":[{"type":"text","value":"from"}]},{"type":"text","value":" "},{"type":"element","tagName":"span","properties":{"className":["token","string"]},"children":[{"type":"text","value":"'./logo.svg'"}]},{"type":"text","value":" \n"},{"type":"element","tagName":"span","properties":{"className":["token","keyword"]},"children":[{"type":"text","value":"import"}]},{"type":"text","value":" "},{"type":"element","tagName":"span","properties":{"className":["token","string"]},"children":[{"type":"text","value":"'./App.css'"}]},{"type":"text","value":"  \n"},{"type":"element","tagName":"span","properties":{"className":["token","keyword"]},"children":[{"type":"text","value":"class"}]},{"type":"text","value":" "},{"type":"element","tagName":"span","properties":{"className":["token","class-name"]},"children":[{"type":"text","value":"App"}]},{"type":"text","value":" "},{"type":"element","tagName":"span","properties":{"className":["token","keyword"]},"children":[{"type":"text","value":"extends"}]},{"type":"text","value":" "},{"type":"element","tagName":"span","properties":{"className":["token","class-name"]},"children":[{"type":"text","value":"Component"}]},{"type":"text","value":" "},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"{"}]},{"type":"text","value":"\n   "},{"type":"element","tagName":"span","properties":{"className":["token","function"]},"children":[{"type":"text","value":"render"}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"("}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":")"}]},{"type":"text","value":" "},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"{"}]},{"type":"text","value":"\n     "},{"type":"element","tagName":"span","properties":{"className":["token","keyword"]},"children":[{"type":"text","value":"return"}]},{"type":"text","value":" "},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"("}]},{"type":"text","value":"\n       "},{"type":"element","tagName":"span","properties":{"className":["token","operator"]},"children":[{"type":"text","value":"<"}]},{"type":"text","value":"div className"},{"type":"element","tagName":"span","properties":{"className":["token","operator"]},"children":[{"type":"text","value":"="}]},{"type":"element","tagName":"span","properties":{"className":["token","string"]},"children":[{"type":"text","value":"\"App\""}]},{"type":"element","tagName":"span","properties":{"className":["token","operator"]},"children":[{"type":"text","value":">"}]},{"type":"text","value":"\n         "},{"type":"element","tagName":"span","properties":{"className":["token","operator"]},"children":[{"type":"text","value":"<"}]},{"type":"text","value":"header className"},{"type":"element","tagName":"span","properties":{"className":["token","operator"]},"children":[{"type":"text","value":"="}]},{"type":"element","tagName":"span","properties":{"className":["token","string"]},"children":[{"type":"text","value":"\"App-header\""}]},{"type":"element","tagName":"span","properties":{"className":["token","operator"]},"children":[{"type":"text","value":">"}]},{"type":"text","value":"\n           "},{"type":"element","tagName":"span","properties":{"className":["token","operator"]},"children":[{"type":"text","value":"<"}]},{"type":"text","value":"h1"},{"type":"element","tagName":"span","properties":{"className":["token","operator"]},"children":[{"type":"text","value":">"}]},{"type":"text","value":"Hello"},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":","}]},{"type":"text","value":" World"},{"type":"element","tagName":"span","properties":{"className":["token","operator"]},"children":[{"type":"text","value":"!"}]},{"type":"element","tagName":"span","properties":{"className":["token","operator"]},"children":[{"type":"text","value":"<"}]},{"type":"element","tagName":"span","properties":{"className":["token","operator"]},"children":[{"type":"text","value":"/"}]},{"type":"text","value":"h1"},{"type":"element","tagName":"span","properties":{"className":["token","operator"]},"children":[{"type":"text","value":">"}]},{"type":"text","value":"\n         "},{"type":"element","tagName":"span","properties":{"className":["token","operator"]},"children":[{"type":"text","value":"<"}]},{"type":"element","tagName":"span","properties":{"className":["token","operator"]},"children":[{"type":"text","value":"/"}]},{"type":"text","value":"header"},{"type":"element","tagName":"span","properties":{"className":["token","operator"]},"children":[{"type":"text","value":">"}]},{"type":"text","value":"\n       "},{"type":"element","tagName":"span","properties":{"className":["token","operator"]},"children":[{"type":"text","value":"<"}]},{"type":"element","tagName":"span","properties":{"className":["token","operator"]},"children":[{"type":"text","value":"/"}]},{"type":"text","value":"div"},{"type":"element","tagName":"span","properties":{"className":["token","operator"]},"children":[{"type":"text","value":">"}]},{"type":"text","value":"\n     "},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":")"}]},{"type":"text","value":"\n   "},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"}"}]},{"type":"text","value":"\n "},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"}"}]},{"type":"text","value":"\n"},{"type":"element","tagName":"span","properties":{"className":["token","keyword"]},"children":[{"type":"text","value":"export"}]},{"type":"text","value":" "},{"type":"element","tagName":"span","properties":{"className":["token","keyword"]},"children":[{"type":"text","value":"default"}]},{"type":"text","value":" App"}]}]}]},{"type":"element","tagName":"p","properties":{},"children":[{"type":"text","value":"We just replaced the content of the "},{"type":"element","tagName":"strong","properties":{},"children":[{"type":"element","tagName":"strong","properties":{},"children":[{"type":"text","value":"header"}]}]},{"type":"text","value":" element. Now, if your server is still running you will see that your page updated without you refreshing it, and you will see that the previous content is replaced with "},{"type":"element","tagName":"strong","properties":{},"children":[{"type":"element","tagName":"strong","properties":{},"children":[{"type":"text","value":"Hello, World!"}]}]}]},{"type":"element","tagName":"p","properties":{},"children":[{"type":"text","value":"So now we know how and where we should edit our React components in order to get the result we want. We can go ahead and start with our objective."}]},{"type":"element","tagName":"p","properties":{},"children":[{"type":"text","value":"What we’ll do for this website is the following:"}]},{"type":"element","tagName":"ol","properties":{},"children":[{"type":"element","tagName":"li","properties":{},"children":[{"type":"text","value":"Show a welcome message the first time the user opens the website, then replace it with a message prompting the user to try the generator."}]},{"type":"element","tagName":"li","properties":{},"children":[{"type":"text","value":"Render a form with "},{"type":"element","tagName":"strong","properties":{},"children":[{"type":"element","tagName":"strong","properties":{},"children":[{"type":"text","value":"text "}]}]},{"type":"text","value":"and "},{"type":"element","tagName":"strong","properties":{},"children":[{"type":"element","tagName":"strong","properties":{},"children":[{"type":"text","value":"select"}]}]},{"type":"text","value":" inputs. The "},{"type":"element","tagName":"strong","properties":{},"children":[{"type":"element","tagName":"strong","properties":{},"children":[{"type":"text","value":"text "}]}]},{"type":"text","value":"input is where the user can enter the number they want to see trivia about, and the "},{"type":"element","tagName":"strong","properties":{},"children":[{"type":"element","tagName":"strong","properties":{},"children":[{"type":"text","value":"select "}]}]},{"type":"text","value":"input will provide the user with options related to the trivia."}]},{"type":"element","tagName":"li","properties":{},"children":[{"type":"text","value":"On submitting the form, send a request to "},{"type":"element","tagName":"a","properties":{"href":"http://numbersapi.com/","rel":["noopener","nofollow"]},"children":[{"type":"text","value":"this API"}]},{"type":"text","value":" to fetch the trivia we need."}]},{"type":"element","tagName":"li","properties":{},"children":[{"type":"text","value":"Render the trivia for the user to see it."}]}]},{"type":"element","tagName":"p","properties":{},"children":[{"type":"text","value":"Let’s start by organizing our directory structure first. In React it’s good practice to create a directory inside "},{"type":"element","tagName":"strong","properties":{},"children":[{"type":"element","tagName":"strong","properties":{},"children":[{"type":"text","value":"src"}]}]},{"type":"text","value":" holding all the components. We’ll create a directory called "},{"type":"element","tagName":"strong","properties":{},"children":[{"type":"element","tagName":"strong","properties":{},"children":[{"type":"text","value":"components"}]}]},{"type":"text","value":". Once you create the directory, move "},{"type":"element","tagName":"strong","properties":{},"children":[{"type":"element","tagName":"strong","properties":{},"children":[{"type":"text","value":"App.js"}]}]},{"type":"text","value":" into there. We will also create a directory called "},{"type":"element","tagName":"strong","properties":{},"children":[{"type":"element","tagName":"strong","properties":{},"children":[{"type":"text","value":"styles"}]}]},{"type":"text","value":" and move "},{"type":"element","tagName":"strong","properties":{},"children":[{"type":"element","tagName":"strong","properties":{},"children":[{"type":"text","value":"App.css "}]}]},{"type":"text","value":"and "},{"type":"element","tagName":"strong","properties":{},"children":[{"type":"element","tagName":"strong","properties":{},"children":[{"type":"text","value":"index.css "}]}]},{"type":"text","value":"into it."}]},{"type":"element","tagName":"p","properties":{},"children":[{"type":"text","value":"When you do that, you will need to change the imports in your files as following:"}]},{"type":"element","tagName":"ol","properties":{},"children":[{"type":"element","tagName":"li","properties":{},"children":[{"type":"text","value":"in "},{"type":"element","tagName":"strong","properties":{},"children":[{"type":"element","tagName":"strong","properties":{},"children":[{"type":"text","value":"index.js:"}]}]}]}]},{"type":"element","tagName":"div","properties":{"className":["kg-card","kg-code-card","gatsby-highlight"],"dataLanguage":"js"},"children":[{"type":"element","tagName":"pre","properties":{"className":["language-js"]},"children":[{"type":"element","tagName":"code","properties":{"className":["language-js"]},"children":[{"type":"element","tagName":"span","properties":{"className":["token","keyword"]},"children":[{"type":"text","value":"import"}]},{"type":"text","value":" React "},{"type":"element","tagName":"span","properties":{"className":["token","keyword"]},"children":[{"type":"text","value":"from"}]},{"type":"text","value":" "},{"type":"element","tagName":"span","properties":{"className":["token","string"]},"children":[{"type":"text","value":"'react'"}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":";"}]},{"type":"text","value":" \n"},{"type":"element","tagName":"span","properties":{"className":["token","keyword"]},"children":[{"type":"text","value":"import"}]},{"type":"text","value":" ReactDOM "},{"type":"element","tagName":"span","properties":{"className":["token","keyword"]},"children":[{"type":"text","value":"from"}]},{"type":"text","value":" "},{"type":"element","tagName":"span","properties":{"className":["token","string"]},"children":[{"type":"text","value":"'react-dom'"}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":";"}]},{"type":"text","value":" \n"},{"type":"element","tagName":"span","properties":{"className":["token","keyword"]},"children":[{"type":"text","value":"import"}]},{"type":"text","value":" "},{"type":"element","tagName":"span","properties":{"className":["token","string"]},"children":[{"type":"text","value":"'../styles/index.css'"}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":";"}]},{"type":"text","value":" \n"},{"type":"element","tagName":"span","properties":{"className":["token","keyword"]},"children":[{"type":"text","value":"import"}]},{"type":"text","value":" App "},{"type":"element","tagName":"span","properties":{"className":["token","keyword"]},"children":[{"type":"text","value":"from"}]},{"type":"text","value":" "},{"type":"element","tagName":"span","properties":{"className":["token","string"]},"children":[{"type":"text","value":"'./components/App'"}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":";"}]},{"type":"text","value":" \n"},{"type":"element","tagName":"span","properties":{"className":["token","keyword"]},"children":[{"type":"text","value":"import"}]},{"type":"text","value":"  "},{"type":"element","tagName":"span","properties":{"className":["token","keyword"]},"children":[{"type":"text","value":"as"}]},{"type":"text","value":" serviceWorker "},{"type":"element","tagName":"span","properties":{"className":["token","keyword"]},"children":[{"type":"text","value":"from"}]},{"type":"text","value":" "},{"type":"element","tagName":"span","properties":{"className":["token","string"]},"children":[{"type":"text","value":"'./serviceWorker'"}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":";"}]}]}]}]},{"type":"element","tagName":"p","properties":{},"children":[{"type":"text","value":"2. in "},{"type":"element","tagName":"strong","properties":{},"children":[{"type":"element","tagName":"strong","properties":{},"children":[{"type":"text","value":"App.js:"}]}]}]},{"type":"element","tagName":"div","properties":{"className":["kg-card","kg-code-card","gatsby-highlight"],"dataLanguage":"js"},"children":[{"type":"element","tagName":"pre","properties":{"className":["language-js"]},"children":[{"type":"element","tagName":"code","properties":{"className":["language-js"]},"children":[{"type":"element","tagName":"span","properties":{"className":["token","keyword"]},"children":[{"type":"text","value":"import"}]},{"type":"text","value":" React"},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":","}]},{"type":"text","value":" "},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"{"}]},{"type":"text","value":" Component "},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"}"}]},{"type":"text","value":" "},{"type":"element","tagName":"span","properties":{"className":["token","keyword"]},"children":[{"type":"text","value":"from"}]},{"type":"text","value":" "},{"type":"element","tagName":"span","properties":{"className":["token","string"]},"children":[{"type":"text","value":"'react'"}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":";"}]},{"type":"text","value":" \n"},{"type":"element","tagName":"span","properties":{"className":["token","keyword"]},"children":[{"type":"text","value":"import"}]},{"type":"text","value":" logo "},{"type":"element","tagName":"span","properties":{"className":["token","keyword"]},"children":[{"type":"text","value":"from"}]},{"type":"text","value":" "},{"type":"element","tagName":"span","properties":{"className":["token","string"]},"children":[{"type":"text","value":"'./logo.svg'"}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":";"}]},{"type":"text","value":" \n"},{"type":"element","tagName":"span","properties":{"className":["token","keyword"]},"children":[{"type":"text","value":"import"}]},{"type":"text","value":" "},{"type":"element","tagName":"span","properties":{"className":["token","string"]},"children":[{"type":"text","value":"'../styles/App.css'"}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":";"}]}]}]}]},{"type":"element","tagName":"p","properties":{},"children":[{"type":"text","value":"Our directory structure should look like this now:"}]},{"type":"element","tagName":"figure","properties":{"className":["kg-card","kg-image-card"]},"children":[{"type":"element","tagName":"img","properties":{"src":"https://res-4.cloudinary.com/hbqmf3mbz/image/upload/q_auto/v1/ghost-blog-images/1_Aqgelb3qbhJiESUbGGNUoQ.png","className":["kg-image"],"alt":"","loading":"lazy"},"children":[]}]},{"type":"element","tagName":"p","properties":{},"children":[{"type":"text","value":"We will go ahead now and start building our webpage."}]},{"type":"element","tagName":"p","properties":{},"children":[{"type":"text","value":"The first thing in our objectives list is showing a welcome message when the user first opens the webpage. It will show up for 3 seconds, and then changes to another message that will prompt the user to try out the trivia generator."}]},{"type":"element","tagName":"p","properties":{},"children":[{"type":"text","value":"Without hooks, this could be done by using React’s lifecycle method "},{"type":"element","tagName":"code","properties":{"className":["language-text"]},"children":[{"type":"text","value":"componentDidMount"}]},{"type":"element","tagName":"em","properties":{},"children":[{"type":"element","tagName":"em","properties":{},"children":[{"type":"text","value":" "}]}]},{"type":"text","value":"which runs right after the component first renders."}]},{"type":"element","tagName":"p","properties":{},"children":[{"type":"text","value":"Now, we can use the "},{"type":"element","tagName":"a","properties":{"href":"https://reactjs.org/docs/hooks-effect.html","rel":["noopener","nofollow"]},"children":[{"type":"element","tagName":"em","properties":{},"children":[{"type":"element","tagName":"em","properties":{},"children":[{"type":"text","value":"effect hook"}]}]}]},{"type":"element","tagName":"em","properties":{},"children":[{"type":"element","tagName":"em","properties":{},"children":[{"type":"text","value":" "}]}]},{"type":"text","value":"instead. It will look something like this:"}]},{"type":"element","tagName":"div","properties":{"className":["kg-card","kg-code-card","gatsby-highlight"],"dataLanguage":"js"},"children":[{"type":"element","tagName":"pre","properties":{"className":["language-js"]},"children":[{"type":"element","tagName":"code","properties":{"className":["language-js"]},"children":[{"type":"element","tagName":"span","properties":{"className":["token","function"]},"children":[{"type":"text","value":"useEffect"}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"("}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"("}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":")"}]},{"type":"text","value":" "},{"type":"element","tagName":"span","properties":{"className":["token","operator"]},"children":[{"type":"text","value":"=>"}]},{"type":"text","value":" "},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"{"}]},{"type":"text","value":"\n  "},{"type":"element","tagName":"span","properties":{"className":["token","comment"]},"children":[{"type":"text","value":"//perform something post render"}]},{"type":"text","value":"\n"},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"}"}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":")"}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":";"}]}]}]}]},{"type":"element","tagName":"p","properties":{},"children":[{"type":"text","value":"The function you pass to "},{"type":"element","tagName":"code","properties":{"className":["language-text"]},"children":[{"type":"text","value":"useEffect"}]},{"type":"text","value":" will be executed after every render. This combines the lifecycles methods "},{"type":"element","tagName":"code","properties":{"className":["language-text"]},"children":[{"type":"text","value":"componentDidMount"}]},{"type":"element","tagName":"strong","properties":{},"children":[{"type":"element","tagName":"strong","properties":{},"children":[{"type":"text","value":" "}]}]},{"type":"text","value":"and "},{"type":"element","tagName":"code","properties":{"className":["language-text"]},"children":[{"type":"text","value":"componentDidUpdate"}]},{"type":"text","value":" into one."}]},{"type":"element","tagName":"p","properties":{},"children":[{"type":"text","value":"What if you want to do something just after the first time the component renders, like in "},{"type":"element","tagName":"code","properties":{"className":["language-text"]},"children":[{"type":"text","value":"componentDidMount"}]},{"type":"text","value":"? You can do this by passing a second parameter to "},{"type":"element","tagName":"code","properties":{"className":["language-text"]},"children":[{"type":"text","value":"useEffect"}]},{"type":"text","value":"."}]},{"type":"element","tagName":"p","properties":{},"children":[{"type":"element","tagName":"code","properties":{"className":["language-text"]},"children":[{"type":"text","value":"useEffect"}]},{"type":"element","tagName":"strong","properties":{},"children":[{"type":"element","tagName":"strong","properties":{},"children":[{"type":"text","value":" "}]}]},{"type":"text","value":"accepts an array as a second parameter. This parameter is used as a condition on when to perform the passed function. So, let’s say you want to change a counter only after the variable "},{"type":"element","tagName":"strong","properties":{},"children":[{"type":"element","tagName":"strong","properties":{},"children":[{"type":"text","value":"counter "}]}]},{"type":"text","value":"changes, you can do it like so:"}]},{"type":"element","tagName":"div","properties":{"className":["kg-card","kg-code-card","gatsby-highlight"],"dataLanguage":"js"},"children":[{"type":"element","tagName":"pre","properties":{"className":["language-js"]},"children":[{"type":"element","tagName":"code","properties":{"className":["language-js"]},"children":[{"type":"element","tagName":"span","properties":{"className":["token","function"]},"children":[{"type":"text","value":"useEffect"}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"("}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"("}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":")"}]},{"type":"text","value":" "},{"type":"element","tagName":"span","properties":{"className":["token","operator"]},"children":[{"type":"text","value":"=>"}]},{"type":"text","value":" document"},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"."}]},{"type":"text","value":"title "},{"type":"element","tagName":"span","properties":{"className":["token","operator"]},"children":[{"type":"text","value":"="}]},{"type":"text","value":" "},{"type":"element","tagName":"span","properties":{"className":["token","template-string"]},"children":[{"type":"element","tagName":"span","properties":{"className":["token","template-punctuation","string"]},"children":[{"type":"text","value":"`"}]},{"type":"element","tagName":"span","properties":{"className":["token","string"]},"children":[{"type":"text","value":"You have clicked "}]},{"type":"element","tagName":"span","properties":{"className":["token","interpolation"]},"children":[{"type":"element","tagName":"span","properties":{"className":["token","interpolation-punctuation","punctuation"]},"children":[{"type":"text","value":"${"}]},{"type":"text","value":"counter"},{"type":"element","tagName":"span","properties":{"className":["token","interpolation-punctuation","punctuation"]},"children":[{"type":"text","value":"}"}]}]},{"type":"element","tagName":"span","properties":{"className":["token","string"]},"children":[{"type":"text","value":" times"}]},{"type":"element","tagName":"span","properties":{"className":["token","template-punctuation","string"]},"children":[{"type":"text","value":"`"}]}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":","}]},{"type":"text","value":" "},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"["}]},{"type":"text","value":"counter"},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"]"}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":")"}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":";"}]}]}]}]},{"type":"element","tagName":"p","properties":{},"children":[{"type":"text","value":"This way, the function passed to "},{"type":"element","tagName":"code","properties":{"className":["language-text"]},"children":[{"type":"text","value":"useEffect"}]},{"type":"text","value":" will run after render only if the value for "},{"type":"element","tagName":"code","properties":{"className":["language-text"]},"children":[{"type":"text","value":"counter"}]},{"type":"element","tagName":"strong","properties":{},"children":[{"type":"element","tagName":"strong","properties":{},"children":[{"type":"text","value":" "}]}]},{"type":"text","value":"changes."}]},{"type":"element","tagName":"p","properties":{},"children":[{"type":"text","value":"If we want the function to run only after the first render, we can do that by passing an "},{"type":"element","tagName":"strong","properties":{},"children":[{"type":"element","tagName":"strong","properties":{},"children":[{"type":"text","value":"empty"}]}]},{"type":"text","value":" array as the second parameter."}]},{"type":"element","tagName":"p","properties":{},"children":[{"type":"text","value":"Let’s come back now to what we want to do. We want to show a welcome message when the user first opens the page, then change that message after 3 seconds. We can do that by adding the following inside "},{"type":"element","tagName":"strong","properties":{},"children":[{"type":"element","tagName":"strong","properties":{},"children":[{"type":"text","value":"App.js"}]}]},{"type":"text","value":":"}]},{"type":"element","tagName":"div","properties":{"className":["kg-card","kg-code-card","gatsby-highlight"],"dataLanguage":"js"},"children":[{"type":"element","tagName":"pre","properties":{"className":["language-js"]},"children":[{"type":"element","tagName":"code","properties":{"className":["language-js"]},"children":[{"type":"element","tagName":"span","properties":{"className":["token","keyword"]},"children":[{"type":"text","value":"import"}]},{"type":"text","value":" React"},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":","}]},{"type":"text","value":" "},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"{"}]},{"type":"text","value":"useEffect"},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"}"}]},{"type":"text","value":" "},{"type":"element","tagName":"span","properties":{"className":["token","keyword"]},"children":[{"type":"text","value":"from"}]},{"type":"text","value":" "},{"type":"element","tagName":"span","properties":{"className":["token","string"]},"children":[{"type":"text","value":"'react'"}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":";"}]},{"type":"text","value":" \n"},{"type":"element","tagName":"span","properties":{"className":["token","keyword"]},"children":[{"type":"text","value":"import"}]},{"type":"text","value":" Form "},{"type":"element","tagName":"span","properties":{"className":["token","keyword"]},"children":[{"type":"text","value":"from"}]},{"type":"text","value":" "},{"type":"element","tagName":"span","properties":{"className":["token","string"]},"children":[{"type":"text","value":"'./Form'"}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":";"}]},{"type":"text","value":" \n"},{"type":"element","tagName":"span","properties":{"className":["token","keyword"]},"children":[{"type":"text","value":"import"}]},{"type":"text","value":" "},{"type":"element","tagName":"span","properties":{"className":["token","string"]},"children":[{"type":"text","value":"'../styles/App.css'"}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":";"}]},{"type":"text","value":" \n \n"},{"type":"element","tagName":"span","properties":{"className":["token","keyword"]},"children":[{"type":"text","value":"function"}]},{"type":"text","value":" "},{"type":"element","tagName":"span","properties":{"className":["token","function"]},"children":[{"type":"text","value":"App"}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"("}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":")"}]},{"type":"text","value":" "},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"{"}]},{"type":"text","value":"\n   "},{"type":"element","tagName":"span","properties":{"className":["token","function"]},"children":[{"type":"text","value":"useEffect"}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"("}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"("}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":")"}]},{"type":"text","value":" "},{"type":"element","tagName":"span","properties":{"className":["token","operator"]},"children":[{"type":"text","value":"=>"}]},{"type":"text","value":" "},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"{"}]},{"type":"text","value":"\n     "},{"type":"element","tagName":"span","properties":{"className":["token","function"]},"children":[{"type":"text","value":"setTimeout"}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"("}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"("}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":")"}]},{"type":"text","value":" "},{"type":"element","tagName":"span","properties":{"className":["token","operator"]},"children":[{"type":"text","value":"=>"}]},{"type":"text","value":" "},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"{"}]},{"type":"text","value":"\n       "},{"type":"element","tagName":"span","properties":{"className":["token","keyword"]},"children":[{"type":"text","value":"let"}]},{"type":"text","value":" welcomeMessage "},{"type":"element","tagName":"span","properties":{"className":["token","operator"]},"children":[{"type":"text","value":"="}]},{"type":"text","value":" document"},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"."}]},{"type":"element","tagName":"span","properties":{"className":["token","function"]},"children":[{"type":"text","value":"getElementById"}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"("}]},{"type":"element","tagName":"span","properties":{"className":["token","string"]},"children":[{"type":"text","value":"\"welcomeMessage\""}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":")"}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":";"}]},{"type":"text","value":"\n       welcomeMessage"},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"."}]},{"type":"text","value":"innerHTML "},{"type":"element","tagName":"span","properties":{"className":["token","operator"]},"children":[{"type":"text","value":"="}]},{"type":"text","value":" "},{"type":"element","tagName":"span","properties":{"className":["token","string"]},"children":[{"type":"text","value":"\"Try Out Our Trivia Generator!\""}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":";"}]},{"type":"text","value":"\n     "},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"}"}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":","}]},{"type":"text","value":" "},{"type":"element","tagName":"span","properties":{"className":["token","number"]},"children":[{"type":"text","value":"3000"}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":")"}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":";"}]},{"type":"text","value":"\n   "},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"}"}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":","}]},{"type":"text","value":" "},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"["}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"]"}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":")"}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":";"}]},{"type":"text","value":"\n   "},{"type":"element","tagName":"span","properties":{"className":["token","keyword"]},"children":[{"type":"text","value":"return"}]},{"type":"text","value":" "},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"("}]},{"type":"text","value":"\n     "},{"type":"element","tagName":"span","properties":{"className":["token","operator"]},"children":[{"type":"text","value":"<"}]},{"type":"text","value":"div className"},{"type":"element","tagName":"span","properties":{"className":["token","operator"]},"children":[{"type":"text","value":"="}]},{"type":"element","tagName":"span","properties":{"className":["token","string"]},"children":[{"type":"text","value":"\"App\""}]},{"type":"element","tagName":"span","properties":{"className":["token","operator"]},"children":[{"type":"text","value":">"}]},{"type":"text","value":"\n       "},{"type":"element","tagName":"span","properties":{"className":["token","operator"]},"children":[{"type":"text","value":"<"}]},{"type":"text","value":"header className"},{"type":"element","tagName":"span","properties":{"className":["token","operator"]},"children":[{"type":"text","value":"="}]},{"type":"element","tagName":"span","properties":{"className":["token","string"]},"children":[{"type":"text","value":"\"App-header\""}]},{"type":"element","tagName":"span","properties":{"className":["token","operator"]},"children":[{"type":"text","value":">"}]},{"type":"text","value":"\n         "},{"type":"element","tagName":"span","properties":{"className":["token","operator"]},"children":[{"type":"text","value":"<"}]},{"type":"text","value":"h1 id"},{"type":"element","tagName":"span","properties":{"className":["token","operator"]},"children":[{"type":"text","value":"="}]},{"type":"element","tagName":"span","properties":{"className":["token","string"]},"children":[{"type":"text","value":"\"welcomeMessage\""}]},{"type":"element","tagName":"span","properties":{"className":["token","operator"]},"children":[{"type":"text","value":">"}]},{"type":"text","value":"Welcome to Numbers Trivia"},{"type":"element","tagName":"span","properties":{"className":["token","operator"]},"children":[{"type":"text","value":"!"}]},{"type":"element","tagName":"span","properties":{"className":["token","operator"]},"children":[{"type":"text","value":"<"}]},{"type":"element","tagName":"span","properties":{"className":["token","operator"]},"children":[{"type":"text","value":"/"}]},{"type":"text","value":"h1"},{"type":"element","tagName":"span","properties":{"className":["token","operator"]},"children":[{"type":"text","value":">"}]},{"type":"text","value":"\n         "},{"type":"element","tagName":"span","properties":{"className":["token","operator"]},"children":[{"type":"text","value":"<"}]},{"type":"text","value":"Form "},{"type":"element","tagName":"span","properties":{"className":["token","operator"]},"children":[{"type":"text","value":"/"}]},{"type":"element","tagName":"span","properties":{"className":["token","operator"]},"children":[{"type":"text","value":">"}]},{"type":"text","value":"\n       "},{"type":"element","tagName":"span","properties":{"className":["token","operator"]},"children":[{"type":"text","value":"<"}]},{"type":"element","tagName":"span","properties":{"className":["token","operator"]},"children":[{"type":"text","value":"/"}]},{"type":"text","value":"header"},{"type":"element","tagName":"span","properties":{"className":["token","operator"]},"children":[{"type":"text","value":">"}]},{"type":"text","value":"\n     "},{"type":"element","tagName":"span","properties":{"className":["token","operator"]},"children":[{"type":"text","value":"<"}]},{"type":"element","tagName":"span","properties":{"className":["token","operator"]},"children":[{"type":"text","value":"/"}]},{"type":"text","value":"div"},{"type":"element","tagName":"span","properties":{"className":["token","operator"]},"children":[{"type":"text","value":">"}]},{"type":"text","value":"\n   "},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":")"}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":";"}]},{"type":"text","value":" "},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"}"}]},{"type":"text","value":"  \n"},{"type":"element","tagName":"span","properties":{"className":["token","keyword"]},"children":[{"type":"text","value":"export"}]},{"type":"text","value":" "},{"type":"element","tagName":"span","properties":{"className":["token","keyword"]},"children":[{"type":"text","value":"default"}]},{"type":"text","value":" App"},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":";"}]}]}]}]},{"type":"element","tagName":"p","properties":{},"children":[{"type":"text","value":"Here’s what we’re doing:"}]},{"type":"element","tagName":"ol","properties":{},"children":[{"type":"element","tagName":"li","properties":{},"children":[{"type":"element","tagName":"strong","properties":{},"children":[{"type":"element","tagName":"strong","properties":{},"children":[{"type":"text","value":"Line 1:"}]}]},{"type":"text","value":" We added an import for "},{"type":"element","tagName":"code","properties":{"className":["language-text"]},"children":[{"type":"text","value":"useEffect"}]}]},{"type":"element","tagName":"li","properties":{},"children":[{"type":"element","tagName":"strong","properties":{},"children":[{"type":"element","tagName":"strong","properties":{},"children":[{"type":"text","value":"Line 4: "}]}]},{"type":"text","value":"We changed our class component into a function component"}]},{"type":"element","tagName":"li","properties":{},"children":[{"type":"element","tagName":"strong","properties":{},"children":[{"type":"element","tagName":"strong","properties":{},"children":[{"type":"text","value":"Line 5–10: "}]}]},{"type":"text","value":"we added an effect to our function component. This effect sets a timer after 3 seconds that will change the text in the element with the id "},{"type":"element","tagName":"code","properties":{"className":["language-text"]},"children":[{"type":"text","value":"welcomeMessage"}]},{"type":"element","tagName":"strong","properties":{},"children":[{"type":"element","tagName":"strong","properties":{},"children":[{"type":"text","value":". "}]}]},{"type":"text","value":"Because we passed an "},{"type":"element","tagName":"strong","properties":{},"children":[{"type":"element","tagName":"strong","properties":{},"children":[{"type":"text","value":"empty"}]}]},{"type":"text","value":" array to "},{"type":"element","tagName":"code","properties":{"className":["language-text"]},"children":[{"type":"text","value":"useEffect"}]},{"type":"text","value":", this effect will only run once."}]},{"type":"element","tagName":"li","properties":{},"children":[{"type":"element","tagName":"strong","properties":{},"children":[{"type":"element","tagName":"strong","properties":{},"children":[{"type":"text","value":"Line 11–17: "}]}]},{"type":"text","value":"We replaced the previous code in "},{"type":"element","tagName":"strong","properties":{},"children":[{"type":"element","tagName":"strong","properties":{},"children":[{"type":"text","value":"App.js "}]}]},{"type":"text","value":"to render an "},{"type":"element","tagName":"strong","properties":{},"children":[{"type":"element","tagName":"strong","properties":{},"children":[{"type":"text","value":"h1 "}]}]},{"type":"text","value":"element having the id "},{"type":"element","tagName":"code","properties":{"className":["language-text"]},"children":[{"type":"text","value":"welcomeMessage"}]},{"type":"element","tagName":"strong","properties":{},"children":[{"type":"element","tagName":"strong","properties":{},"children":[{"type":"text","value":", "}]}]},{"type":"text","value":"which is our target element."}]}]},{"type":"element","tagName":"p","properties":{},"children":[{"type":"text","value":"Okay, now go to our web page and see the changes. At first, the welcome message “"},{"type":"element","tagName":"strong","properties":{},"children":[{"type":"element","tagName":"strong","properties":{},"children":[{"type":"text","value":"Welcome to Numbers Trivia!"}]}]},{"type":"text","value":"” will show up, then 3 seconds later it will change into “"},{"type":"element","tagName":"strong","properties":{},"children":[{"type":"element","tagName":"strong","properties":{},"children":[{"type":"text","value":"Try Out Our Trivia Generator!"}]}]},{"type":"text","value":"” We just used a React Hook!"}]},{"type":"element","tagName":"p","properties":{},"children":[{"type":"text","value":"Next, let’s create our form input elements. To do that, we will create a new React component called "},{"type":"element","tagName":"code","properties":{"className":["language-text"]},"children":[{"type":"text","value":"Form"}]},{"type":"element","tagName":"strong","properties":{},"children":[{"type":"element","tagName":"strong","properties":{},"children":[{"type":"text","value":". "}]}]},{"type":"text","value":"Create in the directory "},{"type":"element","tagName":"strong","properties":{},"children":[{"type":"element","tagName":"strong","properties":{},"children":[{"type":"text","value":"components"}]}]},{"type":"text","value":" the file "},{"type":"element","tagName":"strong","properties":{},"children":[{"type":"element","tagName":"strong","properties":{},"children":[{"type":"text","value":"Form.js. "}]}]},{"type":"text","value":"For now, it will just include the following:"}]},{"type":"element","tagName":"div","properties":{"className":["kg-card","kg-code-card","gatsby-highlight"],"dataLanguage":"js"},"children":[{"type":"element","tagName":"pre","properties":{"className":["language-js"]},"children":[{"type":"element","tagName":"code","properties":{"className":["language-js"]},"children":[{"type":"element","tagName":"span","properties":{"className":["token","keyword"]},"children":[{"type":"text","value":"import"}]},{"type":"text","value":" React "},{"type":"element","tagName":"span","properties":{"className":["token","keyword"]},"children":[{"type":"text","value":"from"}]},{"type":"text","value":" "},{"type":"element","tagName":"span","properties":{"className":["token","string"]},"children":[{"type":"text","value":"'react'"}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":";"}]},{"type":"text","value":"  \n"},{"type":"element","tagName":"span","properties":{"className":["token","keyword"]},"children":[{"type":"text","value":"function"}]},{"type":"text","value":" "},{"type":"element","tagName":"span","properties":{"className":["token","function"]},"children":[{"type":"text","value":"Form"}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"("}]},{"type":"element","tagName":"span","properties":{"className":["token","parameter"]},"children":[{"type":"text","value":"props"}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":")"}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"{"}]},{"type":"text","value":"\n"},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"}"}]},{"type":"text","value":"\n"},{"type":"element","tagName":"span","properties":{"className":["token","keyword"]},"children":[{"type":"text","value":"export"}]},{"type":"text","value":" "},{"type":"element","tagName":"span","properties":{"className":["token","keyword"]},"children":[{"type":"text","value":"default"}]},{"type":"text","value":" Form"},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":";"}]}]}]}]},{"type":"element","tagName":"p","properties":{},"children":[{"type":"text","value":"This will create the new React component. We’re just importing React, then we’re creating a function called Form. As we said earlier in the tutorial, with the use of hooks we can now create components as stateful functions rather than classes. And in the last line, we’re exporting "},{"type":"element","tagName":"strong","properties":{},"children":[{"type":"element","tagName":"strong","properties":{},"children":[{"type":"text","value":"Form"}]}]},{"type":"text","value":" in order to import it in other files."}]},{"type":"element","tagName":"p","properties":{},"children":[{"type":"text","value":"In the form, we will have a "},{"type":"element","tagName":"strong","properties":{},"children":[{"type":"element","tagName":"strong","properties":{},"children":[{"type":"text","value":"text "}]}]},{"type":"text","value":"input and "},{"type":"element","tagName":"strong","properties":{},"children":[{"type":"element","tagName":"strong","properties":{},"children":[{"type":"text","value":"select "}]}]},{"type":"text","value":"elements. This is based on the API we’re using. In the API, two parameters can be sent:"}]},{"type":"element","tagName":"ol","properties":{},"children":[{"type":"element","tagName":"li","properties":{},"children":[{"type":"element","tagName":"strong","properties":{},"children":[{"type":"element","tagName":"strong","properties":{},"children":[{"type":"text","value":"number: "}]}]},{"type":"text","value":"the number you want to get the trivia for. It can be an "},{"type":"element","tagName":"strong","properties":{},"children":[{"type":"element","tagName":"strong","properties":{},"children":[{"type":"text","value":"integer"}]}]},{"type":"text","value":", a date of the form "},{"type":"element","tagName":"strong","properties":{},"children":[{"type":"element","tagName":"strong","properties":{},"children":[{"type":"text","value":"month/day, "}]}]},{"type":"text","value":"or the keyword "},{"type":"element","tagName":"strong","properties":{},"children":[{"type":"element","tagName":"strong","properties":{},"children":[{"type":"text","value":"random "}]}]},{"type":"text","value":"which will retrieve facts about a random number."}]},{"type":"element","tagName":"li","properties":{},"children":[{"type":"element","tagName":"strong","properties":{},"children":[{"type":"element","tagName":"strong","properties":{},"children":[{"type":"text","value":"type: "}]}]},{"type":"text","value":"the type of information you want to get. There are a few types: "},{"type":"element","tagName":"strong","properties":{},"children":[{"type":"element","tagName":"strong","properties":{},"children":[{"type":"text","value":"math, date, year, "}]}]},{"type":"text","value":"or, the default, "},{"type":"element","tagName":"strong","properties":{},"children":[{"type":"element","tagName":"strong","properties":{},"children":[{"type":"text","value":"trivia."}]}]}]}]},{"type":"element","tagName":"p","properties":{},"children":[{"type":"text","value":"We will consider the text input element as optional. If the user does not enter a number or a date, we will send the keyword "},{"type":"element","tagName":"strong","properties":{},"children":[{"type":"element","tagName":"strong","properties":{},"children":[{"type":"text","value":"random"}]}]},{"type":"text","value":" for the number element."}]},{"type":"element","tagName":"p","properties":{},"children":[{"type":"text","value":"Let’s add the following code inside the "},{"type":"element","tagName":"strong","properties":{},"children":[{"type":"element","tagName":"strong","properties":{},"children":[{"type":"text","value":"Form"}]}]},{"type":"text","value":" function in order to render our form:"}]},{"type":"element","tagName":"div","properties":{"className":["kg-card","kg-code-card","gatsby-highlight"],"dataLanguage":"js"},"children":[{"type":"element","tagName":"pre","properties":{"className":["language-js"]},"children":[{"type":"element","tagName":"code","properties":{"className":["language-js"]},"children":[{"type":"element","tagName":"span","properties":{"className":["token","keyword"]},"children":[{"type":"text","value":"function"}]},{"type":"text","value":" "},{"type":"element","tagName":"span","properties":{"className":["token","function"]},"children":[{"type":"text","value":"Form"}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"("}]},{"type":"element","tagName":"span","properties":{"className":["token","parameter"]},"children":[{"type":"text","value":"props"}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":")"}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"{"}]},{"type":"text","value":"\n  "},{"type":"element","tagName":"span","properties":{"className":["token","keyword"]},"children":[{"type":"text","value":"return"}]},{"type":"text","value":" "},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"("}]},{"type":"element","tagName":"span","properties":{"className":["token","operator"]},"children":[{"type":"text","value":"<"}]},{"type":"text","value":"form"},{"type":"element","tagName":"span","properties":{"className":["token","operator"]},"children":[{"type":"text","value":">"}]},{"type":"text","value":"\n             "},{"type":"element","tagName":"span","properties":{"className":["token","operator"]},"children":[{"type":"text","value":"<"}]},{"type":"text","value":"div"},{"type":"element","tagName":"span","properties":{"className":["token","operator"]},"children":[{"type":"text","value":">"}]},{"type":"text","value":"\n              "},{"type":"element","tagName":"span","properties":{"className":["token","operator"]},"children":[{"type":"text","value":"<"}]},{"type":"text","value":"input type"},{"type":"element","tagName":"span","properties":{"className":["token","operator"]},"children":[{"type":"text","value":"="}]},{"type":"element","tagName":"span","properties":{"className":["token","string"]},"children":[{"type":"text","value":"\"text\""}]},{"type":"text","value":" name"},{"type":"element","tagName":"span","properties":{"className":["token","operator"]},"children":[{"type":"text","value":"="}]},{"type":"element","tagName":"span","properties":{"className":["token","string"]},"children":[{"type":"text","value":"\"number\""}]},{"type":"text","value":" placeholder"},{"type":"element","tagName":"span","properties":{"className":["token","operator"]},"children":[{"type":"text","value":"="}]},{"type":"element","tagName":"span","properties":{"className":["token","string"]},"children":[{"type":"text","value":"\"Enter a number (Optional)\""}]},{"type":"text","value":" "},{"type":"element","tagName":"span","properties":{"className":["token","operator"]},"children":[{"type":"text","value":"/"}]},{"type":"element","tagName":"span","properties":{"className":["token","operator"]},"children":[{"type":"text","value":">"}]},{"type":"text","value":"\n             "},{"type":"element","tagName":"span","properties":{"className":["token","operator"]},"children":[{"type":"text","value":"<"}]},{"type":"element","tagName":"span","properties":{"className":["token","operator"]},"children":[{"type":"text","value":"/"}]},{"type":"text","value":"div"},{"type":"element","tagName":"span","properties":{"className":["token","operator"]},"children":[{"type":"text","value":">"}]},{"type":"text","value":"\n             "},{"type":"element","tagName":"span","properties":{"className":["token","operator"]},"children":[{"type":"text","value":"<"}]},{"type":"text","value":"div"},{"type":"element","tagName":"span","properties":{"className":["token","operator"]},"children":[{"type":"text","value":">"}]},{"type":"text","value":"\n              "},{"type":"element","tagName":"span","properties":{"className":["token","operator"]},"children":[{"type":"text","value":"<"}]},{"type":"text","value":"select name"},{"type":"element","tagName":"span","properties":{"className":["token","operator"]},"children":[{"type":"text","value":"="}]},{"type":"element","tagName":"span","properties":{"className":["token","string"]},"children":[{"type":"text","value":"\"type\""}]},{"type":"element","tagName":"span","properties":{"className":["token","operator"]},"children":[{"type":"text","value":">"}]},{"type":"text","value":"\n               "},{"type":"element","tagName":"span","properties":{"className":["token","operator"]},"children":[{"type":"text","value":"<"}]},{"type":"text","value":"option value"},{"type":"element","tagName":"span","properties":{"className":["token","operator"]},"children":[{"type":"text","value":"="}]},{"type":"element","tagName":"span","properties":{"className":["token","string"]},"children":[{"type":"text","value":"\"trivia\""}]},{"type":"element","tagName":"span","properties":{"className":["token","operator"]},"children":[{"type":"text","value":">"}]},{"type":"text","value":"Trivia"},{"type":"element","tagName":"span","properties":{"className":["token","operator"]},"children":[{"type":"text","value":"<"}]},{"type":"element","tagName":"span","properties":{"className":["token","operator"]},"children":[{"type":"text","value":"/"}]},{"type":"text","value":"option"},{"type":"element","tagName":"span","properties":{"className":["token","operator"]},"children":[{"type":"text","value":">"}]},{"type":"text","value":"\n               "},{"type":"element","tagName":"span","properties":{"className":["token","operator"]},"children":[{"type":"text","value":"<"}]},{"type":"text","value":"option value"},{"type":"element","tagName":"span","properties":{"className":["token","operator"]},"children":[{"type":"text","value":"="}]},{"type":"element","tagName":"span","properties":{"className":["token","string"]},"children":[{"type":"text","value":"\"math\""}]},{"type":"element","tagName":"span","properties":{"className":["token","operator"]},"children":[{"type":"text","value":">"}]},{"type":"text","value":"Math"},{"type":"element","tagName":"span","properties":{"className":["token","operator"]},"children":[{"type":"text","value":"<"}]},{"type":"element","tagName":"span","properties":{"className":["token","operator"]},"children":[{"type":"text","value":"/"}]},{"type":"text","value":"option"},{"type":"element","tagName":"span","properties":{"className":["token","operator"]},"children":[{"type":"text","value":">"}]},{"type":"text","value":"\n               "},{"type":"element","tagName":"span","properties":{"className":["token","operator"]},"children":[{"type":"text","value":"<"}]},{"type":"text","value":"option value"},{"type":"element","tagName":"span","properties":{"className":["token","operator"]},"children":[{"type":"text","value":"="}]},{"type":"element","tagName":"span","properties":{"className":["token","string"]},"children":[{"type":"text","value":"\"date\""}]},{"type":"element","tagName":"span","properties":{"className":["token","operator"]},"children":[{"type":"text","value":">"}]},{"type":"text","value":"Date"},{"type":"element","tagName":"span","properties":{"className":["token","operator"]},"children":[{"type":"text","value":"<"}]},{"type":"element","tagName":"span","properties":{"className":["token","operator"]},"children":[{"type":"text","value":"/"}]},{"type":"text","value":"option"},{"type":"element","tagName":"span","properties":{"className":["token","operator"]},"children":[{"type":"text","value":">"}]},{"type":"text","value":"\n               "},{"type":"element","tagName":"span","properties":{"className":["token","operator"]},"children":[{"type":"text","value":"<"}]},{"type":"text","value":"option value"},{"type":"element","tagName":"span","properties":{"className":["token","operator"]},"children":[{"type":"text","value":"="}]},{"type":"element","tagName":"span","properties":{"className":["token","string"]},"children":[{"type":"text","value":"\"year\""}]},{"type":"element","tagName":"span","properties":{"className":["token","operator"]},"children":[{"type":"text","value":">"}]},{"type":"text","value":"Year"},{"type":"element","tagName":"span","properties":{"className":["token","operator"]},"children":[{"type":"text","value":"<"}]},{"type":"element","tagName":"span","properties":{"className":["token","operator"]},"children":[{"type":"text","value":"/"}]},{"type":"text","value":"option"},{"type":"element","tagName":"span","properties":{"className":["token","operator"]},"children":[{"type":"text","value":">"}]},{"type":"text","value":"\n              "},{"type":"element","tagName":"span","properties":{"className":["token","operator"]},"children":[{"type":"text","value":"<"}]},{"type":"element","tagName":"span","properties":{"className":["token","operator"]},"children":[{"type":"text","value":"/"}]},{"type":"text","value":"select"},{"type":"element","tagName":"span","properties":{"className":["token","operator"]},"children":[{"type":"text","value":">"}]},{"type":"text","value":"\n             "},{"type":"element","tagName":"span","properties":{"className":["token","operator"]},"children":[{"type":"text","value":"<"}]},{"type":"element","tagName":"span","properties":{"className":["token","operator"]},"children":[{"type":"text","value":"/"}]},{"type":"text","value":"div"},{"type":"element","tagName":"span","properties":{"className":["token","operator"]},"children":[{"type":"text","value":">"}]},{"type":"text","value":"\n             "},{"type":"element","tagName":"span","properties":{"className":["token","operator"]},"children":[{"type":"text","value":"<"}]},{"type":"text","value":"button type"},{"type":"element","tagName":"span","properties":{"className":["token","operator"]},"children":[{"type":"text","value":"="}]},{"type":"element","tagName":"span","properties":{"className":["token","string"]},"children":[{"type":"text","value":"\"submit\""}]},{"type":"element","tagName":"span","properties":{"className":["token","operator"]},"children":[{"type":"text","value":">"}]},{"type":"text","value":"Generate"},{"type":"element","tagName":"span","properties":{"className":["token","operator"]},"children":[{"type":"text","value":"<"}]},{"type":"element","tagName":"span","properties":{"className":["token","operator"]},"children":[{"type":"text","value":"/"}]},{"type":"text","value":"button"},{"type":"element","tagName":"span","properties":{"className":["token","operator"]},"children":[{"type":"text","value":">"}]},{"type":"text","value":"\n            "},{"type":"element","tagName":"span","properties":{"className":["token","operator"]},"children":[{"type":"text","value":"<"}]},{"type":"element","tagName":"span","properties":{"className":["token","operator"]},"children":[{"type":"text","value":"/"}]},{"type":"text","value":"form"},{"type":"element","tagName":"span","properties":{"className":["token","operator"]},"children":[{"type":"text","value":">"}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":")"}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":";"}]},{"type":"text","value":" \n"},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"}"}]}]}]}]},{"type":"element","tagName":"p","properties":{},"children":[{"type":"text","value":"This will create the form with the "},{"type":"element","tagName":"strong","properties":{},"children":[{"type":"element","tagName":"strong","properties":{},"children":[{"type":"text","value":"text "}]}]},{"type":"text","value":"input and "},{"type":"element","tagName":"strong","properties":{},"children":[{"type":"element","tagName":"strong","properties":{},"children":[{"type":"text","value":"select "}]}]},{"type":"text","value":"and "},{"type":"element","tagName":"strong","properties":{},"children":[{"type":"element","tagName":"strong","properties":{},"children":[{"type":"text","value":"button "}]}]},{"type":"text","value":"elements."}]},{"type":"element","tagName":"p","properties":{},"children":[{"type":"text","value":"After that, we need to import and render the "},{"type":"element","tagName":"strong","properties":{},"children":[{"type":"element","tagName":"strong","properties":{},"children":[{"type":"text","value":"Form"}]}]},{"type":"text","value":" component in our "},{"type":"element","tagName":"strong","properties":{},"children":[{"type":"element","tagName":"strong","properties":{},"children":[{"type":"text","value":"App "}]}]},{"type":"text","value":"component:"}]},{"type":"element","tagName":"div","properties":{"className":["kg-card","kg-code-card","gatsby-highlight"],"dataLanguage":"js"},"children":[{"type":"element","tagName":"pre","properties":{"className":["language-js"]},"children":[{"type":"element","tagName":"code","properties":{"className":["language-js"]},"children":[{"type":"element","tagName":"span","properties":{"className":["token","keyword"]},"children":[{"type":"text","value":"import"}]},{"type":"text","value":" React"},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":","}]},{"type":"text","value":" "},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"{"}]},{"type":"text","value":"useEffect"},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"}"}]},{"type":"text","value":" "},{"type":"element","tagName":"span","properties":{"className":["token","keyword"]},"children":[{"type":"text","value":"from"}]},{"type":"text","value":" "},{"type":"element","tagName":"span","properties":{"className":["token","string"]},"children":[{"type":"text","value":"'react'"}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":";"}]},{"type":"text","value":"\n"},{"type":"element","tagName":"span","properties":{"className":["token","keyword"]},"children":[{"type":"text","value":"import"}]},{"type":"text","value":" Form "},{"type":"element","tagName":"span","properties":{"className":["token","keyword"]},"children":[{"type":"text","value":"from"}]},{"type":"text","value":" "},{"type":"element","tagName":"span","properties":{"className":["token","string"]},"children":[{"type":"text","value":"'./Form'"}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":";"}]},{"type":"text","value":"\n"},{"type":"element","tagName":"span","properties":{"className":["token","keyword"]},"children":[{"type":"text","value":"import"}]},{"type":"text","value":" "},{"type":"element","tagName":"span","properties":{"className":["token","string"]},"children":[{"type":"text","value":"'../styles/App.css'"}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":";"}]},{"type":"text","value":"  \n"},{"type":"element","tagName":"span","properties":{"className":["token","keyword"]},"children":[{"type":"text","value":"function"}]},{"type":"text","value":" "},{"type":"element","tagName":"span","properties":{"className":["token","function"]},"children":[{"type":"text","value":"App"}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"("}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":")"}]},{"type":"text","value":" "},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"{"}]},{"type":"text","value":"\n   "},{"type":"element","tagName":"span","properties":{"className":["token","function"]},"children":[{"type":"text","value":"useEffect"}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"("}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"("}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":")"}]},{"type":"text","value":" "},{"type":"element","tagName":"span","properties":{"className":["token","operator"]},"children":[{"type":"text","value":"=>"}]},{"type":"text","value":" "},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"{"}]},{"type":"text","value":"\n     "},{"type":"element","tagName":"span","properties":{"className":["token","function"]},"children":[{"type":"text","value":"setTimeout"}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"("}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"("}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":")"}]},{"type":"text","value":" "},{"type":"element","tagName":"span","properties":{"className":["token","operator"]},"children":[{"type":"text","value":"=>"}]},{"type":"text","value":" "},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"{"}]},{"type":"text","value":"\n       "},{"type":"element","tagName":"span","properties":{"className":["token","keyword"]},"children":[{"type":"text","value":"let"}]},{"type":"text","value":" welcomeMessage "},{"type":"element","tagName":"span","properties":{"className":["token","operator"]},"children":[{"type":"text","value":"="}]},{"type":"text","value":" document"},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"."}]},{"type":"element","tagName":"span","properties":{"className":["token","function"]},"children":[{"type":"text","value":"getElementById"}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"("}]},{"type":"element","tagName":"span","properties":{"className":["token","string"]},"children":[{"type":"text","value":"\"welcomeMessage\""}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":")"}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":";"}]},{"type":"text","value":"\n       welcomeMessage"},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"."}]},{"type":"text","value":"innerHTML "},{"type":"element","tagName":"span","properties":{"className":["token","operator"]},"children":[{"type":"text","value":"="}]},{"type":"text","value":" "},{"type":"element","tagName":"span","properties":{"className":["token","string"]},"children":[{"type":"text","value":"\"Try Out Our Trivia Generator!\""}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":";"}]},{"type":"text","value":"\n     "},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"}"}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":","}]},{"type":"text","value":" "},{"type":"element","tagName":"span","properties":{"className":["token","number"]},"children":[{"type":"text","value":"3000"}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":")"}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":";"}]},{"type":"text","value":"\n   "},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"}"}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":","}]},{"type":"text","value":" "},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"["}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"]"}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":")"}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":";"}]},{"type":"text","value":"\n   "},{"type":"element","tagName":"span","properties":{"className":["token","keyword"]},"children":[{"type":"text","value":"return"}]},{"type":"text","value":" "},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"("}]},{"type":"text","value":"\n     "},{"type":"element","tagName":"span","properties":{"className":["token","operator"]},"children":[{"type":"text","value":"<"}]},{"type":"text","value":"div className"},{"type":"element","tagName":"span","properties":{"className":["token","operator"]},"children":[{"type":"text","value":"="}]},{"type":"element","tagName":"span","properties":{"className":["token","string"]},"children":[{"type":"text","value":"\"App\""}]},{"type":"element","tagName":"span","properties":{"className":["token","operator"]},"children":[{"type":"text","value":">"}]},{"type":"text","value":"\n       "},{"type":"element","tagName":"span","properties":{"className":["token","operator"]},"children":[{"type":"text","value":"<"}]},{"type":"text","value":"header className"},{"type":"element","tagName":"span","properties":{"className":["token","operator"]},"children":[{"type":"text","value":"="}]},{"type":"element","tagName":"span","properties":{"className":["token","string"]},"children":[{"type":"text","value":"\"App-header\""}]},{"type":"element","tagName":"span","properties":{"className":["token","operator"]},"children":[{"type":"text","value":">"}]},{"type":"text","value":"\n         "},{"type":"element","tagName":"span","properties":{"className":["token","operator"]},"children":[{"type":"text","value":"<"}]},{"type":"text","value":"h1 id"},{"type":"element","tagName":"span","properties":{"className":["token","operator"]},"children":[{"type":"text","value":"="}]},{"type":"element","tagName":"span","properties":{"className":["token","string"]},"children":[{"type":"text","value":"\"welcomeMessage\""}]},{"type":"element","tagName":"span","properties":{"className":["token","operator"]},"children":[{"type":"text","value":">"}]},{"type":"text","value":"Welcome to Numbers Trivia"},{"type":"element","tagName":"span","properties":{"className":["token","operator"]},"children":[{"type":"text","value":"!"}]},{"type":"element","tagName":"span","properties":{"className":["token","operator"]},"children":[{"type":"text","value":"<"}]},{"type":"element","tagName":"span","properties":{"className":["token","operator"]},"children":[{"type":"text","value":"/"}]},{"type":"text","value":"h1"},{"type":"element","tagName":"span","properties":{"className":["token","operator"]},"children":[{"type":"text","value":">"}]},{"type":"text","value":"\n         "},{"type":"element","tagName":"span","properties":{"className":["token","operator"]},"children":[{"type":"text","value":"<"}]},{"type":"text","value":"Form "},{"type":"element","tagName":"span","properties":{"className":["token","operator"]},"children":[{"type":"text","value":"/"}]},{"type":"element","tagName":"span","properties":{"className":["token","operator"]},"children":[{"type":"text","value":">"}]},{"type":"text","value":"\n       "},{"type":"element","tagName":"span","properties":{"className":["token","operator"]},"children":[{"type":"text","value":"<"}]},{"type":"element","tagName":"span","properties":{"className":["token","operator"]},"children":[{"type":"text","value":"/"}]},{"type":"text","value":"header"},{"type":"element","tagName":"span","properties":{"className":["token","operator"]},"children":[{"type":"text","value":">"}]},{"type":"text","value":"\n     "},{"type":"element","tagName":"span","properties":{"className":["token","operator"]},"children":[{"type":"text","value":"<"}]},{"type":"element","tagName":"span","properties":{"className":["token","operator"]},"children":[{"type":"text","value":"/"}]},{"type":"text","value":"div"},{"type":"element","tagName":"span","properties":{"className":["token","operator"]},"children":[{"type":"text","value":">"}]},{"type":"text","value":"\n   "},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":")"}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":";"}]},{"type":"text","value":"\n "},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"}"}]},{"type":"text","value":"\n"},{"type":"element","tagName":"span","properties":{"className":["token","keyword"]},"children":[{"type":"text","value":"export"}]},{"type":"text","value":" "},{"type":"element","tagName":"span","properties":{"className":["token","keyword"]},"children":[{"type":"text","value":"default"}]},{"type":"text","value":" App"},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":";"}]}]}]}]},{"type":"element","tagName":"p","properties":{},"children":[{"type":"text","value":"We have changed the imports to import our "},{"type":"element","tagName":"code","properties":{"className":["language-text"]},"children":[{"type":"text","value":"Form"}]},{"type":"element","tagName":"strong","properties":{},"children":[{"type":"element","tagName":"strong","properties":{},"children":[{"type":"text","value":" "}]}]},{"type":"text","value":"component, and we added "},{"type":"element","tagName":"code","properties":{"className":["language-text"]},"children":[{"type":"text","value":"<Form />"}]},{"type":"element","tagName":"strong","properties":{},"children":[{"type":"element","tagName":"strong","properties":{},"children":[{"type":"text","value":" "}]}]},{"type":"text","value":"to render the form."}]},{"type":"element","tagName":"p","properties":{},"children":[{"type":"text","value":"Let’s also add more styles just to make our form look a little better. Add the following at the end of "},{"type":"element","tagName":"strong","properties":{},"children":[{"type":"element","tagName":"strong","properties":{},"children":[{"type":"text","value":"App.css"}]}]},{"type":"text","value":":"}]},{"type":"element","tagName":"div","properties":{"className":["kg-card","kg-code-card","gatsby-highlight"],"dataLanguage":"css"},"children":[{"type":"element","tagName":"pre","properties":{"className":["language-css"]},"children":[{"type":"element","tagName":"code","properties":{"className":["language-css"]},"children":[{"type":"element","tagName":"span","properties":{"className":["token","selector"]},"children":[{"type":"text","value":"form"}]},{"type":"text","value":" "},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"{"}]},{"type":"text","value":"\n   "},{"type":"element","tagName":"span","properties":{"className":["token","property"]},"children":[{"type":"text","value":"font-size"}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":":"}]},{"type":"text","value":" 15px"},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":";"}]},{"type":"text","value":"\n"},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"}"}]},{"type":"text","value":"  \n"},{"type":"element","tagName":"span","properties":{"className":["token","selector"]},"children":[{"type":"text","value":"form input, form select"}]},{"type":"text","value":" "},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"{"}]},{"type":"text","value":"\n   "},{"type":"element","tagName":"span","properties":{"className":["token","property"]},"children":[{"type":"text","value":"padding"}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":":"}]},{"type":"text","value":" 5px"},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":";"}]},{"type":"text","value":"\n"},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"}"}]},{"type":"text","value":"  \n"},{"type":"element","tagName":"span","properties":{"className":["token","selector"]},"children":[{"type":"text","value":"form select"}]},{"type":"text","value":" "},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"{"}]},{"type":"text","value":"\n   "},{"type":"element","tagName":"span","properties":{"className":["token","property"]},"children":[{"type":"text","value":"width"}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":":"}]},{"type":"text","value":" 100%"},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":";"}]},{"type":"text","value":" \n"},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"}"}]},{"type":"text","value":"  \n"},{"type":"element","tagName":"span","properties":{"className":["token","selector"]},"children":[{"type":"text","value":"form div"}]},{"type":"text","value":" "},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"{"}]},{"type":"text","value":"\n   "},{"type":"element","tagName":"span","properties":{"className":["token","property"]},"children":[{"type":"text","value":"margin-bottom"}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":":"}]},{"type":"text","value":" 8px"},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":";"}]},{"type":"text","value":"\n"},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"}"}]}]}]}]},{"type":"element","tagName":"p","properties":{},"children":[{"type":"text","value":"If you refresh the page now, you will find it has changed and now it’s showing our form."}]},{"type":"element","tagName":"p","properties":{},"children":[{"type":"text","value":"Now, we need to add some logic to our form. "},{"type":"element","tagName":"strong","properties":{},"children":[{"type":"element","tagName":"strong","properties":{},"children":[{"type":"text","value":"On Submit"}]}]},{"type":"text","value":", we need to get the values of our input elements, then call the API to retrieve the results."}]},{"type":"element","tagName":"p","properties":{},"children":[{"type":"text","value":"To handle form elements and their values, you need to use the state of the component. You make the value of the element equal to a property in the state of the component."}]},{"type":"element","tagName":"p","properties":{},"children":[{"type":"text","value":"Before hooks, in order to get the value in the state you would have to use "},{"type":"element","tagName":"code","properties":{"className":["language-text"]},"children":[{"type":"text","value":"this.state.value"}]},{"type":"element","tagName":"strong","properties":{},"children":[{"type":"element","tagName":"strong","properties":{},"children":[{"type":"text","value":", "}]}]},{"type":"text","value":"and then to set the state, you will need to call "},{"type":"element","tagName":"code","properties":{"className":["language-text"]},"children":[{"type":"text","value":"this.setState"}]},{"type":"text","value":"."}]},{"type":"element","tagName":"p","properties":{},"children":[{"type":"text","value":"Now, you can use the state hook. The state hook is the "},{"type":"element","tagName":"code","properties":{"className":["language-text"]},"children":[{"type":"text","value":"useState"}]},{"type":"element","tagName":"strong","properties":{},"children":[{"type":"element","tagName":"strong","properties":{},"children":[{"type":"text","value":" "}]}]},{"type":"text","value":"function. It accepts one argument, which is the initial value, and it returns a pair of values: the "},{"type":"element","tagName":"strong","properties":{},"children":[{"type":"element","tagName":"strong","properties":{},"children":[{"type":"text","value":"current state"}]}]},{"type":"text","value":" and a "},{"type":"element","tagName":"strong","properties":{},"children":[{"type":"element","tagName":"strong","properties":{},"children":[{"type":"text","value":"function"}]}]},{"type":"text","value":" that updates it. Then, you will be able to access the current state using the first returned value, and update it using the second returned value which is the function."}]},{"type":"element","tagName":"p","properties":{},"children":[{"type":"text","value":"Here’s an example:"}]},{"type":"element","tagName":"div","properties":{"className":["kg-card","kg-code-card","gatsby-highlight"],"dataLanguage":"js"},"children":[{"type":"element","tagName":"pre","properties":{"className":["language-js"]},"children":[{"type":"element","tagName":"code","properties":{"className":["language-js"]},"children":[{"type":"element","tagName":"span","properties":{"className":["token","keyword"]},"children":[{"type":"text","value":"const"}]},{"type":"text","value":" "},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"["}]},{"type":"text","value":"count"},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":","}]},{"type":"text","value":" setCount"},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"]"}]},{"type":"text","value":" "},{"type":"element","tagName":"span","properties":{"className":["token","operator"]},"children":[{"type":"text","value":"="}]},{"type":"text","value":" "},{"type":"element","tagName":"span","properties":{"className":["token","function"]},"children":[{"type":"text","value":"useState"}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"("}]},{"type":"element","tagName":"span","properties":{"className":["token","number"]},"children":[{"type":"text","value":"0"}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":")"}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":";"}]}]}]}]},{"type":"element","tagName":"p","properties":{},"children":[{"type":"text","value":"in this example, we call the "},{"type":"element","tagName":"code","properties":{"className":["language-text"]},"children":[{"type":"text","value":"useState"}]},{"type":"element","tagName":"strong","properties":{},"children":[{"type":"element","tagName":"strong","properties":{},"children":[{"type":"text","value":" "}]}]},{"type":"text","value":"hook and pass it 0, and we set the returned value equal to "},{"type":"element","tagName":"code","properties":{"className":["language-text"]},"children":[{"type":"text","value":"count"}]},{"type":"text","value":" and "},{"type":"element","tagName":"code","properties":{"className":["language-text"]},"children":[{"type":"text","value":"setCount"}]},{"type":"text","value":". This means that we have created state variable called "},{"type":"element","tagName":"code","properties":{"className":["language-text"]},"children":[{"type":"text","value":"count"}]},{"type":"text","value":". Its initial value is 0, and to change its value we can use "},{"type":"element","tagName":"code","properties":{"className":["language-text"]},"children":[{"type":"text","value":"setCount"}]},{"type":"text","value":"."}]},{"type":"element","tagName":"p","properties":{},"children":[{"type":"text","value":"For our "},{"type":"element","tagName":"code","properties":{"className":["language-text"]},"children":[{"type":"text","value":"Form"}]},{"type":"element","tagName":"strong","properties":{},"children":[{"type":"element","tagName":"strong","properties":{},"children":[{"type":"text","value":" "}]}]},{"type":"text","value":"component, we need two state variables, one for the text"},{"type":"element","tagName":"strong","properties":{},"children":[{"type":"element","tagName":"strong","properties":{},"children":[{"type":"text","value":" "}]}]},{"type":"text","value":"input which we will call "},{"type":"element","tagName":"code","properties":{"className":["language-text"]},"children":[{"type":"text","value":"number"}]},{"type":"text","value":", and one for the select"},{"type":"element","tagName":"strong","properties":{},"children":[{"type":"element","tagName":"strong","properties":{},"children":[{"type":"text","value":" "}]}]},{"type":"text","value":"input which we will call "},{"type":"element","tagName":"code","properties":{"className":["language-text"]},"children":[{"type":"text","value":"type"}]},{"type":"text","value":". Then, on change event for these two input elements, we will change the values for "},{"type":"element","tagName":"code","properties":{"className":["language-text"]},"children":[{"type":"text","value":"number"}]},{"type":"element","tagName":"strong","properties":{},"children":[{"type":"element","tagName":"strong","properties":{},"children":[{"type":"text","value":" "}]}]},{"type":"text","value":"and "},{"type":"element","tagName":"code","properties":{"className":["language-text"]},"children":[{"type":"text","value":"type"}]},{"type":"text","value":" using the function returned by "},{"type":"element","tagName":"code","properties":{"className":["language-text"]},"children":[{"type":"text","value":"setState"}]},{"type":"element","tagName":"strong","properties":{},"children":[{"type":"element","tagName":"strong","properties":{},"children":[{"type":"text","value":"."}]}]}]},{"type":"element","tagName":"p","properties":{},"children":[{"type":"text","value":"Open our "},{"type":"element","tagName":"code","properties":{"className":["language-text"]},"children":[{"type":"text","value":"Form"}]},{"type":"element","tagName":"strong","properties":{},"children":[{"type":"element","tagName":"strong","properties":{},"children":[{"type":"text","value":" "}]}]},{"type":"text","value":"component and change it to the following:"}]},{"type":"element","tagName":"div","properties":{"className":["kg-card","kg-code-card","gatsby-highlight"],"dataLanguage":"js"},"children":[{"type":"element","tagName":"pre","properties":{"className":["language-js"]},"children":[{"type":"element","tagName":"code","properties":{"className":["language-js"]},"children":[{"type":"element","tagName":"span","properties":{"className":["token","keyword"]},"children":[{"type":"text","value":"import"}]},{"type":"text","value":" React"},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":","}]},{"type":"text","value":" "},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"{"}]},{"type":"text","value":" useState "},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"}"}]},{"type":"text","value":" "},{"type":"element","tagName":"span","properties":{"className":["token","keyword"]},"children":[{"type":"text","value":"from"}]},{"type":"text","value":" "},{"type":"element","tagName":"span","properties":{"className":["token","string"]},"children":[{"type":"text","value":"'react'"}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":";"}]},{"type":"text","value":"\n"},{"type":"element","tagName":"span","properties":{"className":["token","keyword"]},"children":[{"type":"text","value":"function"}]},{"type":"text","value":" "},{"type":"element","tagName":"span","properties":{"className":["token","function"]},"children":[{"type":"text","value":"Form"}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"("}]},{"type":"element","tagName":"span","properties":{"className":["token","parameter"]},"children":[{"type":"text","value":"props"}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":")"}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"{"}]},{"type":"text","value":"\n  "},{"type":"element","tagName":"span","properties":{"className":["token","keyword"]},"children":[{"type":"text","value":"let"}]},{"type":"text","value":" "},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"["}]},{"type":"text","value":"number"},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":","}]},{"type":"text","value":" setNumber"},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"]"}]},{"type":"text","value":" "},{"type":"element","tagName":"span","properties":{"className":["token","operator"]},"children":[{"type":"text","value":"="}]},{"type":"text","value":" "},{"type":"element","tagName":"span","properties":{"className":["token","function"]},"children":[{"type":"text","value":"useState"}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"("}]},{"type":"element","tagName":"span","properties":{"className":["token","string"]},"children":[{"type":"text","value":"\"random\""}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":")"}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":";"}]},{"type":"text","value":"\n  "},{"type":"element","tagName":"span","properties":{"className":["token","keyword"]},"children":[{"type":"text","value":"let"}]},{"type":"text","value":" "},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"["}]},{"type":"text","value":"type"},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":","}]},{"type":"text","value":" setType"},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"]"}]},{"type":"text","value":" "},{"type":"element","tagName":"span","properties":{"className":["token","operator"]},"children":[{"type":"text","value":"="}]},{"type":"text","value":" "},{"type":"element","tagName":"span","properties":{"className":["token","function"]},"children":[{"type":"text","value":"useState"}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"("}]},{"type":"element","tagName":"span","properties":{"className":["token","string"]},"children":[{"type":"text","value":"\"trivia\""}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":")"}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":";"}]},{"type":"text","value":"\n  "},{"type":"element","tagName":"span","properties":{"className":["token","keyword"]},"children":[{"type":"text","value":"function"}]},{"type":"text","value":" "},{"type":"element","tagName":"span","properties":{"className":["token","function"]},"children":[{"type":"text","value":"onNumberChanged"}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"("}]},{"type":"element","tagName":"span","properties":{"className":["token","parameter"]},"children":[{"type":"text","value":"e"}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":")"}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"{"}]},{"type":"text","value":"\n    "},{"type":"element","tagName":"span","properties":{"className":["token","keyword"]},"children":[{"type":"text","value":"let"}]},{"type":"text","value":" value "},{"type":"element","tagName":"span","properties":{"className":["token","operator"]},"children":[{"type":"text","value":"="}]},{"type":"text","value":" e"},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"."}]},{"type":"text","value":"target"},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"."}]},{"type":"text","value":"value"},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"."}]},{"type":"element","tagName":"span","properties":{"className":["token","function"]},"children":[{"type":"text","value":"trim"}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"("}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":")"}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":";"}]},{"type":"text","value":"\n    "},{"type":"element","tagName":"span","properties":{"className":["token","keyword"]},"children":[{"type":"text","value":"if"}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"("}]},{"type":"element","tagName":"span","properties":{"className":["token","operator"]},"children":[{"type":"text","value":"!"}]},{"type":"text","value":"value"},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"."}]},{"type":"text","value":"length"},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":")"}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"{"}]},{"type":"text","value":"\n      "},{"type":"element","tagName":"span","properties":{"className":["token","function"]},"children":[{"type":"text","value":"setNumber"}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"("}]},{"type":"element","tagName":"span","properties":{"className":["token","string"]},"children":[{"type":"text","value":"\"random\""}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":")"}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":";"}]},{"type":"text","value":" "},{"type":"element","tagName":"span","properties":{"className":["token","comment"]},"children":[{"type":"text","value":"//default value"}]},{"type":"text","value":"\n    "},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"}"}]},{"type":"text","value":" "},{"type":"element","tagName":"span","properties":{"className":["token","keyword"]},"children":[{"type":"text","value":"else"}]},{"type":"text","value":" "},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"{"}]},{"type":"text","value":"\n      "},{"type":"element","tagName":"span","properties":{"className":["token","function"]},"children":[{"type":"text","value":"setNumber"}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"("}]},{"type":"text","value":"value"},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":")"}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":";"}]},{"type":"text","value":"\n    "},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"}"}]},{"type":"text","value":"\n  "},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"}"}]},{"type":"text","value":"\n  "},{"type":"element","tagName":"span","properties":{"className":["token","keyword"]},"children":[{"type":"text","value":"function"}]},{"type":"text","value":" "},{"type":"element","tagName":"span","properties":{"className":["token","function"]},"children":[{"type":"text","value":"onTypeChanged"}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"("}]},{"type":"element","tagName":"span","properties":{"className":["token","parameter"]},"children":[{"type":"text","value":"e"}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":")"}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"{"}]},{"type":"text","value":"\n    "},{"type":"element","tagName":"span","properties":{"className":["token","keyword"]},"children":[{"type":"text","value":"let"}]},{"type":"text","value":" value "},{"type":"element","tagName":"span","properties":{"className":["token","operator"]},"children":[{"type":"text","value":"="}]},{"type":"text","value":" e"},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"."}]},{"type":"text","value":"target"},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"."}]},{"type":"text","value":"value"},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"."}]},{"type":"element","tagName":"span","properties":{"className":["token","function"]},"children":[{"type":"text","value":"trim"}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"("}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":")"}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":";"}]},{"type":"text","value":"\n    "},{"type":"element","tagName":"span","properties":{"className":["token","keyword"]},"children":[{"type":"text","value":"if"}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"("}]},{"type":"element","tagName":"span","properties":{"className":["token","operator"]},"children":[{"type":"text","value":"!"}]},{"type":"text","value":"value"},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"."}]},{"type":"text","value":"length"},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":")"}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"{"}]},{"type":"text","value":"\n      "},{"type":"element","tagName":"span","properties":{"className":["token","function"]},"children":[{"type":"text","value":"setType"}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"("}]},{"type":"element","tagName":"span","properties":{"className":["token","string"]},"children":[{"type":"text","value":"\"trivia\""}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":")"}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":";"}]},{"type":"text","value":" "},{"type":"element","tagName":"span","properties":{"className":["token","comment"]},"children":[{"type":"text","value":"//default value"}]},{"type":"text","value":"\n    "},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"}"}]},{"type":"text","value":" "},{"type":"element","tagName":"span","properties":{"className":["token","keyword"]},"children":[{"type":"text","value":"else"}]},{"type":"text","value":" "},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"{"}]},{"type":"text","value":"\n      "},{"type":"element","tagName":"span","properties":{"className":["token","function"]},"children":[{"type":"text","value":"setType"}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"("}]},{"type":"text","value":"value"},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":")"}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":";"}]},{"type":"text","value":"\n    "},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"}"}]},{"type":"text","value":"\n  "},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"}"}]},{"type":"text","value":"\n  "},{"type":"element","tagName":"span","properties":{"className":["token","keyword"]},"children":[{"type":"text","value":"return"}]},{"type":"text","value":" "},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"("}]},{"type":"element","tagName":"span","properties":{"className":["token","operator"]},"children":[{"type":"text","value":"<"}]},{"type":"text","value":"form"},{"type":"element","tagName":"span","properties":{"className":["token","operator"]},"children":[{"type":"text","value":">"}]},{"type":"text","value":"\n            "},{"type":"element","tagName":"span","properties":{"className":["token","operator"]},"children":[{"type":"text","value":"<"}]},{"type":"text","value":"div"},{"type":"element","tagName":"span","properties":{"className":["token","operator"]},"children":[{"type":"text","value":">"}]},{"type":"text","value":"\n              "},{"type":"element","tagName":"span","properties":{"className":["token","operator"]},"children":[{"type":"text","value":"<"}]},{"type":"text","value":"input type"},{"type":"element","tagName":"span","properties":{"className":["token","operator"]},"children":[{"type":"text","value":"="}]},{"type":"element","tagName":"span","properties":{"className":["token","string"]},"children":[{"type":"text","value":"\"text\""}]},{"type":"text","value":" name"},{"type":"element","tagName":"span","properties":{"className":["token","operator"]},"children":[{"type":"text","value":"="}]},{"type":"element","tagName":"span","properties":{"className":["token","string"]},"children":[{"type":"text","value":"\"number\""}]},{"type":"text","value":" placeholder"},{"type":"element","tagName":"span","properties":{"className":["token","operator"]},"children":[{"type":"text","value":"="}]},{"type":"element","tagName":"span","properties":{"className":["token","string"]},"children":[{"type":"text","value":"\"Enter a number (Optional)\""}]},{"type":"text","value":" value"},{"type":"element","tagName":"span","properties":{"className":["token","operator"]},"children":[{"type":"text","value":"="}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"{"}]},{"type":"text","value":"number"},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"}"}]},{"type":"text","value":" onChange"},{"type":"element","tagName":"span","properties":{"className":["token","operator"]},"children":[{"type":"text","value":"="}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"{"}]},{"type":"text","value":"onNumberChanged"},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"}"}]},{"type":"text","value":" "},{"type":"element","tagName":"span","properties":{"className":["token","operator"]},"children":[{"type":"text","value":"/"}]},{"type":"element","tagName":"span","properties":{"className":["token","operator"]},"children":[{"type":"text","value":">"}]},{"type":"text","value":"\n            "},{"type":"element","tagName":"span","properties":{"className":["token","operator"]},"children":[{"type":"text","value":"<"}]},{"type":"element","tagName":"span","properties":{"className":["token","operator"]},"children":[{"type":"text","value":"/"}]},{"type":"text","value":"div"},{"type":"element","tagName":"span","properties":{"className":["token","operator"]},"children":[{"type":"text","value":">"}]},{"type":"text","value":"\n            "},{"type":"element","tagName":"span","properties":{"className":["token","operator"]},"children":[{"type":"text","value":"<"}]},{"type":"text","value":"div"},{"type":"element","tagName":"span","properties":{"className":["token","operator"]},"children":[{"type":"text","value":">"}]},{"type":"text","value":"\n              "},{"type":"element","tagName":"span","properties":{"className":["token","operator"]},"children":[{"type":"text","value":"<"}]},{"type":"text","value":"select name"},{"type":"element","tagName":"span","properties":{"className":["token","operator"]},"children":[{"type":"text","value":"="}]},{"type":"element","tagName":"span","properties":{"className":["token","string"]},"children":[{"type":"text","value":"\"type\""}]},{"type":"text","value":" value"},{"type":"element","tagName":"span","properties":{"className":["token","operator"]},"children":[{"type":"text","value":"="}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"{"}]},{"type":"text","value":"type"},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"}"}]},{"type":"text","value":" onChange"},{"type":"element","tagName":"span","properties":{"className":["token","operator"]},"children":[{"type":"text","value":"="}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"{"}]},{"type":"text","value":"onTypeChanged"},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"}"}]},{"type":"element","tagName":"span","properties":{"className":["token","operator"]},"children":[{"type":"text","value":">"}]},{"type":"text","value":"\n                "},{"type":"element","tagName":"span","properties":{"className":["token","operator"]},"children":[{"type":"text","value":"<"}]},{"type":"text","value":"option value"},{"type":"element","tagName":"span","properties":{"className":["token","operator"]},"children":[{"type":"text","value":"="}]},{"type":"element","tagName":"span","properties":{"className":["token","string"]},"children":[{"type":"text","value":"\"trivia\""}]},{"type":"element","tagName":"span","properties":{"className":["token","operator"]},"children":[{"type":"text","value":">"}]},{"type":"text","value":"Trivia"},{"type":"element","tagName":"span","properties":{"className":["token","operator"]},"children":[{"type":"text","value":"<"}]},{"type":"element","tagName":"span","properties":{"className":["token","operator"]},"children":[{"type":"text","value":"/"}]},{"type":"text","value":"option"},{"type":"element","tagName":"span","properties":{"className":["token","operator"]},"children":[{"type":"text","value":">"}]},{"type":"text","value":"\n                "},{"type":"element","tagName":"span","properties":{"className":["token","operator"]},"children":[{"type":"text","value":"<"}]},{"type":"text","value":"option value"},{"type":"element","tagName":"span","properties":{"className":["token","operator"]},"children":[{"type":"text","value":"="}]},{"type":"element","tagName":"span","properties":{"className":["token","string"]},"children":[{"type":"text","value":"\"math\""}]},{"type":"element","tagName":"span","properties":{"className":["token","operator"]},"children":[{"type":"text","value":">"}]},{"type":"text","value":"Math"},{"type":"element","tagName":"span","properties":{"className":["token","operator"]},"children":[{"type":"text","value":"<"}]},{"type":"element","tagName":"span","properties":{"className":["token","operator"]},"children":[{"type":"text","value":"/"}]},{"type":"text","value":"option"},{"type":"element","tagName":"span","properties":{"className":["token","operator"]},"children":[{"type":"text","value":">"}]},{"type":"text","value":"\n                "},{"type":"element","tagName":"span","properties":{"className":["token","operator"]},"children":[{"type":"text","value":"<"}]},{"type":"text","value":"option value"},{"type":"element","tagName":"span","properties":{"className":["token","operator"]},"children":[{"type":"text","value":"="}]},{"type":"element","tagName":"span","properties":{"className":["token","string"]},"children":[{"type":"text","value":"\"date\""}]},{"type":"element","tagName":"span","properties":{"className":["token","operator"]},"children":[{"type":"text","value":">"}]},{"type":"text","value":"Date"},{"type":"element","tagName":"span","properties":{"className":["token","operator"]},"children":[{"type":"text","value":"<"}]},{"type":"element","tagName":"span","properties":{"className":["token","operator"]},"children":[{"type":"text","value":"/"}]},{"type":"text","value":"option"},{"type":"element","tagName":"span","properties":{"className":["token","operator"]},"children":[{"type":"text","value":">"}]},{"type":"text","value":"\n                "},{"type":"element","tagName":"span","properties":{"className":["token","operator"]},"children":[{"type":"text","value":"<"}]},{"type":"text","value":"option value"},{"type":"element","tagName":"span","properties":{"className":["token","operator"]},"children":[{"type":"text","value":"="}]},{"type":"element","tagName":"span","properties":{"className":["token","string"]},"children":[{"type":"text","value":"\"year\""}]},{"type":"element","tagName":"span","properties":{"className":["token","operator"]},"children":[{"type":"text","value":">"}]},{"type":"text","value":"Year"},{"type":"element","tagName":"span","properties":{"className":["token","operator"]},"children":[{"type":"text","value":"<"}]},{"type":"element","tagName":"span","properties":{"className":["token","operator"]},"children":[{"type":"text","value":"/"}]},{"type":"text","value":"option"},{"type":"element","tagName":"span","properties":{"className":["token","operator"]},"children":[{"type":"text","value":">"}]},{"type":"text","value":"\n              "},{"type":"element","tagName":"span","properties":{"className":["token","operator"]},"children":[{"type":"text","value":"<"}]},{"type":"element","tagName":"span","properties":{"className":["token","operator"]},"children":[{"type":"text","value":"/"}]},{"type":"text","value":"select"},{"type":"element","tagName":"span","properties":{"className":["token","operator"]},"children":[{"type":"text","value":">"}]},{"type":"text","value":"\n            "},{"type":"element","tagName":"span","properties":{"className":["token","operator"]},"children":[{"type":"text","value":"<"}]},{"type":"element","tagName":"span","properties":{"className":["token","operator"]},"children":[{"type":"text","value":"/"}]},{"type":"text","value":"div"},{"type":"element","tagName":"span","properties":{"className":["token","operator"]},"children":[{"type":"text","value":">"}]},{"type":"text","value":"\n            "},{"type":"element","tagName":"span","properties":{"className":["token","operator"]},"children":[{"type":"text","value":"<"}]},{"type":"text","value":"button type"},{"type":"element","tagName":"span","properties":{"className":["token","operator"]},"children":[{"type":"text","value":"="}]},{"type":"element","tagName":"span","properties":{"className":["token","string"]},"children":[{"type":"text","value":"\"submit\""}]},{"type":"element","tagName":"span","properties":{"className":["token","operator"]},"children":[{"type":"text","value":">"}]},{"type":"text","value":"Generate"},{"type":"element","tagName":"span","properties":{"className":["token","operator"]},"children":[{"type":"text","value":"<"}]},{"type":"element","tagName":"span","properties":{"className":["token","operator"]},"children":[{"type":"text","value":"/"}]},{"type":"text","value":"button"},{"type":"element","tagName":"span","properties":{"className":["token","operator"]},"children":[{"type":"text","value":">"}]},{"type":"text","value":"\n"},{"type":"element","tagName":"span","properties":{"className":["token","operator"]},"children":[{"type":"text","value":"<"}]},{"type":"element","tagName":"span","properties":{"className":["token","operator"]},"children":[{"type":"text","value":"/"}]},{"type":"text","value":"form"},{"type":"element","tagName":"span","properties":{"className":["token","operator"]},"children":[{"type":"text","value":">"}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":")"}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":";"}]},{"type":"text","value":"\n"},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"}"}]},{"type":"text","value":"\n"},{"type":"element","tagName":"span","properties":{"className":["token","keyword"]},"children":[{"type":"text","value":"export"}]},{"type":"text","value":" "},{"type":"element","tagName":"span","properties":{"className":["token","keyword"]},"children":[{"type":"text","value":"default"}]},{"type":"text","value":" Form"},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":";"}]}]}]}]},{"type":"element","tagName":"ol","properties":{},"children":[{"type":"element","tagName":"li","properties":{},"children":[{"type":"element","tagName":"strong","properties":{},"children":[{"type":"element","tagName":"strong","properties":{},"children":[{"type":"text","value":"Line 1: "}]}]},{"type":"text","value":"add an import for "},{"type":"element","tagName":"code","properties":{"className":["language-text"]},"children":[{"type":"text","value":"useState"}]},{"type":"text","value":" hook."}]},{"type":"element","tagName":"li","properties":{},"children":[{"type":"element","tagName":"strong","properties":{},"children":[{"type":"element","tagName":"strong","properties":{},"children":[{"type":"text","value":"Line 3–4: "}]}]},{"type":"text","value":"create two state variables "},{"type":"element","tagName":"code","properties":{"className":["language-text"]},"children":[{"type":"text","value":"number"}]},{"type":"text","value":" and "},{"type":"element","tagName":"code","properties":{"className":["language-text"]},"children":[{"type":"text","value":"type"}]},{"type":"text","value":" using "},{"type":"element","tagName":"code","properties":{"className":["language-text"]},"children":[{"type":"text","value":"useState"}]},{"type":"text","value":" . Here we pass "},{"type":"element","tagName":"strong","properties":{},"children":[{"type":"element","tagName":"strong","properties":{},"children":[{"type":"text","value":"random "}]}]},{"type":"text","value":"as the initial value for number, and "},{"type":"element","tagName":"strong","properties":{},"children":[{"type":"element","tagName":"strong","properties":{},"children":[{"type":"text","value":"trivia "}]}]},{"type":"text","value":"as initial value for type because they are the default values for the parameters in the API."}]},{"type":"element","tagName":"li","properties":{},"children":[{"type":"element","tagName":"strong","properties":{},"children":[{"type":"element","tagName":"strong","properties":{},"children":[{"type":"text","value":"Line 5–10: "}]}]},{"type":"text","value":"implement input change handler functions for both text and select inputs, where we change the value of the state variables using the functions returned by "},{"type":"element","tagName":"code","properties":{"className":["language-text"]},"children":[{"type":"text","value":"useState"}]},{"type":"text","value":" . If the value is unset, we automatically change the values to the default value."}]},{"type":"element","tagName":"li","properties":{},"children":[{"type":"element","tagName":"strong","properties":{},"children":[{"type":"element","tagName":"strong","properties":{},"children":[{"type":"text","value":"Line 13: "}]}]},{"type":"text","value":"pass the "},{"type":"element","tagName":"code","properties":{"className":["language-text"]},"children":[{"type":"text","value":"onNumberChanged"}]},{"type":"text","value":" function to "},{"type":"element","tagName":"code","properties":{"className":["language-text"]},"children":[{"type":"text","value":"onChange"}]},{"type":"text","value":" event for text input."}]},{"type":"element","tagName":"li","properties":{},"children":[{"type":"element","tagName":"strong","properties":{},"children":[{"type":"element","tagName":"strong","properties":{},"children":[{"type":"text","value":"Line 16: "}]}]},{"type":"text","value":"pass the "},{"type":"element","tagName":"code","properties":{"className":["language-text"]},"children":[{"type":"text","value":"onTypeChanged"}]},{"type":"text","value":" function to "},{"type":"element","tagName":"code","properties":{"className":["language-text"]},"children":[{"type":"text","value":"onChange"}]},{"type":"text","value":" event for select input."}]}]},{"type":"element","tagName":"p","properties":{},"children":[{"type":"text","value":"In addition, let’s go back to our "},{"type":"element","tagName":"code","properties":{"className":["language-text"]},"children":[{"type":"text","value":"App"}]},{"type":"text","value":" component to modify it and use states. Instead of modifying our welcome message by changing the "},{"type":"element","tagName":"code","properties":{"className":["language-text"]},"children":[{"type":"text","value":"innerHTML"}]},{"type":"text","value":" of the element, we will use a state. Our App component should now be like this:"}]},{"type":"element","tagName":"div","properties":{"className":["kg-card","kg-code-card","gatsby-highlight"],"dataLanguage":"text"},"children":[{"type":"element","tagName":"pre","properties":{"className":["language-text"]},"children":[{"type":"element","tagName":"code","properties":{"className":["language-text"]},"children":[{"type":"text","value":"import React, {useEffect, useState} from 'react'; \nimport Form from './Form'; \nimport '../styles/App.css'; \n \nfunction App() {\n  const [ welcomeMessage, setWelcomeMessage ] = useState(\n    \"Welcome to Numbers Trivia!\",\n  );\nuseEffect(() => {\n    setTimeout(() => {\n      setWelcomeMessage(\"Try Out Our Trivia Generator!\");\n    }, 3000);\n  }, []);\nreturn (\n    <div className=\"App\">\n      <header className=\"App-header\">\n        <h1>{welcomeMessage}</h1>\n      </header>\n      <Form/>\n    </div>\n  );\n}\nexport default App;"}]}]}]},{"type":"element","tagName":"p","properties":{},"children":[{"type":"text","value":"Now, we are using "},{"type":"element","tagName":"code","properties":{"className":["language-text"]},"children":[{"type":"text","value":"useState"}]},{"type":"text","value":" to declare and initialize our welcome message. It will return "},{"type":"element","tagName":"code","properties":{"className":["language-text"]},"children":[{"type":"text","value":"welcomeMessage"}]},{"type":"text","value":" , our state variable, and "},{"type":"element","tagName":"code","properties":{"className":["language-text"]},"children":[{"type":"text","value":"setWelcomeMessage"}]},{"type":"text","value":" , which we will use to change the value of "},{"type":"element","tagName":"code","properties":{"className":["language-text"]},"children":[{"type":"text","value":"welcomeMessage"}]},{"type":"text","value":" after 3 seconds from “"},{"type":"element","tagName":"strong","properties":{},"children":[{"type":"element","tagName":"strong","properties":{},"children":[{"type":"text","value":"Welcome to Numbers Trivia!"}]}]},{"type":"text","value":"” to “"},{"type":"element","tagName":"strong","properties":{},"children":[{"type":"element","tagName":"strong","properties":{},"children":[{"type":"text","value":"Try Out Our Trivia Generator!"}]}]},{"type":"text","value":"”"}]},{"type":"element","tagName":"p","properties":{},"children":[{"type":"text","value":"What’s left now is to add a function to handle the form’s "},{"type":"element","tagName":"code","properties":{"className":["language-text"]},"children":[{"type":"text","value":"onSubmit"}]},{"type":"text","value":" event. On submit, we will send a request to the API with our parameters, then display the result."}]},{"type":"element","tagName":"p","properties":{},"children":[{"type":"text","value":"In order to perform the request, we need to install "},{"type":"element","tagName":"a","properties":{"href":"https://github.com/axios/axios","rel":["noopener","nofollow"]},"children":[{"type":"text","value":"axios"}]},{"type":"text","value":":"}]},{"type":"element","tagName":"div","properties":{"className":["kg-card","kg-code-card","gatsby-highlight"],"dataLanguage":"shell"},"children":[{"type":"element","tagName":"pre","properties":{"className":["language-shell"]},"children":[{"type":"element","tagName":"code","properties":{"className":["language-shell"]},"children":[{"type":"element","tagName":"span","properties":{"className":["token","function"]},"children":[{"type":"text","value":"npm"}]},{"type":"text","value":" i axios"}]}]}]},{"type":"element","tagName":"p","properties":{},"children":[{"type":"text","value":"Then, require axios at the beginning of "},{"type":"element","tagName":"strong","properties":{},"children":[{"type":"element","tagName":"strong","properties":{},"children":[{"type":"text","value":"Form.js:"}]}]}]},{"type":"element","tagName":"div","properties":{"className":["kg-card","kg-code-card","gatsby-highlight"],"dataLanguage":"js"},"children":[{"type":"element","tagName":"pre","properties":{"className":["language-js"]},"children":[{"type":"element","tagName":"code","properties":{"className":["language-js"]},"children":[{"type":"element","tagName":"span","properties":{"className":["token","keyword"]},"children":[{"type":"text","value":"const"}]},{"type":"text","value":" axios "},{"type":"element","tagName":"span","properties":{"className":["token","operator"]},"children":[{"type":"text","value":"="}]},{"type":"text","value":" "},{"type":"element","tagName":"span","properties":{"className":["token","function"]},"children":[{"type":"text","value":"require"}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"("}]},{"type":"element","tagName":"span","properties":{"className":["token","string"]},"children":[{"type":"text","value":"'axios'"}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":")"}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":";"}]}]}]}]},{"type":"element","tagName":"p","properties":{},"children":[{"type":"text","value":"Now, add the following function below "},{"type":"element","tagName":"code","properties":{"className":["language-text"]},"children":[{"type":"text","value":"onTypeChanged"}]},{"type":"text","value":" in our "},{"type":"element","tagName":"strong","properties":{},"children":[{"type":"element","tagName":"strong","properties":{},"children":[{"type":"text","value":"Form"}]}]},{"type":"text","value":" component:"}]},{"type":"element","tagName":"div","properties":{"className":["kg-card","kg-code-card","gatsby-highlight"],"dataLanguage":"js"},"children":[{"type":"element","tagName":"pre","properties":{"className":["language-js"]},"children":[{"type":"element","tagName":"code","properties":{"className":["language-js"]},"children":[{"type":"element","tagName":"span","properties":{"className":["token","keyword"]},"children":[{"type":"text","value":"function"}]},{"type":"text","value":" "},{"type":"element","tagName":"span","properties":{"className":["token","function"]},"children":[{"type":"text","value":"onSubmit"}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"("}]},{"type":"element","tagName":"span","properties":{"className":["token","parameter"]},"children":[{"type":"text","value":"e"}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":")"}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"{"}]},{"type":"text","value":"\n  e"},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"."}]},{"type":"element","tagName":"span","properties":{"className":["token","function"]},"children":[{"type":"text","value":"preventDefault"}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"("}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":")"}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":";"}]},{"type":"text","value":"\n  axios"},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"."}]},{"type":"element","tagName":"span","properties":{"className":["token","function"]},"children":[{"type":"text","value":"get"}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"("}]},{"type":"element","tagName":"span","properties":{"className":["token","string"]},"children":[{"type":"text","value":"'http://numbersapi.com/'"}]},{"type":"text","value":" "},{"type":"element","tagName":"span","properties":{"className":["token","operator"]},"children":[{"type":"text","value":"+"}]},{"type":"text","value":" number "},{"type":"element","tagName":"span","properties":{"className":["token","operator"]},"children":[{"type":"text","value":"+"}]},{"type":"text","value":" "},{"type":"element","tagName":"span","properties":{"className":["token","string"]},"children":[{"type":"text","value":"'/'"}]},{"type":"text","value":" "},{"type":"element","tagName":"span","properties":{"className":["token","operator"]},"children":[{"type":"text","value":"+"}]},{"type":"text","value":" type"},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":")"}]},{"type":"text","value":"\n       "},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"."}]},{"type":"element","tagName":"span","properties":{"className":["token","function"]},"children":[{"type":"text","value":"then"}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"("}]},{"type":"element","tagName":"span","properties":{"className":["token","keyword"]},"children":[{"type":"text","value":"function"}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"("}]},{"type":"element","tagName":"span","properties":{"className":["token","parameter"]},"children":[{"type":"text","value":"response"}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":")"}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"{"}]},{"type":"text","value":"\n         "},{"type":"element","tagName":"span","properties":{"className":["token","keyword"]},"children":[{"type":"text","value":"let"}]},{"type":"text","value":" elm "},{"type":"element","tagName":"span","properties":{"className":["token","operator"]},"children":[{"type":"text","value":"="}]},{"type":"text","value":" document"},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"."}]},{"type":"element","tagName":"span","properties":{"className":["token","function"]},"children":[{"type":"text","value":"getElementById"}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"("}]},{"type":"element","tagName":"span","properties":{"className":["token","string"]},"children":[{"type":"text","value":"'result'"}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":")"}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":";"}]},{"type":"text","value":"\n         elm"},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"."}]},{"type":"text","value":"innerHTML "},{"type":"element","tagName":"span","properties":{"className":["token","operator"]},"children":[{"type":"text","value":"="}]},{"type":"text","value":" response"},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"."}]},{"type":"text","value":"data"},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":";"}]},{"type":"text","value":"\n       "},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"}"}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":")"}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"."}]},{"type":"element","tagName":"span","properties":{"className":["token","function"]},"children":[{"type":"text","value":"catch"}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"("}]},{"type":"element","tagName":"span","properties":{"className":["token","keyword"]},"children":[{"type":"text","value":"function"}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"("}]},{"type":"element","tagName":"span","properties":{"className":["token","parameter"]},"children":[{"type":"text","value":"e"}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":")"}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"{"}]},{"type":"text","value":"\n         console"},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"."}]},{"type":"element","tagName":"span","properties":{"className":["token","function"]},"children":[{"type":"text","value":"log"}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"("}]},{"type":"element","tagName":"span","properties":{"className":["token","string"]},"children":[{"type":"text","value":"\"error\""}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":","}]},{"type":"text","value":" e"},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":")"}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":";"}]},{"type":"text","value":" "},{"type":"element","tagName":"span","properties":{"className":["token","comment"]},"children":[{"type":"text","value":"//simple error handling"}]},{"type":"text","value":"\n       "},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"}"}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":")"}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":";"}]},{"type":"text","value":"\n"},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"}"}]}]}]}]},{"type":"element","tagName":"p","properties":{},"children":[{"type":"text","value":"We’re just performing a request to the API, passing the "},{"type":"element","tagName":"code","properties":{"className":["language-text"]},"children":[{"type":"text","value":"number"}]},{"type":"text","value":" and "},{"type":"element","tagName":"code","properties":{"className":["language-text"]},"children":[{"type":"text","value":"type"}]},{"type":"text","value":" then displaying the result in an element of id "},{"type":"element","tagName":"code","properties":{"className":["language-text"]},"children":[{"type":"text","value":"result"}]},{"type":"text","value":" (which we will add in a minute). In case of an error, we’re just displaying it in the console just as a simple error handling."}]},{"type":"element","tagName":"p","properties":{},"children":[{"type":"text","value":"Now, let’s add this function as the handler for the form "},{"type":"element","tagName":"code","properties":{"className":["language-text"]},"children":[{"type":"text","value":"onSubmit"}]},{"type":"text","value":" event in the return statement in Form.js:"}]},{"type":"element","tagName":"div","properties":{"className":["kg-card","kg-code-card","gatsby-highlight"],"dataLanguage":"jsx"},"children":[{"type":"element","tagName":"pre","properties":{"className":["language-jsx"]},"children":[{"type":"element","tagName":"code","properties":{"className":["language-jsx"]},"children":[{"type":"element","tagName":"span","properties":{"className":["token","tag"]},"children":[{"type":"element","tagName":"span","properties":{"className":["token","tag"]},"children":[{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"<"}]},{"type":"text","value":"form"}]},{"type":"text","value":" "},{"type":"element","tagName":"span","properties":{"className":["token","attr-name"]},"children":[{"type":"text","value":"onSubmit"}]},{"type":"element","tagName":"span","properties":{"className":["token","script","language-javascript"]},"children":[{"type":"element","tagName":"span","properties":{"className":["token","script-punctuation","punctuation"]},"children":[{"type":"text","value":"="}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"{"}]},{"type":"text","value":"onSubmit"},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"}"}]}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":">"}]}]}]}]}]},{"type":"element","tagName":"p","properties":{},"children":[{"type":"text","value":"The only thing left is to add the "},{"type":"element","tagName":"code","properties":{"className":["language-text"]},"children":[{"type":"text","value":"#result"}]},{"type":"text","value":" element. We can add it in "},{"type":"element","tagName":"strong","properties":{},"children":[{"type":"element","tagName":"strong","properties":{},"children":[{"type":"text","value":"App.js "}]}]},{"type":"text","value":"before "},{"type":"element","tagName":"code","properties":{"className":["language-text"]},"children":[{"type":"text","value":"<Form />"}]},{"type":"text","value":" :"}]},{"type":"element","tagName":"div","properties":{"className":["kg-card","kg-code-card","gatsby-highlight"],"dataLanguage":"jsx"},"children":[{"type":"element","tagName":"pre","properties":{"className":["language-jsx"]},"children":[{"type":"element","tagName":"code","properties":{"className":["language-jsx"]},"children":[{"type":"element","tagName":"span","properties":{"className":["token","tag"]},"children":[{"type":"element","tagName":"span","properties":{"className":["token","tag"]},"children":[{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"<"}]},{"type":"text","value":"div"}]},{"type":"text","value":" "},{"type":"element","tagName":"span","properties":{"className":["token","attr-name"]},"children":[{"type":"text","value":"id"}]},{"type":"element","tagName":"span","properties":{"className":["token","attr-value"]},"children":[{"type":"element","tagName":"span","properties":{"className":["token","punctuation","attr-equals"]},"children":[{"type":"text","value":"="}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"\""}]},{"type":"text","value":"result"},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"\""}]}]},{"type":"text","value":" "},{"type":"element","tagName":"span","properties":{"className":["token","attr-name"]},"children":[{"type":"text","value":"style"}]},{"type":"element","tagName":"span","properties":{"className":["token","script","language-javascript"]},"children":[{"type":"element","tagName":"span","properties":{"className":["token","script-punctuation","punctuation"]},"children":[{"type":"text","value":"="}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"{"}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"{"}]},{"type":"element","tagName":"span","properties":{"className":["token","literal-property","property"]},"children":[{"type":"text","value":"marginBottom"}]},{"type":"element","tagName":"span","properties":{"className":["token","operator"]},"children":[{"type":"text","value":":"}]},{"type":"text","value":" "},{"type":"element","tagName":"span","properties":{"className":["token","string"]},"children":[{"type":"text","value":"'15px'"}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"}"}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"}"}]}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":">"}]}]},{"type":"element","tagName":"span","properties":{"className":["token","tag"]},"children":[{"type":"element","tagName":"span","properties":{"className":["token","tag"]},"children":[{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"</"}]},{"type":"text","value":"div"}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":">"}]}]}]}]}]},{"type":"element","tagName":"p","properties":{},"children":[{"type":"text","value":"Alright, now go to your website and discover all new trivia about numbers!"}]},{"type":"element","tagName":"hr","properties":{},"children":[]},{"type":"element","tagName":"h2","properties":{"id":"conclusion"},"children":[{"type":"text","value":"Conclusion"}]},{"type":"element","tagName":"p","properties":{},"children":[{"type":"text","value":"You just created a React app and used some React Hooks! Of course, there’s still more to explore and learn, so be sure to head to the "},{"type":"element","tagName":"a","properties":{"href":"https://reactjs.org/docs/hooks-intro.html","rel":["noopener","nofollow"]},"children":[{"type":"text","value":"docs"}]},{"type":"text","value":" to learn more about React Hooks."}]}],"data":{"quirksMode":false}},"tableOfContents":[{"id":"conclusion","heading":"Conclusion"}]},"featureImageSharp":{"base":"1_-Ijet6kVJqGgul6adezDLQ.png","publicURL":"/static/811e7e5724a90f775f8832f17c9353ff/1_-Ijet6kVJqGgul6adezDLQ.png","imageMeta":{"width":700,"height":350},"childImageSharp":{"fluid":{"base64":"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAKCAIAAAA7N+mxAAAACXBIWXMAAAsTAAALEwEAmpwYAAABLElEQVQoz5WS3UrDQBCF8yDd7M5MdwesMf2NpSaUPkAvJCUNUqxKoNKgEStUsMU7vRJ8ZKFp2kYv1MMy7M03ezhnLTwU0fb8Tdb+CgBSbue3jb/DWpuTetWwcVwA2FCU7yyWEGxUhonAFlxvdW/uzubL5vkFgEIAIQQiMjMiGmOEEMystcaCt3LDpE07mvaXb4PVR/CwNt3A67Rns9nldBqORkmSxHGcZdlkMvF937bt3FQBG26Pr/vP74PXzyBbm17/1PPSNE2SJAzDeZrGcfy4WERRNBwOK5XKAUykbMEtr3f75N+/dMZXoBQiSimZ2XEcw1yr1aSUjUaDmaFke/M6ac1uUx8dG8fFIjAAUErlk4iklD8C21dlg1K7qqjQLnkq12aVK63+65N8AWEWZr2hm3MKAAAAAElFTkSuQmCC","aspectRatio":1.9886363636363635,"src":"/static/811e7e5724a90f775f8832f17c9353ff/60290/1_-Ijet6kVJqGgul6adezDLQ.png","srcSet":"/static/811e7e5724a90f775f8832f17c9353ff/847ef/1_-Ijet6kVJqGgul6adezDLQ.png 175w,\n/static/811e7e5724a90f775f8832f17c9353ff/91cba/1_-Ijet6kVJqGgul6adezDLQ.png 350w,\n/static/811e7e5724a90f775f8832f17c9353ff/60290/1_-Ijet6kVJqGgul6adezDLQ.png 700w","srcWebp":"/static/811e7e5724a90f775f8832f17c9353ff/89afa/1_-Ijet6kVJqGgul6adezDLQ.webp","srcSetWebp":"/static/811e7e5724a90f775f8832f17c9353ff/9fca7/1_-Ijet6kVJqGgul6adezDLQ.webp 175w,\n/static/811e7e5724a90f775f8832f17c9353ff/37a4e/1_-Ijet6kVJqGgul6adezDLQ.webp 350w,\n/static/811e7e5724a90f775f8832f17c9353ff/89afa/1_-Ijet6kVJqGgul6adezDLQ.webp 700w","sizes":"(max-width: 700px) 100vw, 700px"}}}},"next":{"id":"Ghost__Post__6127ba1b3ed159214d382e6b","title":"Website Ideas To Practice Your Web Development Skills","slug":"website-ideas-to-practice-your-web-development-skills","featured":false,"feature_image":"https://res-5.cloudinary.com/hbqmf3mbz/image/upload/q_auto/v1/ghost-blog-images/photo-1517694712202-14dd9538aa97.jpg","excerpt":"It can be hard to practice your skills as a programmer, especially when you’re a beginner.","custom_excerpt":"It can be hard to practice your skills as a programmer, especially when you’re a beginner.","visibility":"public","created_at_pretty":"26 Oct 2020","published_at_pretty":"10 Jun 2018","updated_at_pretty":"26 Aug 2021","created_at":"2020-10-26T21:23:28.000+00:00","published_at":"2018-06-10T21:22:00.000+00:00","updated_at":"2021-08-26T17:54:54.000+00:00","meta_title":null,"meta_description":null,"og_description":null,"og_image":null,"og_title":null,"twitter_description":null,"twitter_image":null,"twitter_title":null,"authors":[{"slug":"shahed","url":"https://backend.shahednasser.com/author/shahed/","name":"Shahed Nasser","bio":null,"cover_image":null,"profile_image":"https://backend.shahednasser.com/content/images/2022/03/IMG_0591.jpg","location":null,"website":null,"twitter":null,"facebook":null,"meta_title":null,"meta_description":null,"coverImageSharp":null,"profileImageSharp":null}],"primary_author":{"slug":"shahed","url":"https://backend.shahednasser.com/author/shahed/","name":"Shahed Nasser","bio":null,"cover_image":null,"profile_image":"https://backend.shahednasser.com/content/images/2022/03/IMG_0591.jpg","location":null,"website":null,"twitter":null,"facebook":null,"meta_title":null,"meta_description":null,"coverImageSharp":null,"profileImageSharp":{"base":"IMG_0591.jpg","publicURL":"/static/ceb49c3c631485453e71e00d7f84b069/IMG_0591.jpg","imageMeta":{"width":1182,"height":1179},"childImageSharp":{"fluid":{"base64":"data:image/jpeg;base64,/9j/2wBDABALDA4MChAODQ4SERATGCgaGBYWGDEjJR0oOjM9PDkzODdASFxOQERXRTc4UG1RV19iZ2hnPk1xeXBkeFxlZ2P/2wBDARESEhgVGC8aGi9jQjhCY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2P/wgARCAAUABQDASIAAhEBAxEB/8QAGAABAQEBAQAAAAAAAAAAAAAAAAMEAQL/xAAWAQEBAQAAAAAAAAAAAAAAAAADBAL/2gAMAwEAAhADEAAAAdXiFM6i0CohUWXoKn//xAAcEAACAgIDAAAAAAAAAAAAAAACAwESBBEhM0H/2gAIAQEAAQUCWySE3WEr7SzbXjAj4iKty+sOQ//EABYRAQEBAAAAAAAAAAAAAAAAAAERIP/aAAgBAwEBPwEhj//EABQRAQAAAAAAAAAAAAAAAAAAACD/2gAIAQIBAT8BH//EAB4QAAIBBAMBAAAAAAAAAAAAAAABIRESMUECECJx/9oACAEBAAY/ApVGWvOjzgtUwLlTZA0sdL4f/8QAHBAAAwACAwEAAAAAAAAAAAAAAAERITFBkbHB/9oACAEBAAE/IahkCy+N2GwZpjQiJHJCspUFY0QrSi+HqiW2rgf/2gAMAwEAAgADAAAAEPw3/wD/xAAYEQEBAAMAAAAAAAAAAAAAAAAAARExQf/aAAgBAwEBPxCtjDqP/8QAFxEBAQEBAAAAAAAAAAAAAAAAAQAxEf/aAAgBAgEBPxBFus6Tt//EAB8QAQEAAgIBBQAAAAAAAAAAAAERACExQWFRcYGR0f/aAAgBAQABPxAuaBPPzkO1wyX7F4wkwXanfZrFQgeqE9JgS14vVOvrERIJomVBKwt2jebAeP0yVa8h1n//2Q==","aspectRatio":1,"src":"/static/ceb49c3c631485453e71e00d7f84b069/31709/IMG_0591.jpg","srcSet":"/static/ceb49c3c631485453e71e00d7f84b069/f340b/IMG_0591.jpg 28w,\n/static/ceb49c3c631485453e71e00d7f84b069/22d64/IMG_0591.jpg 55w,\n/static/ceb49c3c631485453e71e00d7f84b069/31709/IMG_0591.jpg 110w,\n/static/ceb49c3c631485453e71e00d7f84b069/aa249/IMG_0591.jpg 165w,\n/static/ceb49c3c631485453e71e00d7f84b069/0dc33/IMG_0591.jpg 220w,\n/static/ceb49c3c631485453e71e00d7f84b069/d8257/IMG_0591.jpg 1182w","srcWebp":"/static/ceb49c3c631485453e71e00d7f84b069/8678c/IMG_0591.webp","srcSetWebp":"/static/ceb49c3c631485453e71e00d7f84b069/59cda/IMG_0591.webp 28w,\n/static/ceb49c3c631485453e71e00d7f84b069/7da75/IMG_0591.webp 55w,\n/static/ceb49c3c631485453e71e00d7f84b069/8678c/IMG_0591.webp 110w,\n/static/ceb49c3c631485453e71e00d7f84b069/f282e/IMG_0591.webp 165w,\n/static/ceb49c3c631485453e71e00d7f84b069/a7b21/IMG_0591.webp 220w,\n/static/ceb49c3c631485453e71e00d7f84b069/63099/IMG_0591.webp 1182w","sizes":"(max-width: 110px) 100vw, 110px"}}}},"primary_tag":{"slug":"beginner","url":"https://backend.shahednasser.com/tag/beginner/","name":"Beginners","visibility":"public","feature_image":"https://backend.shahednasser.com/content/images/2022/01/photo-1521185496955-15097b20c5fe-2.jpeg","description":"Tutorials, articles, and tips to help beginners accelerate their journey in programming.","meta_title":"Beginners","meta_description":"Tutorials, articles, and tips to help beginners accelerate their journey in programming.","featureImageSharp":{"base":"photo-1521185496955-15097b20c5fe-2.jpeg","publicURL":"/static/13f953658eaecb779984996ea356b1c4/photo-1521185496955-15097b20c5fe-2.jpeg","imageMeta":{"width":2000,"height":1547},"childImageSharp":{"fluid":{"base64":"data:image/jpeg;base64,/9j/2wBDABALDA4MChAODQ4SERATGCgaGBYWGDEjJR0oOjM9PDkzODdASFxOQERXRTc4UG1RV19iZ2hnPk1xeXBkeFxlZ2P/2wBDARESEhgVGC8aGi9jQjhCY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2P/wgARCAAPABQDASIAAhEBAxEB/8QAFwABAQEBAAAAAAAAAAAAAAAAAAQCBf/EABYBAQEBAAAAAAAAAAAAAAAAAAIAAf/aAAwDAQACEAMQAAABhU05c9sV/8QAGRAAAwEBAQAAAAAAAAAAAAAAAQIDABIR/9oACAEBAAEFAuTgGxGjVeRdPLOHf//EABQRAQAAAAAAAAAAAAAAAAAAABD/2gAIAQMBAT8BP//EABQRAQAAAAAAAAAAAAAAAAAAABD/2gAIAQIBAT8BP//EABkQAAMBAQEAAAAAAAAAAAAAAAABEXECEP/aAAgBAQAGPwL2dJaOqYVH/8QAGhAAAwADAQAAAAAAAAAAAAAAAAERITFBYf/aAAgBAQABPyFpWJH6IutKCacL+lRRGxiDm+H/2gAMAwEAAgADAAAAEK//AP/EABYRAQEBAAAAAAAAAAAAAAAAAAEREP/aAAgBAwEBPxAI3P/EABYRAQEBAAAAAAAAAAAAAAAAAAERAP/aAAgBAgEBPxBaR03/xAAbEAEBAAIDAQAAAAAAAAAAAAABEQAhMUGxcf/aAAgBAQABPxAInmDF9wQ6urfMI5JgA/si0141k9pEa4fcjFJmybz/2Q==","aspectRatio":1.2935323383084578,"src":"/static/13f953658eaecb779984996ea356b1c4/d5c54/photo-1521185496955-15097b20c5fe-2.jpg","srcSet":"/static/13f953658eaecb779984996ea356b1c4/65d8c/photo-1521185496955-15097b20c5fe-2.jpg 260w,\n/static/13f953658eaecb779984996ea356b1c4/c5f21/photo-1521185496955-15097b20c5fe-2.jpg 520w,\n/static/13f953658eaecb779984996ea356b1c4/d5c54/photo-1521185496955-15097b20c5fe-2.jpg 1040w,\n/static/13f953658eaecb779984996ea356b1c4/81a53/photo-1521185496955-15097b20c5fe-2.jpg 1560w,\n/static/13f953658eaecb779984996ea356b1c4/4e5f3/photo-1521185496955-15097b20c5fe-2.jpg 2000w","srcWebp":"/static/13f953658eaecb779984996ea356b1c4/e4875/photo-1521185496955-15097b20c5fe-2.webp","srcSetWebp":"/static/13f953658eaecb779984996ea356b1c4/dc8f3/photo-1521185496955-15097b20c5fe-2.webp 260w,\n/static/13f953658eaecb779984996ea356b1c4/2db4b/photo-1521185496955-15097b20c5fe-2.webp 520w,\n/static/13f953658eaecb779984996ea356b1c4/e4875/photo-1521185496955-15097b20c5fe-2.webp 1040w,\n/static/13f953658eaecb779984996ea356b1c4/f5845/photo-1521185496955-15097b20c5fe-2.webp 1560w,\n/static/13f953658eaecb779984996ea356b1c4/49d6b/photo-1521185496955-15097b20c5fe-2.webp 2000w","sizes":"(max-width: 1040px) 100vw, 1040px"}}}},"tags":[{"slug":"beginner","url":"https://backend.shahednasser.com/tag/beginner/","name":"Beginners","visibility":"public","feature_image":"https://backend.shahednasser.com/content/images/2022/01/photo-1521185496955-15097b20c5fe-2.jpeg","description":"Tutorials, articles, and tips to help beginners accelerate their journey in programming.","meta_title":"Beginners","meta_description":"Tutorials, articles, and tips to help beginners accelerate their journey in programming.","featureImageSharp":null},{"slug":"tips","url":"https://backend.shahednasser.com/tag/tips/","name":"Tips","visibility":"public","feature_image":null,"description":"Learn more about programming and development through these articles that have essential tips!","meta_title":"Tips on Technology and Programming","meta_description":null,"featureImageSharp":null}],"plaintext":"It can be hard to practice your skills as a programmer, especially when you’re a\nbeginner. There are many websites that offer you exercises, easy or hard, to\npractice your skills and improve yourself.\n\nHowever, the best way to improve your knowledge in programming languages is\nthrough practices that resemble real-life projects. Through these kinds of\nprojects, you can basically get your hands dirty and benefit greatly for future\nreal projects and any problems you might face then.\n\n\n--------------------------------------------------------------------------------\n\nI present to you today a few ideas for projects that can help you test your\nknowledge and expand it. These projects have the elements that can be a part of\nmost websites.\n\nBelow you will find the ideas explained briefly with a few pointers on what\nfeatures you can include. You can include as many features as you want or change\nthe ideas to whatever you desire. I’m just giving you a place to start.\n\nNOTE: These website ideas are for beginners whose skills are basic in\nHTML/CSS/Javascript/PHP. They are ordered from easiest to the more challenging\nones. If you are looking to advance your skills, I advise you to follow them in\norder.\n\n\n--------------------------------------------------------------------------------\n\nMovies\nThis is probably one of the most classical project ideas. Simply put, the\nwebsite offers the user a list of movies they can watch.\n\nThe features of the website are basic enough. You can browse all movies\navailable, search through movies, or browse them by category. If you are logged\nin, you can add a comment or rate the movie. Also, you can offer news about the\nmovies or the actors. Any feature you can think of is possible.\n\nWhat you should focus on: The main focus is on the admin’s dashboard. The admin\nis the person that adds, deletes, or edits movies, news, users or comments. If\nthis is your first time developing a project with an admin’s dashboard, then\nthat should be your main focus.\n\n\n--------------------------------------------------------------------------------\n\nOnline bookstore\nThis is somewhat similar to the previous project. Create a website that shows\nthe user a list of available books they can buy.\n\nIf you don’t want to implement the payment part, you don’t have to. You can\nsimulate the buying experience and represent it with a simple email sent to the\nuser indicating what items they have bought.\n\nWhat you should focus on: Many of the features of this website are similar to\nthe ones in the previous project. So, try to implement them differently while\nproviding a seamless user experience. If you haven’t used AJAX before or you’re\nnot very good at it, I advise you to learn how to use it in this website. Also,\ntry to focus on security. Learn more about form validations, securing data\nbefore inserting them into the database, and other ways to make sure your\nwebsite is secure.\n\n\n--------------------------------------------------------------------------------\n\nTests Generator\nThis website can offer many features for users. The main feature is that it\ngives a user the ability to create a multiple choice test and view people’s\nanswers or scores.\n\nYou can add several features to this website. For example, a user that creates a\ntest can make it public or private (accessed by a list of people specified by\nthe user). Another example is that a user can view charts of the questions most\nanswered correctly or least answered correctly. There are so many others as\nwell.\n\nWhat you should focus on: This project has more logic than the previous two.\nFocus in this one on how to implement the main and additional features in the\nmost efficient ways. Also, make sure to implement authorization (which user can\nsee what).\n\n\n--------------------------------------------------------------------------------\n\nTop Ten Lists\nThis is a website that I personally worked on to improve my knowledge of React\n[https://reactjs.org/], but you can create it with the basic and fundamental\nprogramming languages.\n\nSimply, this website lets users create top ten lists about any topic they want.\nAdditional features are users interacting with each other’s lists, voting on\neach other’s lists, messaging each other, among a lot of other features.\n\nWhat you should focus on: A feature you can learn how to implement is giving\nusers the ability to register or login with their social media accounts. You can\ntry Google, Facebook, or Twitter. Another thing you can focus on is learning \nBootstrap [https://getbootstrap.com/] or jQuery [https://jquery.com/] or both.\nBoth of them are very important and can make your projects (and life) easier.\n\n\n--------------------------------------------------------------------------------\n\nBudget Tracker\nA very useful website. The main point of this website is to help users manage\ntheir expenses and incomes.\n\nSome features that can be added to the website are calculating profits,\ninforming the user what are their biggest expenses and how they can save up. You\ncan also generate reports for the user.\n\nWhat you should focus on: on this website, focus on making things simple. This\nwebsite is about helping the user, and the best way you can help them is making\nthe website easy to use. Whether it’s the design or how a user can do a simple\nor big operation, make it as simple as possible.\n\n\n--------------------------------------------------------------------------------\n\nSchool Management\nA very required project by schools and institutes. It’s mostly required as a\nsoftware, but some need it as a website (run locally, mostly) as well.\n\nThis website gives users the ability to store, edit and view students and\nteachers’ information. The school can also manage their finances and their\ncurriculum plans, among other features.\n\nAn institute once asked me to add charts that analyze incomes, expenses, number\nof registered users compared with other users, etc… You can implement that as\nwell.\n\nWhat you should focus on: this website’s core focus is on data. A school will\nfocus mainly on how to view the data and deal with it quickly and efficiently.\nAnother important part is creating backup. Find a way to give the school the\nability to create a backup of their data.\n\nTip: an easy way to generate charts is through Google’s chart web service\n[https://developers.google.com/chart/].\n\n\n--------------------------------------------------------------------------------\n\nConclusion\nThis was a list of a few website ideas you can use to increase your knowledge\nand skills. There are so many more ideas you can think of to create, but this is\nmerely to give you a head start.\n\nYour opinion: what other ideas can beginners use to create websites and improve\ntheir skills?","html":"<p>It can be hard to practice your skills as a programmer, especially when you’re a beginner. There are many websites that offer you exercises, easy or hard, to practice your skills and improve yourself.</p><p>However, the best way to improve your knowledge in programming languages is through practices that resemble real-life projects. Through these kinds of projects, you can basically get your hands dirty and benefit greatly for future real projects and any problems you might face then.</p><hr><p>I present to you today a few ideas for projects that can help you test your knowledge and expand it. These projects have the elements that can be a part of most websites.</p><p>Below you will find the ideas explained briefly with a few pointers on what features you can include. You can include as many features as you want or change the ideas to whatever you desire. I’m just giving you a place to start.</p><p><strong><strong>NOTE: These website ideas are for beginners whose skills are basic in HTML/CSS/Javascript/PHP. They are ordered from easiest to the more challenging ones. If you are looking to advance your skills, I advise you to follow them in order.</strong></strong></p><hr><h1 id=\"movies\">Movies</h1><p>This is probably one of the most classical project ideas. Simply put, the website offers the user a list of movies they can watch.</p><p>The features of the website are basic enough. You can browse all movies available, search through movies, or browse them by category. If you are logged in, you can add a comment or rate the movie. Also, you can offer news about the movies or the actors. Any feature you can think of is possible.</p><p><strong><strong>What you should focus on: </strong></strong>The main focus is on the admin’s dashboard. The admin is the person that adds, deletes, or edits movies, news, users or comments. If this is your first time developing a project with an admin’s dashboard, then that should be your main focus.</p><hr><h1 id=\"online-bookstore\">Online bookstore</h1><p>This is somewhat similar to the previous project. Create a website that shows the user a list of available books they can buy.</p><p>If you don’t want to implement the payment part, you don’t have to. You can simulate the buying experience and represent it with a simple email sent to the user indicating what items they have bought.</p><p><strong><strong>What you should focus on:</strong></strong> Many of the features of this website are similar to the ones in the previous project. So, try to implement them differently while providing a seamless user experience. If you haven’t used AJAX before or you’re not very good at it, I advise you to learn how to use it in this website. Also, try to focus on security. Learn more about form validations, securing data before inserting them into the database, and other ways to make sure your website is secure.</p><hr><h1 id=\"tests-generator\">Tests Generator</h1><p>This website can offer many features for users. The main feature is that it gives a user the ability to create a multiple choice test and view people’s answers or scores.</p><p>You can add several features to this website. For example, a user that creates a test can make it public or private (accessed by a list of people specified by the user). Another example is that a user can view charts of the questions most answered correctly or least answered correctly. There are so many others as well.</p><p><strong><strong>What you should focus on:</strong></strong> This project has more logic than the previous two. Focus in this one on how to implement the main and additional features in the most efficient ways. Also, make sure to implement authorization (which user can see what).</p><hr><h1 id=\"top-ten-lists\">Top Ten Lists</h1><p>This is a website that I personally worked on to improve my knowledge of <a href=\"https://reactjs.org/\" rel=\"noopener nofollow\">React</a>, but you can create it with the basic and fundamental programming languages.</p><p>Simply, this website lets users create top ten lists about any topic they want. Additional features are users interacting with each other’s lists, voting on each other’s lists, messaging each other, among a lot of other features.</p><p><strong><strong>What you should focus on:</strong></strong> A feature you can learn how to implement is giving users the ability to register or login with their social media accounts. You can try Google, Facebook, or Twitter. Another thing you can focus on is learning <a href=\"https://getbootstrap.com/\" rel=\"noopener nofollow\">Bootstrap</a> or <a href=\"https://jquery.com/\" rel=\"noopener nofollow\">jQuery</a> or both. Both of them are very important and can make your projects (and life) easier.</p><hr><h1 id=\"budget-tracker\">Budget Tracker</h1><p>A very useful website. The main point of this website is to help users manage their expenses and incomes.</p><p>Some features that can be added to the website are calculating profits, informing the user what are their biggest expenses and how they can save up. You can also generate reports for the user.</p><p><strong><strong>What you should focus on: </strong></strong>on this website, focus on making things simple. This website is about helping the user, and the best way you can help them is making the website easy to use. Whether it’s the design or how a user can do a simple or big operation, make it as simple as possible.</p><hr><h1 id=\"school-management\">School Management</h1><p>A very required project by schools and institutes. It’s mostly required as a software, but some need it as a website (run locally, mostly) as well.</p><p>This website gives users the ability to store, edit and view students and teachers’ information. The school can also manage their finances and their curriculum plans, among other features.</p><p>An institute once asked me to add charts that analyze incomes, expenses, number of registered users compared with other users, etc… You can implement that as well.</p><p><strong><strong>What you should focus on: </strong></strong>this website’s core focus is on data. A school will focus mainly on how to view the data and deal with it quickly and efficiently. Another important part is creating backup. Find a way to give the school the ability to create a backup of their data.</p><p><strong><strong>Tip: </strong></strong>an easy way to generate charts is through <a href=\"https://developers.google.com/chart/\" rel=\"noopener nofollow\">Google’s chart web service</a>.</p><hr><h1 id=\"conclusion\">Conclusion</h1><p>This was a list of a few website ideas you can use to increase your knowledge and skills. There are so many more ideas you can think of to create, but this is merely to give you a head start.</p><p><strong><strong>Your opinion: </strong></strong>what other ideas can beginners use to create websites and improve their skills?</p>","url":"https://backend.shahednasser.com/website-ideas-to-practice-your-web-development-skills/","canonical_url":null,"uuid":"9d2a78ac-7148-47c2-a0de-29dcd0cb7da8","codeinjection_foot":null,"codeinjection_head":null,"codeinjection_styles":null,"comment_id":"5f973e50b366d4001e86cc33","reading_time":4,"send_email_when_published":null,"email_subject":null,"childHtmlRehype":{"html":"<p>It can be hard to practice your skills as a programmer, especially when you’re a beginner. There are many websites that offer you exercises, easy or hard, to practice your skills and improve yourself.</p><p>However, the best way to improve your knowledge in programming languages is through practices that resemble real-life projects. Through these kinds of projects, you can basically get your hands dirty and benefit greatly for future real projects and any problems you might face then.</p><hr><p>I present to you today a few ideas for projects that can help you test your knowledge and expand it. These projects have the elements that can be a part of most websites.</p><p>Below you will find the ideas explained briefly with a few pointers on what features you can include. You can include as many features as you want or change the ideas to whatever you desire. I’m just giving you a place to start.</p><p><strong><strong>NOTE: These website ideas are for beginners whose skills are basic in HTML/CSS/Javascript/PHP. They are ordered from easiest to the more challenging ones. If you are looking to advance your skills, I advise you to follow them in order.</strong></strong></p><hr><h1 id=\"movies\">Movies</h1><p>This is probably one of the most classical project ideas. Simply put, the website offers the user a list of movies they can watch.</p><p>The features of the website are basic enough. You can browse all movies available, search through movies, or browse them by category. If you are logged in, you can add a comment or rate the movie. Also, you can offer news about the movies or the actors. Any feature you can think of is possible.</p><p><strong><strong>What you should focus on: </strong></strong>The main focus is on the admin’s dashboard. The admin is the person that adds, deletes, or edits movies, news, users or comments. If this is your first time developing a project with an admin’s dashboard, then that should be your main focus.</p><hr><h1 id=\"online-bookstore\">Online bookstore</h1><p>This is somewhat similar to the previous project. Create a website that shows the user a list of available books they can buy.</p><p>If you don’t want to implement the payment part, you don’t have to. You can simulate the buying experience and represent it with a simple email sent to the user indicating what items they have bought.</p><p><strong><strong>What you should focus on:</strong></strong> Many of the features of this website are similar to the ones in the previous project. So, try to implement them differently while providing a seamless user experience. If you haven’t used AJAX before or you’re not very good at it, I advise you to learn how to use it in this website. Also, try to focus on security. Learn more about form validations, securing data before inserting them into the database, and other ways to make sure your website is secure.</p><hr><h1 id=\"tests-generator\">Tests Generator</h1><p>This website can offer many features for users. The main feature is that it gives a user the ability to create a multiple choice test and view people’s answers or scores.</p><p>You can add several features to this website. For example, a user that creates a test can make it public or private (accessed by a list of people specified by the user). Another example is that a user can view charts of the questions most answered correctly or least answered correctly. There are so many others as well.</p><p><strong><strong>What you should focus on:</strong></strong> This project has more logic than the previous two. Focus in this one on how to implement the main and additional features in the most efficient ways. Also, make sure to implement authorization (which user can see what).</p><hr><h1 id=\"top-ten-lists\">Top Ten Lists</h1><p>This is a website that I personally worked on to improve my knowledge of <a href=\"https://reactjs.org/\" rel=\"noopener nofollow\">React</a>, but you can create it with the basic and fundamental programming languages.</p><p>Simply, this website lets users create top ten lists about any topic they want. Additional features are users interacting with each other’s lists, voting on each other’s lists, messaging each other, among a lot of other features.</p><p><strong><strong>What you should focus on:</strong></strong> A feature you can learn how to implement is giving users the ability to register or login with their social media accounts. You can try Google, Facebook, or Twitter. Another thing you can focus on is learning <a href=\"https://getbootstrap.com/\" rel=\"noopener nofollow\">Bootstrap</a> or <a href=\"https://jquery.com/\" rel=\"noopener nofollow\">jQuery</a> or both. Both of them are very important and can make your projects (and life) easier.</p><hr><h1 id=\"budget-tracker\">Budget Tracker</h1><p>A very useful website. The main point of this website is to help users manage their expenses and incomes.</p><p>Some features that can be added to the website are calculating profits, informing the user what are their biggest expenses and how they can save up. You can also generate reports for the user.</p><p><strong><strong>What you should focus on: </strong></strong>on this website, focus on making things simple. This website is about helping the user, and the best way you can help them is making the website easy to use. Whether it’s the design or how a user can do a simple or big operation, make it as simple as possible.</p><hr><h1 id=\"school-management\">School Management</h1><p>A very required project by schools and institutes. It’s mostly required as a software, but some need it as a website (run locally, mostly) as well.</p><p>This website gives users the ability to store, edit and view students and teachers’ information. The school can also manage their finances and their curriculum plans, among other features.</p><p>An institute once asked me to add charts that analyze incomes, expenses, number of registered users compared with other users, etc… You can implement that as well.</p><p><strong><strong>What you should focus on: </strong></strong>this website’s core focus is on data. A school will focus mainly on how to view the data and deal with it quickly and efficiently. Another important part is creating backup. Find a way to give the school the ability to create a backup of their data.</p><p><strong><strong>Tip: </strong></strong>an easy way to generate charts is through <a href=\"https://developers.google.com/chart/\" rel=\"noopener nofollow\">Google’s chart web service</a>.</p><hr><h1 id=\"conclusion\">Conclusion</h1><p>This was a list of a few website ideas you can use to increase your knowledge and skills. There are so many more ideas you can think of to create, but this is merely to give you a head start.</p><p><strong><strong>Your opinion: </strong></strong>what other ideas can beginners use to create websites and improve their skills?</p>","htmlAst":{"type":"root","children":[{"type":"element","tagName":"p","properties":{},"children":[{"type":"text","value":"It can be hard to practice your skills as a programmer, especially when you’re a beginner. There are many websites that offer you exercises, easy or hard, to practice your skills and improve yourself."}]},{"type":"element","tagName":"p","properties":{},"children":[{"type":"text","value":"However, the best way to improve your knowledge in programming languages is through practices that resemble real-life projects. Through these kinds of projects, you can basically get your hands dirty and benefit greatly for future real projects and any problems you might face then."}]},{"type":"element","tagName":"hr","properties":{},"children":[]},{"type":"element","tagName":"p","properties":{},"children":[{"type":"text","value":"I present to you today a few ideas for projects that can help you test your knowledge and expand it. These projects have the elements that can be a part of most websites."}]},{"type":"element","tagName":"p","properties":{},"children":[{"type":"text","value":"Below you will find the ideas explained briefly with a few pointers on what features you can include. You can include as many features as you want or change the ideas to whatever you desire. I’m just giving you a place to start."}]},{"type":"element","tagName":"p","properties":{},"children":[{"type":"element","tagName":"strong","properties":{},"children":[{"type":"element","tagName":"strong","properties":{},"children":[{"type":"text","value":"NOTE: These website ideas are for beginners whose skills are basic in HTML/CSS/Javascript/PHP. They are ordered from easiest to the more challenging ones. If you are looking to advance your skills, I advise you to follow them in order."}]}]}]},{"type":"element","tagName":"hr","properties":{},"children":[]},{"type":"element","tagName":"h1","properties":{"id":"movies"},"children":[{"type":"text","value":"Movies"}]},{"type":"element","tagName":"p","properties":{},"children":[{"type":"text","value":"This is probably one of the most classical project ideas. Simply put, the website offers the user a list of movies they can watch."}]},{"type":"element","tagName":"p","properties":{},"children":[{"type":"text","value":"The features of the website are basic enough. You can browse all movies available, search through movies, or browse them by category. If you are logged in, you can add a comment or rate the movie. Also, you can offer news about the movies or the actors. Any feature you can think of is possible."}]},{"type":"element","tagName":"p","properties":{},"children":[{"type":"element","tagName":"strong","properties":{},"children":[{"type":"element","tagName":"strong","properties":{},"children":[{"type":"text","value":"What you should focus on: "}]}]},{"type":"text","value":"The main focus is on the admin’s dashboard. The admin is the person that adds, deletes, or edits movies, news, users or comments. If this is your first time developing a project with an admin’s dashboard, then that should be your main focus."}]},{"type":"element","tagName":"hr","properties":{},"children":[]},{"type":"element","tagName":"h1","properties":{"id":"online-bookstore"},"children":[{"type":"text","value":"Online bookstore"}]},{"type":"element","tagName":"p","properties":{},"children":[{"type":"text","value":"This is somewhat similar to the previous project. Create a website that shows the user a list of available books they can buy."}]},{"type":"element","tagName":"p","properties":{},"children":[{"type":"text","value":"If you don’t want to implement the payment part, you don’t have to. You can simulate the buying experience and represent it with a simple email sent to the user indicating what items they have bought."}]},{"type":"element","tagName":"p","properties":{},"children":[{"type":"element","tagName":"strong","properties":{},"children":[{"type":"element","tagName":"strong","properties":{},"children":[{"type":"text","value":"What you should focus on:"}]}]},{"type":"text","value":" Many of the features of this website are similar to the ones in the previous project. So, try to implement them differently while providing a seamless user experience. If you haven’t used AJAX before or you’re not very good at it, I advise you to learn how to use it in this website. Also, try to focus on security. Learn more about form validations, securing data before inserting them into the database, and other ways to make sure your website is secure."}]},{"type":"element","tagName":"hr","properties":{},"children":[]},{"type":"element","tagName":"h1","properties":{"id":"tests-generator"},"children":[{"type":"text","value":"Tests Generator"}]},{"type":"element","tagName":"p","properties":{},"children":[{"type":"text","value":"This website can offer many features for users. The main feature is that it gives a user the ability to create a multiple choice test and view people’s answers or scores."}]},{"type":"element","tagName":"p","properties":{},"children":[{"type":"text","value":"You can add several features to this website. For example, a user that creates a test can make it public or private (accessed by a list of people specified by the user). Another example is that a user can view charts of the questions most answered correctly or least answered correctly. There are so many others as well."}]},{"type":"element","tagName":"p","properties":{},"children":[{"type":"element","tagName":"strong","properties":{},"children":[{"type":"element","tagName":"strong","properties":{},"children":[{"type":"text","value":"What you should focus on:"}]}]},{"type":"text","value":" This project has more logic than the previous two. Focus in this one on how to implement the main and additional features in the most efficient ways. Also, make sure to implement authorization (which user can see what)."}]},{"type":"element","tagName":"hr","properties":{},"children":[]},{"type":"element","tagName":"h1","properties":{"id":"top-ten-lists"},"children":[{"type":"text","value":"Top Ten Lists"}]},{"type":"element","tagName":"p","properties":{},"children":[{"type":"text","value":"This is a website that I personally worked on to improve my knowledge of "},{"type":"element","tagName":"a","properties":{"href":"https://reactjs.org/","rel":["noopener","nofollow"]},"children":[{"type":"text","value":"React"}]},{"type":"text","value":", but you can create it with the basic and fundamental programming languages."}]},{"type":"element","tagName":"p","properties":{},"children":[{"type":"text","value":"Simply, this website lets users create top ten lists about any topic they want. Additional features are users interacting with each other’s lists, voting on each other’s lists, messaging each other, among a lot of other features."}]},{"type":"element","tagName":"p","properties":{},"children":[{"type":"element","tagName":"strong","properties":{},"children":[{"type":"element","tagName":"strong","properties":{},"children":[{"type":"text","value":"What you should focus on:"}]}]},{"type":"text","value":" A feature you can learn how to implement is giving users the ability to register or login with their social media accounts. You can try Google, Facebook, or Twitter. Another thing you can focus on is learning "},{"type":"element","tagName":"a","properties":{"href":"https://getbootstrap.com/","rel":["noopener","nofollow"]},"children":[{"type":"text","value":"Bootstrap"}]},{"type":"text","value":" or "},{"type":"element","tagName":"a","properties":{"href":"https://jquery.com/","rel":["noopener","nofollow"]},"children":[{"type":"text","value":"jQuery"}]},{"type":"text","value":" or both. Both of them are very important and can make your projects (and life) easier."}]},{"type":"element","tagName":"hr","properties":{},"children":[]},{"type":"element","tagName":"h1","properties":{"id":"budget-tracker"},"children":[{"type":"text","value":"Budget Tracker"}]},{"type":"element","tagName":"p","properties":{},"children":[{"type":"text","value":"A very useful website. The main point of this website is to help users manage their expenses and incomes."}]},{"type":"element","tagName":"p","properties":{},"children":[{"type":"text","value":"Some features that can be added to the website are calculating profits, informing the user what are their biggest expenses and how they can save up. You can also generate reports for the user."}]},{"type":"element","tagName":"p","properties":{},"children":[{"type":"element","tagName":"strong","properties":{},"children":[{"type":"element","tagName":"strong","properties":{},"children":[{"type":"text","value":"What you should focus on: "}]}]},{"type":"text","value":"on this website, focus on making things simple. This website is about helping the user, and the best way you can help them is making the website easy to use. Whether it’s the design or how a user can do a simple or big operation, make it as simple as possible."}]},{"type":"element","tagName":"hr","properties":{},"children":[]},{"type":"element","tagName":"h1","properties":{"id":"school-management"},"children":[{"type":"text","value":"School Management"}]},{"type":"element","tagName":"p","properties":{},"children":[{"type":"text","value":"A very required project by schools and institutes. It’s mostly required as a software, but some need it as a website (run locally, mostly) as well."}]},{"type":"element","tagName":"p","properties":{},"children":[{"type":"text","value":"This website gives users the ability to store, edit and view students and teachers’ information. The school can also manage their finances and their curriculum plans, among other features."}]},{"type":"element","tagName":"p","properties":{},"children":[{"type":"text","value":"An institute once asked me to add charts that analyze incomes, expenses, number of registered users compared with other users, etc… You can implement that as well."}]},{"type":"element","tagName":"p","properties":{},"children":[{"type":"element","tagName":"strong","properties":{},"children":[{"type":"element","tagName":"strong","properties":{},"children":[{"type":"text","value":"What you should focus on: "}]}]},{"type":"text","value":"this website’s core focus is on data. A school will focus mainly on how to view the data and deal with it quickly and efficiently. Another important part is creating backup. Find a way to give the school the ability to create a backup of their data."}]},{"type":"element","tagName":"p","properties":{},"children":[{"type":"element","tagName":"strong","properties":{},"children":[{"type":"element","tagName":"strong","properties":{},"children":[{"type":"text","value":"Tip: "}]}]},{"type":"text","value":"an easy way to generate charts is through "},{"type":"element","tagName":"a","properties":{"href":"https://developers.google.com/chart/","rel":["noopener","nofollow"]},"children":[{"type":"text","value":"Google’s chart web service"}]},{"type":"text","value":"."}]},{"type":"element","tagName":"hr","properties":{},"children":[]},{"type":"element","tagName":"h1","properties":{"id":"conclusion"},"children":[{"type":"text","value":"Conclusion"}]},{"type":"element","tagName":"p","properties":{},"children":[{"type":"text","value":"This was a list of a few website ideas you can use to increase your knowledge and skills. There are so many more ideas you can think of to create, but this is merely to give you a head start."}]},{"type":"element","tagName":"p","properties":{},"children":[{"type":"element","tagName":"strong","properties":{},"children":[{"type":"element","tagName":"strong","properties":{},"children":[{"type":"text","value":"Your opinion: "}]}]},{"type":"text","value":"what other ideas can beginners use to create websites and improve their skills?"}]}],"data":{"quirksMode":false}},"tableOfContents":[{"id":"movies","heading":"Movies"},{"id":"online-bookstore","heading":"Online bookstore"},{"id":"tests-generator","heading":"Tests Generator"},{"id":"top-ten-lists","heading":"Top Ten Lists"},{"id":"budget-tracker","heading":"Budget Tracker"},{"id":"school-management","heading":"School Management"},{"id":"conclusion","heading":"Conclusion"}]},"featureImageSharp":{"base":"photo-1517694712202-14dd9538aa97.jpg","publicURL":"/static/6c6fc55bcf24f1e3efdaa0001ab5758e/photo-1517694712202-14dd9538aa97.jpg","imageMeta":{"width":2000,"height":1333},"childImageSharp":{"fluid":{"base64":"data:image/jpeg;base64,/9j/2wBDABALDA4MChAODQ4SERATGCgaGBYWGDEjJR0oOjM9PDkzODdASFxOQERXRTc4UG1RV19iZ2hnPk1xeXBkeFxlZ2P/2wBDARESEhgVGC8aGi9jQjhCY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2P/wgARCAANABQDASIAAhEBAxEB/8QAGAAAAwEBAAAAAAAAAAAAAAAAAAMEAgX/xAAUAQEAAAAAAAAAAAAAAAAAAAAA/9oADAMBAAIQAxAAAAHCumklKg//xAAYEAEBAAMAAAAAAAAAAAAAAAACAwABEf/aAAgBAQABBQK1aGopR77lZBowI3w5/8QAFBEBAAAAAAAAAAAAAAAAAAAAEP/aAAgBAwEBPwE//8QAFhEAAwAAAAAAAAAAAAAAAAAAARAx/9oACAECAQE/ATF//8QAGhAAAwADAQAAAAAAAAAAAAAAAAERAiExEP/aAAgBAQAGPwLJLLVGu+VrZVTh/8QAGhAAAwEBAQEAAAAAAAAAAAAAAAERITFBUf/aAAgBAQABPyF92MKI84Vfg9jfD6afTCpydGpzZ//aAAwDAQACAAMAAAAQu/8A/8QAFhEAAwAAAAAAAAAAAAAAAAAAARAx/9oACAEDAQE/EBV//8QAFhEAAwAAAAAAAAAAAAAAAAAAEDFR/9oACAECAQE/EIMf/8QAHhABAQABAwUAAAAAAAAAAAAAAREAMUFhIVFxkdH/2gAIAQEAAT8QNQUWoeucdkdNBAPV51wqw1q6gaNy40u1RsfOXQLuz5n/2Q==","aspectRatio":1.4957264957264957,"src":"/static/6c6fc55bcf24f1e3efdaa0001ab5758e/ea4ab/photo-1517694712202-14dd9538aa97.jpg","srcSet":"/static/6c6fc55bcf24f1e3efdaa0001ab5758e/477ba/photo-1517694712202-14dd9538aa97.jpg 175w,\n/static/6c6fc55bcf24f1e3efdaa0001ab5758e/06776/photo-1517694712202-14dd9538aa97.jpg 350w,\n/static/6c6fc55bcf24f1e3efdaa0001ab5758e/ea4ab/photo-1517694712202-14dd9538aa97.jpg 700w,\n/static/6c6fc55bcf24f1e3efdaa0001ab5758e/3055e/photo-1517694712202-14dd9538aa97.jpg 1050w,\n/static/6c6fc55bcf24f1e3efdaa0001ab5758e/eff08/photo-1517694712202-14dd9538aa97.jpg 1400w,\n/static/6c6fc55bcf24f1e3efdaa0001ab5758e/4e5f3/photo-1517694712202-14dd9538aa97.jpg 2000w","srcWebp":"/static/6c6fc55bcf24f1e3efdaa0001ab5758e/89afa/photo-1517694712202-14dd9538aa97.webp","srcSetWebp":"/static/6c6fc55bcf24f1e3efdaa0001ab5758e/9fca7/photo-1517694712202-14dd9538aa97.webp 175w,\n/static/6c6fc55bcf24f1e3efdaa0001ab5758e/37a4e/photo-1517694712202-14dd9538aa97.webp 350w,\n/static/6c6fc55bcf24f1e3efdaa0001ab5758e/89afa/photo-1517694712202-14dd9538aa97.webp 700w,\n/static/6c6fc55bcf24f1e3efdaa0001ab5758e/78e7a/photo-1517694712202-14dd9538aa97.webp 1050w,\n/static/6c6fc55bcf24f1e3efdaa0001ab5758e/03d34/photo-1517694712202-14dd9538aa97.webp 1400w,\n/static/6c6fc55bcf24f1e3efdaa0001ab5758e/49d6b/photo-1517694712202-14dd9538aa97.webp 2000w","sizes":"(max-width: 700px) 100vw, 700px"}}}},"allGhostPost":{"edges":[{"node":{"id":"Ghost__Post__6127ba1b3ed159214d382ebb","title":"Register a Keyword in Chrome's Omnibox in Your  Extension","slug":"register-a-keyword-in-chrome-omnibox-in-your-extension","featured":false,"feature_image":"https://res-1.cloudinary.com/hbqmf3mbz/image/upload/q_auto/v1/ghost-blog-images/Register-a-Keyword-in-Chrome-s-Omnibox-2.png","excerpt":"In this tutorial, we'll go over how to register a keyword with Chrome's address bar or Omnibox in your extension to handle the user's input.","custom_excerpt":"In this tutorial, we'll go over how to register a keyword with Chrome's address bar or Omnibox in your extension to handle the user's input.","visibility":"public","created_at_pretty":"4 Aug 2021","published_at_pretty":"9 Aug 2021","updated_at_pretty":"26 Aug 2021","created_at":"2021-08-04T11:01:16.000+00:00","published_at":"2021-08-09T06:43:53.000+00:00","updated_at":"2021-08-26T17:31:03.000+00:00","meta_title":null,"meta_description":null,"og_description":null,"og_image":null,"og_title":null,"twitter_description":null,"twitter_image":null,"twitter_title":null,"authors":[{"slug":"shahed","url":"https://backend.shahednasser.com/author/shahed/","name":"Shahed Nasser","bio":null,"cover_image":null,"profile_image":"https://backend.shahednasser.com/content/images/2022/03/IMG_0591.jpg","location":null,"website":null,"twitter":null,"facebook":null,"meta_title":null,"meta_description":null,"coverImageSharp":null,"profileImageSharp":null}],"primary_author":{"slug":"shahed","url":"https://backend.shahednasser.com/author/shahed/","name":"Shahed Nasser","bio":null,"cover_image":null,"profile_image":"https://backend.shahednasser.com/content/images/2022/03/IMG_0591.jpg","location":null,"website":null,"twitter":null,"facebook":null,"meta_title":null,"meta_description":null,"coverImageSharp":null,"profileImageSharp":{"base":"IMG_0591.jpg","publicURL":"/static/ceb49c3c631485453e71e00d7f84b069/IMG_0591.jpg","imageMeta":{"width":1182,"height":1179},"childImageSharp":{"fluid":{"base64":"data:image/jpeg;base64,/9j/2wBDABALDA4MChAODQ4SERATGCgaGBYWGDEjJR0oOjM9PDkzODdASFxOQERXRTc4UG1RV19iZ2hnPk1xeXBkeFxlZ2P/2wBDARESEhgVGC8aGi9jQjhCY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2P/wgARCAAUABQDASIAAhEBAxEB/8QAGAABAQEBAQAAAAAAAAAAAAAAAAMEAQL/xAAWAQEBAQAAAAAAAAAAAAAAAAADBAL/2gAMAwEAAhADEAAAAdXiFM6i0CohUWXoKn//xAAcEAACAgIDAAAAAAAAAAAAAAACAwESBBEhM0H/2gAIAQEAAQUCWySE3WEr7SzbXjAj4iKty+sOQ//EABYRAQEBAAAAAAAAAAAAAAAAAAERIP/aAAgBAwEBPwEhj//EABQRAQAAAAAAAAAAAAAAAAAAACD/2gAIAQIBAT8BH//EAB4QAAIBBAMBAAAAAAAAAAAAAAABIRESMUECECJx/9oACAEBAAY/ApVGWvOjzgtUwLlTZA0sdL4f/8QAHBAAAwACAwEAAAAAAAAAAAAAAAERITFBkbHB/9oACAEBAAE/IahkCy+N2GwZpjQiJHJCspUFY0QrSi+HqiW2rgf/2gAMAwEAAgADAAAAEPw3/wD/xAAYEQEBAAMAAAAAAAAAAAAAAAAAARExQf/aAAgBAwEBPxCtjDqP/8QAFxEBAQEBAAAAAAAAAAAAAAAAAQAxEf/aAAgBAgEBPxBFus6Tt//EAB8QAQEAAgIBBQAAAAAAAAAAAAERACExQWFRcYGR0f/aAAgBAQABPxAuaBPPzkO1wyX7F4wkwXanfZrFQgeqE9JgS14vVOvrERIJomVBKwt2jebAeP0yVa8h1n//2Q==","aspectRatio":1,"src":"/static/ceb49c3c631485453e71e00d7f84b069/31709/IMG_0591.jpg","srcSet":"/static/ceb49c3c631485453e71e00d7f84b069/f340b/IMG_0591.jpg 28w,\n/static/ceb49c3c631485453e71e00d7f84b069/22d64/IMG_0591.jpg 55w,\n/static/ceb49c3c631485453e71e00d7f84b069/31709/IMG_0591.jpg 110w,\n/static/ceb49c3c631485453e71e00d7f84b069/aa249/IMG_0591.jpg 165w,\n/static/ceb49c3c631485453e71e00d7f84b069/0dc33/IMG_0591.jpg 220w,\n/static/ceb49c3c631485453e71e00d7f84b069/d8257/IMG_0591.jpg 1182w","srcWebp":"/static/ceb49c3c631485453e71e00d7f84b069/8678c/IMG_0591.webp","srcSetWebp":"/static/ceb49c3c631485453e71e00d7f84b069/59cda/IMG_0591.webp 28w,\n/static/ceb49c3c631485453e71e00d7f84b069/7da75/IMG_0591.webp 55w,\n/static/ceb49c3c631485453e71e00d7f84b069/8678c/IMG_0591.webp 110w,\n/static/ceb49c3c631485453e71e00d7f84b069/f282e/IMG_0591.webp 165w,\n/static/ceb49c3c631485453e71e00d7f84b069/a7b21/IMG_0591.webp 220w,\n/static/ceb49c3c631485453e71e00d7f84b069/63099/IMG_0591.webp 1182w","sizes":"(max-width: 110px) 100vw, 110px"}}}},"primary_tag":{"slug":"browser-extensions","url":"https://backend.shahednasser.com/tag/browser-extensions/","name":"Browser Extensions","visibility":"public","feature_image":"https://backend.shahednasser.com/content/images/2022/01/photo-1616499370260-485b3e5ed653-2.jpeg","description":"Learn more about Browser Extensions through tutorials, articles, and tips.","meta_title":null,"meta_description":null,"featureImageSharp":{"base":"photo-1616499370260-485b3e5ed653-2.jpeg","publicURL":"/static/17d3062927434b0d9d22f3e45ece68b8/photo-1616499370260-485b3e5ed653-2.jpeg","imageMeta":{"width":2000,"height":1334},"childImageSharp":{"fluid":{"base64":"data:image/jpeg;base64,/9j/2wBDABALDA4MChAODQ4SERATGCgaGBYWGDEjJR0oOjM9PDkzODdASFxOQERXRTc4UG1RV19iZ2hnPk1xeXBkeFxlZ2P/2wBDARESEhgVGC8aGi9jQjhCY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2P/wgARCAANABQDASIAAhEBAxEB/8QAGAAAAgMAAAAAAAAAAAAAAAAAAAMCBAX/xAAVAQEBAAAAAAAAAAAAAAAAAAAAAv/aAAwDAQACEAMQAAABQ+hsRUBAf//EABoQAQADAAMAAAAAAAAAAAAAAAIAAQMEEyL/2gAIAQEAAQUCKpA3nefSo/JALzvlaXP/xAAVEQEBAAAAAAAAAAAAAAAAAAAQEf/aAAgBAwEBPwGH/8QAFREBAQAAAAAAAAAAAAAAAAAAERD/2gAIAQIBAT8BSf/EAB4QAAEDBAMAAAAAAAAAAAAAAAEAAiEDEBEiMVFh/9oACAEBAAY/Ai1x9CG2/VmEcoVCJwogL//EABoQAQEBAQADAAAAAAAAAAAAAAERACFBcYH/2gAIAQEAAT8h6pD2YW6ifbqrwMDk3W6VFUxyFZ4ib//aAAwDAQACAAMAAAAQLB//xAAXEQEAAwAAAAAAAAAAAAAAAAAAAREh/9oACAEDAQE/EJs1/8QAFREBAQAAAAAAAAAAAAAAAAAAABH/2gAIAQIBAT8QFf/EABwQAQEAAwEAAwAAAAAAAAAAAAERACExQVFhgf/aAAgBAQABPxCsM1ex6ZRBIol9InPbigkXRbrDW2E9042KoTWHL8fWeOKEM/Xuf//Z","aspectRatio":1.5028901734104045,"src":"/static/17d3062927434b0d9d22f3e45ece68b8/d5c54/photo-1616499370260-485b3e5ed653-2.jpg","srcSet":"/static/17d3062927434b0d9d22f3e45ece68b8/65d8c/photo-1616499370260-485b3e5ed653-2.jpg 260w,\n/static/17d3062927434b0d9d22f3e45ece68b8/c5f21/photo-1616499370260-485b3e5ed653-2.jpg 520w,\n/static/17d3062927434b0d9d22f3e45ece68b8/d5c54/photo-1616499370260-485b3e5ed653-2.jpg 1040w,\n/static/17d3062927434b0d9d22f3e45ece68b8/81a53/photo-1616499370260-485b3e5ed653-2.jpg 1560w,\n/static/17d3062927434b0d9d22f3e45ece68b8/4e5f3/photo-1616499370260-485b3e5ed653-2.jpg 2000w","srcWebp":"/static/17d3062927434b0d9d22f3e45ece68b8/e4875/photo-1616499370260-485b3e5ed653-2.webp","srcSetWebp":"/static/17d3062927434b0d9d22f3e45ece68b8/dc8f3/photo-1616499370260-485b3e5ed653-2.webp 260w,\n/static/17d3062927434b0d9d22f3e45ece68b8/2db4b/photo-1616499370260-485b3e5ed653-2.webp 520w,\n/static/17d3062927434b0d9d22f3e45ece68b8/e4875/photo-1616499370260-485b3e5ed653-2.webp 1040w,\n/static/17d3062927434b0d9d22f3e45ece68b8/f5845/photo-1616499370260-485b3e5ed653-2.webp 1560w,\n/static/17d3062927434b0d9d22f3e45ece68b8/49d6b/photo-1616499370260-485b3e5ed653-2.webp 2000w","sizes":"(max-width: 1040px) 100vw, 1040px"}}}},"tags":[{"slug":"browser-extensions","url":"https://backend.shahednasser.com/tag/browser-extensions/","name":"Browser Extensions","visibility":"public","feature_image":"https://backend.shahednasser.com/content/images/2022/01/photo-1616499370260-485b3e5ed653-2.jpeg","description":"Learn more about Browser Extensions through tutorials, articles, and tips.","meta_title":null,"meta_description":null,"featureImageSharp":null},{"slug":"js","url":"https://backend.shahednasser.com/tag/js/","name":"Javascript","visibility":"public","feature_image":"https://backend.shahednasser.com/content/images/2022/01/photo-1592609931095-54a2168ae893-2.jpeg","description":"Learn more about Javascript through tutorials, articles, and tips.","meta_title":null,"meta_description":"Learn more about Javascript through tutorials, articles and tips.","featureImageSharp":null}],"plaintext":"Using Chrome's API, you can do a lot of things in your extension. One of them is\nregistering a keyword in Chrome's address bar, also knows as the Omnibox\n[https://developer.chrome.com/docs/extensions/reference/omnibox/#type-DescriptionStyleType]\n, to handle the user's input when the user uses the keyword.\n\nIn this tutorial, we'll go over how to register a keyword with Chrome's address\nbar or Omnibox in your extension to handle the user's input. The extension we're\ncreating will register the keyword color, then when the user uses this keyword\nin the Omnibox, they can enter a hex color of 6 characters. Once the user enters\nthe hex, the extension will use COLOURlovers' API [http://www.colourlovers.com] \nto get information about the color and give the user the RGB equivalent of the\ncolor.\n\nIt should be noted that this tutorial will create a manifest V2 extension\ninstead of V3. This is due to an error that occurs with the Omnibox API in\nmanifest V3\n[https://github.com/GoogleChrome/chrome-extensions-samples/issues/541] at the\ntime of writing this article.\n\nYou can find the full code of the extension in this article in this GitHub\nrepository [https://github.com/shahednasser/chrome-omnibox-tutorial].\n\nCreate the Extension\nWe'll first create the extension. First, create the directory that will hold the\nextension. Then, inside the directory, create manifest.json with the following\ncontent:\n\n{\n    \"name\": \"hex-to-rgb\",\n    \"description\": \"Convert Hexadecimal colors to RGB right from Chrome's Omnibox\",\n    \"version\": \"0.0.1\",\n    \"manifest_version\": 2,\n    \"icons\": {\n        \"16\": \"images/icon16.png\",\n        \"32\": \"images/icon32.png\",\n        \"48\": \"images/icon48.png\",\n        \"128\": \"images/icon128.png\"\n    }\n}\n\nNote that for the icons I'm using an icon [https://iconscout.com/icons/color] by \nThalita Torres [https://iconscout.com/contributors/thalita-torres] on Iconscout\n[https://iconscout.com]. Make sure to download it either from the website or\nfrom the GitHub repository and place them just like detailed in the \nmanifest.json.\n\nNext, open Chrome and go to chrome://extensions. Then, enable Developer Mode \nfrom the top right if it's not enabled. After that, click on Load Unpacked and\nchoose the directory that you just created for the extension. You should see the\nextension after that in the list of extensions.\n\nRegister Omnibox Keyword\nTo register a keyword in your extension, you'll need to add a new key omnibox in\nyour manifest.json. The value of omnibox is an object with the key keyword. In\nour extension, we'll use the keyword color. Add the following in your \nmanifest.json:\n\n\"omnibox\": {\n\t\"keyword\": \"color\"\n}\n\nNext, we'll need to add a background script that will listen to when the user\nenters the keyword and then handle the user's input. Add the following in your \nmanifest.json to add a background script:\n\n\"background\": {\n    \"persistent\": false,\n\t\"scripts\": [\"background.js\"]\n}\n\nFor now, create an empty file background.js in the root of the directory.\n\nThen, go to chrome://extensions again and click on the refresh button for your\nextension. You should see a new background page registered.\n\nLet's test out the keyword we just added. Open a new tab, then enter in the\naddress bar (the Omnibox) color. You should see a suggestion for hex-to-rgb \nwhich is the extension we just created.\n\nIf you press TAB, you'll see that the extension's name is now added on the left\nin the Omnibox with the extension's icon. This means that now the extension will\nstart receiving the input being entered. As we still haven't added listeners to\nhandle the user's input, nothing will happen at the moment if you enter\nanything.\n\nAdd a Default Suggestion\nWhen using the Omnibox API, you can specify a default suggestion that will\nappear regardless of what the user enters. To do that, you can use the method \nchrome.omnibox.setDefaultSuggestion. This method accepts an object with one\nproperty description.\n\nThe description property accepts XML styling. This means you can use XML tags to\napply minimal styling to the suggestion you're showing to the user. These tags\nare:\n\n 1. <url>: shows the text inside it styled as a link.\n 2. <match>: shows the text inside it highlighted.\n 3. <dim>: shows the text inside it dimmed.\n\nIt should be noted that you can also nest the XML tags. For example, you can\nmake a URL also highlighted.\n\nAt the beginning of our background script, we'll add a default suggestion that\nwill let the user know what they need to enter to get a result. Add the\nfollowing at the beginning of background.js:\n\nchrome.omnibox.setDefaultSuggestion({\n    description: 'Enter a hex code of 6 characters to convert to RGB (for example, <match>ffffff</match>)'\n});\n\nNotice that in the description, we've used the XML tag <match>ffffff</match>.\nThis means that the suggestion ffffff will be highlighted.\n\nLet's test it. First, refresh the extension from chrome://extensions like we did\nbefore. Then, type color in a new tab and hit TAB. Try entering a character and\nyou'll see the suggestion we just defined in background.js. Note that the\ndefault suggestion does not appear until at least one character is entered.\n\nHandle Input\nIn this section, we'll handle the input the user enters after entering the\nkeyword. To do that, we'll add an event listener to the event \nchrome.omnibox.onInputChanged. The listener will receive 2 parameters, text \nwhich is the text the user entered, and suggest which is a callback function we\nshould use to send our suggestions that we want to show the user back to Chrome.\nThis event is triggered every time the user enters a character.\n\nAdd the following to background.js:\n\nchrome.omnibox.onInputChanged.addListener((text, suggest) => {\n    //check that the length of the text is 6 characters\n    if (text.length !== 6) {\n        suggest([]);\n        return;\n    }\n    \n    //TODO send the text to the API\n});\n\nIn our listener, for now, we're just checking if the text's length is not 6,\nthen we're calling the suggest callback passing it an empty array.\n\nNext, we'll send a request to the COLOURlovers API and retrieve the color's\ninformation. But before we do that, we need to add the API's URL in the\npermissions array in manifest.json so that requests to the API will be allowed.\nAdd the following to manifest.json:\n\n\"permissions\": [\n   \"http://www.colourlovers.com/*\"\n ]\n\nBack to our background script, we'll send a request to the endpoint \nhttp://www.colourlovers.com/api/color/ using the Fetch API\n[https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API]. This endpoint\naccepts the color as a parameter, then returns an array. The array is either\nempty if the color is not found (for example, if it's an invalid color) or an\narray that will hold an object with details about the color. For example, try\nopening the URL http://www.colourlovers.com/api/color/ffffff?format=json in your\nbrowser and see how the response will look like.\n\nAdd the following to your background script inside the listener:\n\n//send text to API\nfetch('http://www.colourlovers.com/api/color/' + text + '?format=json')\n    .then((response) => response.json())\n    .then((data) => {\n    \tif (!data.length) {\n            //no color was found\n            suggest([]);\n        } else {\n            //TODO send suggestion when color exists\n        }\n})\n\nIn case no color was found, we're sending back an empty array to the suggest \ncallback. We'll need to handle the case that color was found next and how the\nsuggestion should be sent.\n\nThe suggestion object has 3 properties:\n\n 1. content: Holds the text that will be placed inside the Omnibox when the user\n    points at the suggestion.\n 2. deletable: Whether the user can delete the suggestion from the suggestions\n    history. This property is optional.\n 3. description: similar to the description property we used in \n    setDefaultSuggestion, this will be the text that the user will see in the\n    list of suggestions. It can be styled using the XML tags just as we\n    mentioned in the previous section.\n\nWhen a color is received, we'll set the content inside the Omnibox to be the RGB\nvalue and the URL to the color in case the user wants to see additional\ninformation. We'll set the description to the color's name, the hex code, and\nthe URL to see more information.\n\nAdd the following code in place of the TODO we added in the previous code block:\n\nsuggest([\n    {\n        content: `${data[0].rgb.red}, ${data[0].rgb.green}, ${data[0].rgb.blue} url: ${data[0].url}`,\n        deletable: true,\n        description: `Color name: ${data[0].title}, hex: <match>${data[0].hex}</match>, more information: <url>${data[0].url}</url> `\n    }\n])\n\nOur code to convert the hex to RGB is done. Again, refresh the extension from \nchrome://extensions, open a new tab, type color in the Omnibox, and hit TAB.\nThen, enter a hex color. For example, enter f5f5f5. You should see a new\nsuggestion below. Try pointing at the suggestion using the keyboard up and down\narrows, and you'll see that the content of the Omnibox has changed to the text\nwe set in content.\n\nHandle Choosing Suggestion\nThe last event we need to handle is when the user selects the suggestion either\nby clicking on it or hitting enter. This event is chrome.omnibox.onInputEntered.\nThe listener to this event receives two parameters: the text which is the text\ninside the Omnibox, and OnInputEnteredDisposition, which suggests if the new URL\nshould be opened (if that's the action to be performed by the extension) in the \ncurrentTab, newForegroundTab or newBackgroundTab.\n\nWhat we'll do is extract the URL from the text, then if \nOnInputEnteredDisposition is currentTab, we'll replace the URL of the current\ntab with the URL extracted. Else, we'll open a new tab with the URL.\n\nIn background.js add the following:\n\nchrome.omnibox.onInputEntered.addListener((text, OnInputEnteredDisposition) => {\n    const prefixIndex = text.indexOf('url: ');\n    if (prefixIndex !== -1) {\n        const url = text.substring(prefixIndex + 'url: '.length)\n        console.log(url);\n        if (OnInputEnteredDisposition === 'currentTab') {\n            chrome.tabs.create({url});\n        } else {\n            chrome.tabs.update({url});\n        }\n    }\n});\n\nAs explained earlier, we're extracting the URL from text based on the format we\nwrote in content. Then, we're opening the URL based on the value of \nOnInputEnteredDisposition.\n\nLet's test it out. Again, refresh the extension from chrome://extensions, open a\nnew tab, type color in the Omnibox, and hit TAB. Then, enter a hex color. For\nexample, enter f5f5f5. Click on the suggestion shown for the color, or point at\nit with the up and down arrows on your keyboard and hit enter. You'll be taken\nto the page in COLOURlovers with the information about the color.\n\nConclusion\nUsing Chrome's Omnibox API, you can add functionalities to your extension to\nmake it easier for your user to find something or perform certain tasks. Make\nsure to check out the full API reference\n[https://developer.chrome.com/docs/extensions/reference/omnibox/#type-DescriptionStyleType] \nto know more about what you can do with this API.","html":"<p>Using Chrome's API, you can do a lot of things in your extension. One of them is registering a keyword in Chrome's address bar, also knows as the <a href=\"https://developer.chrome.com/docs/extensions/reference/omnibox/#type-DescriptionStyleType\">Omnibox</a>, to handle the user's input when the user uses the keyword.</p><p>In this tutorial, we'll go over how to register a keyword with Chrome's address bar or Omnibox in your extension to handle the user's input. The extension we're creating will register the keyword <code>color</code>, then when the user uses this keyword in the Omnibox, they can enter a hex color of 6 characters. Once the user enters the hex, the extension will use <a href=\"http://www.colourlovers.com\">COLOURlovers' API</a> to get information about the color and give the user the RGB equivalent of the color.</p><p>It should be noted that this tutorial will create a manifest V2 extension instead of V3. This is due to <a href=\"https://github.com/GoogleChrome/chrome-extensions-samples/issues/541\">an error that occurs with the Omnibox API in manifest V3</a> at the time of writing this article.</p><p>You can find the full code of the extension in this article in <a href=\"https://github.com/shahednasser/chrome-omnibox-tutorial\">this GitHub repository</a>.</p><h2 id=\"create-the-extension\">Create the Extension</h2><p>We'll first create the extension. First, create the directory that will hold the extension. Then, inside the directory, create <code>manifest.json</code> with the following content:</p><pre><code class=\"language-json\">{\n    \"name\": \"hex-to-rgb\",\n    \"description\": \"Convert Hexadecimal colors to RGB right from Chrome's Omnibox\",\n    \"version\": \"0.0.1\",\n    \"manifest_version\": 2,\n    \"icons\": {\n        \"16\": \"images/icon16.png\",\n        \"32\": \"images/icon32.png\",\n        \"48\": \"images/icon48.png\",\n        \"128\": \"images/icon128.png\"\n    }\n}</code></pre><p>Note that for the icons I'm using an <a href=\"https://iconscout.com/icons/color\">icon</a> by <a href=\"https://iconscout.com/contributors/thalita-torres\">Thalita Torres</a> on <a href=\"https://iconscout.com\">Iconscout</a>. Make sure to download it either from the website or from the GitHub repository and place them just like detailed in the <code>manifest.json</code>.</p><p>Next, open Chrome and go to <code>chrome://extensions</code>. Then, enable <em>Developer Mode </em>from the top right if it's not enabled. After that, click on <em>Load Unpacked </em>and choose the directory that you just created for the extension. You should see the extension after that in the list of extensions.</p><figure class=\"kg-card kg-image-card\"><img src=\"https://res-1.cloudinary.com/hbqmf3mbz/image/upload/q_auto/v1/ghost-blog-images/Screen-Shot-2021-08-04-at-2.18.29-PM.png\" class=\"kg-image\" alt loading=\"lazy\" width=\"838\" height=\"464\"></figure><h2 id=\"register-omnibox-keyword\">Register Omnibox Keyword</h2><p>To register a keyword in your extension, you'll need to add a new key <code>omnibox</code> in your <code>manifest.json</code>. The value of <code>omnibox</code> is an object with the key <code>keyword</code>. In our extension, we'll use the keyword <code>color</code>. Add the following in your <code>manifest.json</code>:</p><pre><code class=\"language-json\">\"omnibox\": {\n\t\"keyword\": \"color\"\n}</code></pre><p>Next, we'll need to add a background script that will listen to when the user enters the keyword and then handle the user's input. Add the following in your <code>manifest.json</code> to add a background script:</p><pre><code class=\"language-json\">\"background\": {\n    \"persistent\": false,\n\t\"scripts\": [\"background.js\"]\n}</code></pre><p>For now, create an empty file <code>background.js</code> in the root of the directory.</p><p>Then, go to <code>chrome://extensions</code> again and click on the refresh button for your extension. You should see a new background page registered.</p><figure class=\"kg-card kg-image-card\"><img src=\"https://res-3.cloudinary.com/hbqmf3mbz/image/upload/q_auto/v1/ghost-blog-images/Screen-Shot-2021-08-04-at-2.35.21-PM.png\" class=\"kg-image\" alt loading=\"lazy\" width=\"848\" height=\"458\"></figure><p>Let's test out the keyword we just added. Open a new tab, then enter in the address bar (the Omnibox) <code>color</code>. You should see a suggestion for <code>hex-to-rgb</code> which is the extension we just created.</p><figure class=\"kg-card kg-image-card\"><img src=\"https://res-4.cloudinary.com/hbqmf3mbz/image/upload/q_auto/v1/ghost-blog-images/Screen-Shot-2021-08-04-at-2.35.48-PM.png\" class=\"kg-image\" alt loading=\"lazy\" width=\"1818\" height=\"220\"></figure><p>If you press TAB, you'll see that the extension's name is now added on the left in the Omnibox with the extension's icon. This means that now the extension will start receiving the input being entered. As we still haven't added listeners to handle the user's input, nothing will happen at the moment if you enter anything.</p><figure class=\"kg-card kg-image-card\"><img src=\"https://res-3.cloudinary.com/hbqmf3mbz/image/upload/q_auto/v1/ghost-blog-images/Screen-Shot-2021-08-04-at-2.35.55-PM.png\" class=\"kg-image\" alt loading=\"lazy\" width=\"1822\" height=\"222\"></figure><h2 id=\"add-a-default-suggestion\">Add a Default Suggestion</h2><p>When using the Omnibox API, you can specify a default suggestion that will appear regardless of what the user enters. To do that, you can use the method <code>chrome.omnibox.setDefaultSuggestion</code>. This method accepts an object with one property <code>description</code>.</p><p>The description property accepts XML styling. This means you can use XML tags to apply minimal styling to the suggestion you're showing to the user. These tags are:</p><ol><li><code>&lt;url&gt;</code>: shows the text inside it styled as a link.</li><li><code>&lt;match&gt;</code>: shows the text inside it highlighted.</li><li><code>&lt;dim&gt;</code>: shows the text inside it dimmed.</li></ol><p>It should be noted that you can also nest the XML tags. For example, you can make a URL also highlighted.</p><p>At the beginning of our background script, we'll add a default suggestion that will let the user know what they need to enter to get a result. Add the following at the beginning of <code>background.js</code>:</p><pre><code class=\"language-js\">chrome.omnibox.setDefaultSuggestion({\n    description: 'Enter a hex code of 6 characters to convert to RGB (for example, &lt;match&gt;ffffff&lt;/match&gt;)'\n});</code></pre><p>Notice that in the <code>description</code>, we've used the XML tag <code>&lt;match&gt;ffffff&lt;/match&gt;</code>. This means that the suggestion <code>ffffff</code> will be highlighted.</p><p>Let's test it. First, refresh the extension from <code>chrome://extensions</code> like we did before. Then, type <code>color</code> in a new tab and hit <code>TAB</code>. Try entering a character and you'll see the suggestion we just defined in <code>background.js</code>. Note that the default suggestion does not appear until at least one character is entered.</p><figure class=\"kg-card kg-image-card\"><img src=\"https://res-5.cloudinary.com/hbqmf3mbz/image/upload/q_auto/v1/ghost-blog-images/Screen-Shot-2021-08-04-at-2.46.53-PM.png\" class=\"kg-image\" alt loading=\"lazy\" width=\"1812\" height=\"140\"></figure><h2 id=\"handle-input\">Handle Input</h2><p>In this section, we'll handle the input the user enters after entering the keyword. To do that, we'll add an event listener to the event <code>chrome.omnibox.onInputChanged</code>. The listener will receive 2 parameters, <code>text</code> which is the text the user entered, and <code>suggest</code> which is a callback function we should use to send our suggestions that we want to show the user back to Chrome. This event is triggered every time the user enters a character.</p><p>Add the following to <code>background.js</code>:</p><pre><code class=\"language-js\">chrome.omnibox.onInputChanged.addListener((text, suggest) =&gt; {\n    //check that the length of the text is 6 characters\n    if (text.length !== 6) {\n        suggest([]);\n        return;\n    }\n    \n    //TODO send the text to the API\n});</code></pre><p>In our listener, for now, we're just checking if the text's length is not 6, then we're calling the <code>suggest</code> callback passing it an empty array.</p><p>Next, we'll send a request to the COLOURlovers API and retrieve the color's information. But before we do that, we need to add the API's URL in the permissions array in <code>manifest.json</code> so that requests to the API will be allowed. Add the following to <code>manifest.json</code>:</p><pre><code class=\"language-json\">\"permissions\": [\n   \"http://www.colourlovers.com/*\"\n ]</code></pre><p>Back to our background script, we'll send a request to the endpoint <a href=\"http://www.colourlovers.com/api/color/\">http://www.colourlovers.com/api/color/</a> using the <a href=\"https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API\">Fetch API</a>. This endpoint accepts the color as a parameter, then returns an array. The array is either empty if the color is not found (for example, if it's an invalid color) or an array that will hold an object with details about the color. For example, try opening the URL <a href=\"http://www.colourlovers.com/api/color/ffffff?format=json\">http://www.colourlovers.com/api/color/ffffff?format=json</a> in your browser and see how the response will look like.</p><p>Add the following to your background script inside the listener:</p><pre><code class=\"language-js\">//send text to API\nfetch('http://www.colourlovers.com/api/color/' + text + '?format=json')\n    .then((response) =&gt; response.json())\n    .then((data) =&gt; {\n    \tif (!data.length) {\n            //no color was found\n            suggest([]);\n        } else {\n            //TODO send suggestion when color exists\n        }\n})</code></pre><p>In case no color was found, we're sending back an empty array to the <code>suggest</code> callback. We'll need to handle the case that color was found next and how the suggestion should be sent.</p><p>The suggestion object has 3 properties:</p><ol><li><code>content</code>: Holds the text that will be placed inside the Omnibox when the user points at the suggestion.</li><li><code>deletable</code>: Whether the user can delete the suggestion from the suggestions history. This property is optional.</li><li><code>description</code>: similar to the description property we used in <code>setDefaultSuggestion</code>, this will be the text that the user will see in the list of suggestions. It can be styled using the XML tags just as we mentioned in the previous section.</li></ol><p>When a color is received, we'll set the <code>content</code> inside the Omnibox to be the RGB value and the URL to the color in case the user wants to see additional information. We'll set the <code>description</code> to the color's name, the hex code, and the URL to see more information.</p><p>Add the following code in place of the <code>TODO</code> we added in the previous code block:</p><pre><code class=\"language-js\">suggest([\n    {\n        content: `${data[0].rgb.red}, ${data[0].rgb.green}, ${data[0].rgb.blue} url: ${data[0].url}`,\n        deletable: true,\n        description: `Color name: ${data[0].title}, hex: &lt;match&gt;${data[0].hex}&lt;/match&gt;, more information: &lt;url&gt;${data[0].url}&lt;/url&gt; `\n    }\n])</code></pre><p>Our code to convert the hex to RGB is done. Again, refresh the extension from <code>chrome://extensions</code>, open a new tab, type <code>color</code> in the Omnibox, and hit TAB. Then, enter a hex color. For example, enter <code>f5f5f5</code>. You should see a new suggestion below. Try pointing at the suggestion using the keyboard up and down arrows, and you'll see that the content of the Omnibox has changed to the text we set in <code>content</code>.</p><figure class=\"kg-card kg-image-card\"><img src=\"https://res-3.cloudinary.com/hbqmf3mbz/image/upload/q_auto/v1/ghost-blog-images/Screen-Shot-2021-08-04-at-3.42.17-PM.png\" class=\"kg-image\" alt loading=\"lazy\" width=\"1820\" height=\"214\"></figure><h2 id=\"handle-choosing-suggestion\">Handle Choosing Suggestion</h2><p>The last event we need to handle is when the user selects the suggestion either by clicking on it or hitting enter. This event is <code>chrome.omnibox.onInputEntered</code>. The listener to this event receives two parameters: the <code>text</code> which is the text inside the Omnibox, and <code>OnInputEnteredDisposition</code>, which suggests if the new URL should be opened (if that's the action to be performed by the extension) in the <code>currentTab</code>, <code>newForegroundTab</code> or <code>newBackgroundTab</code>.</p><p>What we'll do is extract the URL from the <code>text</code>, then if <code>OnInputEnteredDisposition</code> is <code>currentTab</code>, we'll replace the URL of the current tab with the URL extracted. Else, we'll open a new tab with the URL.</p><p>In <code>background.js</code> add the following:</p><pre><code class=\"language-js\">chrome.omnibox.onInputEntered.addListener((text, OnInputEnteredDisposition) =&gt; {\n    const prefixIndex = text.indexOf('url: ');\n    if (prefixIndex !== -1) {\n        const url = text.substring(prefixIndex + 'url: '.length)\n        console.log(url);\n        if (OnInputEnteredDisposition === 'currentTab') {\n            chrome.tabs.create({url});\n        } else {\n            chrome.tabs.update({url});\n        }\n    }\n});</code></pre><p>As explained earlier, we're extracting the <code>URL</code> from <code>text</code> based on the format we wrote in <code>content</code>. Then, we're opening the URL based on the value of <code>OnInputEnteredDisposition</code>.</p><p>Let's test it out. Again, refresh the extension from <code>chrome://extensions</code>, open a new tab, type <code>color</code> in the Omnibox, and hit TAB. Then, enter a hex color. For example, enter <code>f5f5f5</code>. Click on the suggestion shown for the color, or point at it with the up and down arrows on your keyboard and hit enter. You'll be taken to the page in COLOURlovers with the information about the color.</p><figure class=\"kg-card kg-image-card\"><img src=\"https://res-5.cloudinary.com/hbqmf3mbz/image/upload/q_auto/v1/ghost-blog-images/Screen-Shot-2021-08-04-at-3.49.37-PM.png\" class=\"kg-image\" alt loading=\"lazy\" width=\"2880\" height=\"1464\"></figure><h2 id=\"conclusion\">Conclusion</h2><p>Using Chrome's Omnibox API, you can add functionalities to your extension to make it easier for your user to find something or perform certain tasks. Make sure to check out the full <a href=\"https://developer.chrome.com/docs/extensions/reference/omnibox/#type-DescriptionStyleType\">API reference</a> to know more about what you can do with this API.</p>","url":"https://backend.shahednasser.com/register-a-keyword-in-chrome-omnibox-in-your-extension/","canonical_url":null,"uuid":"333a07a2-9435-4d0d-bef4-d1521d24b23e","codeinjection_foot":null,"codeinjection_head":null,"codeinjection_styles":null,"comment_id":"610a737c15470d001c9e3898","reading_time":8,"send_email_when_published":null,"email_subject":null,"childHtmlRehype":{"html":"<p>Using Chrome's API, you can do a lot of things in your extension. One of them is registering a keyword in Chrome's address bar, also knows as the <a href=\"https://developer.chrome.com/docs/extensions/reference/omnibox/#type-DescriptionStyleType\">Omnibox</a>, to handle the user's input when the user uses the keyword.</p><p>In this tutorial, we'll go over how to register a keyword with Chrome's address bar or Omnibox in your extension to handle the user's input. The extension we're creating will register the keyword <code class=\"language-text\">color</code>, then when the user uses this keyword in the Omnibox, they can enter a hex color of 6 characters. Once the user enters the hex, the extension will use <a href=\"http://www.colourlovers.com\">COLOURlovers' API</a> to get information about the color and give the user the RGB equivalent of the color.</p><p>It should be noted that this tutorial will create a manifest V2 extension instead of V3. This is due to <a href=\"https://github.com/GoogleChrome/chrome-extensions-samples/issues/541\">an error that occurs with the Omnibox API in manifest V3</a> at the time of writing this article.</p><p>You can find the full code of the extension in this article in <a href=\"https://github.com/shahednasser/chrome-omnibox-tutorial\">this GitHub repository</a>.</p><h2 id=\"create-the-extension\">Create the Extension</h2><p>We'll first create the extension. First, create the directory that will hold the extension. Then, inside the directory, create <code class=\"language-text\">manifest.json</code> with the following content:</p><div class=\"kg-card kg-code-card gatsby-highlight\" data-language=\"json\"><pre class=\"language-json\"><code class=\"language-json\"><span class=\"token punctuation\">{</span>\n    <span class=\"token property\">\"name\"</span><span class=\"token operator\">:</span> <span class=\"token string\">\"hex-to-rgb\"</span><span class=\"token punctuation\">,</span>\n    <span class=\"token property\">\"description\"</span><span class=\"token operator\">:</span> <span class=\"token string\">\"Convert Hexadecimal colors to RGB right from Chrome's Omnibox\"</span><span class=\"token punctuation\">,</span>\n    <span class=\"token property\">\"version\"</span><span class=\"token operator\">:</span> <span class=\"token string\">\"0.0.1\"</span><span class=\"token punctuation\">,</span>\n    <span class=\"token property\">\"manifest_version\"</span><span class=\"token operator\">:</span> <span class=\"token number\">2</span><span class=\"token punctuation\">,</span>\n    <span class=\"token property\">\"icons\"</span><span class=\"token operator\">:</span> <span class=\"token punctuation\">{</span>\n        <span class=\"token property\">\"16\"</span><span class=\"token operator\">:</span> <span class=\"token string\">\"images/icon16.png\"</span><span class=\"token punctuation\">,</span>\n        <span class=\"token property\">\"32\"</span><span class=\"token operator\">:</span> <span class=\"token string\">\"images/icon32.png\"</span><span class=\"token punctuation\">,</span>\n        <span class=\"token property\">\"48\"</span><span class=\"token operator\">:</span> <span class=\"token string\">\"images/icon48.png\"</span><span class=\"token punctuation\">,</span>\n        <span class=\"token property\">\"128\"</span><span class=\"token operator\">:</span> <span class=\"token string\">\"images/icon128.png\"</span>\n    <span class=\"token punctuation\">}</span>\n<span class=\"token punctuation\">}</span></code></pre></div><p>Note that for the icons I'm using an <a href=\"https://iconscout.com/icons/color\">icon</a> by <a href=\"https://iconscout.com/contributors/thalita-torres\">Thalita Torres</a> on <a href=\"https://iconscout.com\">Iconscout</a>. Make sure to download it either from the website or from the GitHub repository and place them just like detailed in the <code class=\"language-text\">manifest.json</code>.</p><p>Next, open Chrome and go to <code class=\"language-text\">chrome://extensions</code>. Then, enable <em>Developer Mode </em>from the top right if it's not enabled. After that, click on <em>Load Unpacked </em>and choose the directory that you just created for the extension. You should see the extension after that in the list of extensions.</p><figure class=\"kg-card kg-image-card\"><img src=\"https://res-1.cloudinary.com/hbqmf3mbz/image/upload/q_auto/v1/ghost-blog-images/Screen-Shot-2021-08-04-at-2.18.29-PM.png\" class=\"kg-image\" alt=\"\" loading=\"lazy\" width=\"838\" height=\"464\"></figure><h2 id=\"register-omnibox-keyword\">Register Omnibox Keyword</h2><p>To register a keyword in your extension, you'll need to add a new key <code class=\"language-text\">omnibox</code> in your <code class=\"language-text\">manifest.json</code>. The value of <code class=\"language-text\">omnibox</code> is an object with the key <code class=\"language-text\">keyword</code>. In our extension, we'll use the keyword <code class=\"language-text\">color</code>. Add the following in your <code class=\"language-text\">manifest.json</code>:</p><div class=\"kg-card kg-code-card gatsby-highlight\" data-language=\"json\"><pre class=\"language-json\"><code class=\"language-json\"><span class=\"token property\">\"omnibox\"</span><span class=\"token operator\">:</span> <span class=\"token punctuation\">{</span>\n\t<span class=\"token property\">\"keyword\"</span><span class=\"token operator\">:</span> <span class=\"token string\">\"color\"</span>\n<span class=\"token punctuation\">}</span></code></pre></div><p>Next, we'll need to add a background script that will listen to when the user enters the keyword and then handle the user's input. Add the following in your <code class=\"language-text\">manifest.json</code> to add a background script:</p><div class=\"kg-card kg-code-card gatsby-highlight\" data-language=\"json\"><pre class=\"language-json\"><code class=\"language-json\"><span class=\"token property\">\"background\"</span><span class=\"token operator\">:</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token property\">\"persistent\"</span><span class=\"token operator\">:</span> <span class=\"token boolean\">false</span><span class=\"token punctuation\">,</span>\n\t<span class=\"token property\">\"scripts\"</span><span class=\"token operator\">:</span> <span class=\"token punctuation\">[</span><span class=\"token string\">\"background.js\"</span><span class=\"token punctuation\">]</span>\n<span class=\"token punctuation\">}</span></code></pre></div><p>For now, create an empty file <code class=\"language-text\">background.js</code> in the root of the directory.</p><p>Then, go to <code class=\"language-text\">chrome://extensions</code> again and click on the refresh button for your extension. You should see a new background page registered.</p><figure class=\"kg-card kg-image-card\"><img src=\"https://res-3.cloudinary.com/hbqmf3mbz/image/upload/q_auto/v1/ghost-blog-images/Screen-Shot-2021-08-04-at-2.35.21-PM.png\" class=\"kg-image\" alt=\"\" loading=\"lazy\" width=\"848\" height=\"458\"></figure><p>Let's test out the keyword we just added. Open a new tab, then enter in the address bar (the Omnibox) <code class=\"language-text\">color</code>. You should see a suggestion for <code class=\"language-text\">hex-to-rgb</code> which is the extension we just created.</p><figure class=\"kg-card kg-image-card\"><img src=\"https://res-4.cloudinary.com/hbqmf3mbz/image/upload/q_auto/v1/ghost-blog-images/Screen-Shot-2021-08-04-at-2.35.48-PM.png\" class=\"kg-image\" alt=\"\" loading=\"lazy\" width=\"1818\" height=\"220\"></figure><p>If you press TAB, you'll see that the extension's name is now added on the left in the Omnibox with the extension's icon. This means that now the extension will start receiving the input being entered. As we still haven't added listeners to handle the user's input, nothing will happen at the moment if you enter anything.</p><figure class=\"kg-card kg-image-card\"><img src=\"https://res-3.cloudinary.com/hbqmf3mbz/image/upload/q_auto/v1/ghost-blog-images/Screen-Shot-2021-08-04-at-2.35.55-PM.png\" class=\"kg-image\" alt=\"\" loading=\"lazy\" width=\"1822\" height=\"222\"></figure><h2 id=\"add-a-default-suggestion\">Add a Default Suggestion</h2><p>When using the Omnibox API, you can specify a default suggestion that will appear regardless of what the user enters. To do that, you can use the method <code class=\"language-text\">chrome.omnibox.setDefaultSuggestion</code>. This method accepts an object with one property <code class=\"language-text\">description</code>.</p><p>The description property accepts XML styling. This means you can use XML tags to apply minimal styling to the suggestion you're showing to the user. These tags are:</p><ol><li><code class=\"language-text\">&#x3C;url></code>: shows the text inside it styled as a link.</li><li><code class=\"language-text\">&#x3C;match></code>: shows the text inside it highlighted.</li><li><code class=\"language-text\">&#x3C;dim></code>: shows the text inside it dimmed.</li></ol><p>It should be noted that you can also nest the XML tags. For example, you can make a URL also highlighted.</p><p>At the beginning of our background script, we'll add a default suggestion that will let the user know what they need to enter to get a result. Add the following at the beginning of <code class=\"language-text\">background.js</code>:</p><div class=\"kg-card kg-code-card gatsby-highlight\" data-language=\"js\"><pre class=\"language-js\"><code class=\"language-js\">chrome<span class=\"token punctuation\">.</span>omnibox<span class=\"token punctuation\">.</span><span class=\"token function\">setDefaultSuggestion</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">{</span>\n    <span class=\"token literal-property property\">description</span><span class=\"token operator\">:</span> <span class=\"token string\">'Enter a hex code of 6 characters to convert to RGB (for example, &#x3C;match>ffffff&#x3C;/match>)'</span>\n<span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span></code></pre></div><p>Notice that in the <code class=\"language-text\">description</code>, we've used the XML tag <code class=\"language-text\">&#x3C;match>ffffff&#x3C;/match></code>. This means that the suggestion <code class=\"language-text\">ffffff</code> will be highlighted.</p><p>Let's test it. First, refresh the extension from <code class=\"language-text\">chrome://extensions</code> like we did before. Then, type <code class=\"language-text\">color</code> in a new tab and hit <code class=\"language-text\">TAB</code>. Try entering a character and you'll see the suggestion we just defined in <code class=\"language-text\">background.js</code>. Note that the default suggestion does not appear until at least one character is entered.</p><figure class=\"kg-card kg-image-card\"><img src=\"https://res-5.cloudinary.com/hbqmf3mbz/image/upload/q_auto/v1/ghost-blog-images/Screen-Shot-2021-08-04-at-2.46.53-PM.png\" class=\"kg-image\" alt=\"\" loading=\"lazy\" width=\"1812\" height=\"140\"></figure><h2 id=\"handle-input\">Handle Input</h2><p>In this section, we'll handle the input the user enters after entering the keyword. To do that, we'll add an event listener to the event <code class=\"language-text\">chrome.omnibox.onInputChanged</code>. The listener will receive 2 parameters, <code class=\"language-text\">text</code> which is the text the user entered, and <code class=\"language-text\">suggest</code> which is a callback function we should use to send our suggestions that we want to show the user back to Chrome. This event is triggered every time the user enters a character.</p><p>Add the following to <code class=\"language-text\">background.js</code>:</p><div class=\"kg-card kg-code-card gatsby-highlight\" data-language=\"js\"><pre class=\"language-js\"><code class=\"language-js\">chrome<span class=\"token punctuation\">.</span>omnibox<span class=\"token punctuation\">.</span>onInputChanged<span class=\"token punctuation\">.</span><span class=\"token function\">addListener</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">(</span><span class=\"token parameter\">text<span class=\"token punctuation\">,</span> suggest</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">=></span> <span class=\"token punctuation\">{</span>\n    <span class=\"token comment\">//check that the length of the text is 6 characters</span>\n    <span class=\"token keyword\">if</span> <span class=\"token punctuation\">(</span>text<span class=\"token punctuation\">.</span>length <span class=\"token operator\">!==</span> <span class=\"token number\">6</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n        <span class=\"token function\">suggest</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">[</span><span class=\"token punctuation\">]</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n        <span class=\"token keyword\">return</span><span class=\"token punctuation\">;</span>\n    <span class=\"token punctuation\">}</span>\n    \n    <span class=\"token comment\">//TODO send the text to the API</span>\n<span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span></code></pre></div><p>In our listener, for now, we're just checking if the text's length is not 6, then we're calling the <code class=\"language-text\">suggest</code> callback passing it an empty array.</p><p>Next, we'll send a request to the COLOURlovers API and retrieve the color's information. But before we do that, we need to add the API's URL in the permissions array in <code class=\"language-text\">manifest.json</code> so that requests to the API will be allowed. Add the following to <code class=\"language-text\">manifest.json</code>:</p><div class=\"kg-card kg-code-card gatsby-highlight\" data-language=\"json\"><pre class=\"language-json\"><code class=\"language-json\"><span class=\"token property\">\"permissions\"</span><span class=\"token operator\">:</span> <span class=\"token punctuation\">[</span>\n   <span class=\"token string\">\"http://www.colourlovers.com/*\"</span>\n <span class=\"token punctuation\">]</span></code></pre></div><p>Back to our background script, we'll send a request to the endpoint <a href=\"http://www.colourlovers.com/api/color/\">http://www.colourlovers.com/api/color/</a> using the <a href=\"https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API\">Fetch API</a>. This endpoint accepts the color as a parameter, then returns an array. The array is either empty if the color is not found (for example, if it's an invalid color) or an array that will hold an object with details about the color. For example, try opening the URL <a href=\"http://www.colourlovers.com/api/color/ffffff?format=json\">http://www.colourlovers.com/api/color/ffffff?format=json</a> in your browser and see how the response will look like.</p><p>Add the following to your background script inside the listener:</p><div class=\"kg-card kg-code-card gatsby-highlight\" data-language=\"js\"><pre class=\"language-js\"><code class=\"language-js\"><span class=\"token comment\">//send text to API</span>\n<span class=\"token function\">fetch</span><span class=\"token punctuation\">(</span><span class=\"token string\">'http://www.colourlovers.com/api/color/'</span> <span class=\"token operator\">+</span> text <span class=\"token operator\">+</span> <span class=\"token string\">'?format=json'</span><span class=\"token punctuation\">)</span>\n    <span class=\"token punctuation\">.</span><span class=\"token function\">then</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">(</span><span class=\"token parameter\">response</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">=></span> response<span class=\"token punctuation\">.</span><span class=\"token function\">json</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span>\n    <span class=\"token punctuation\">.</span><span class=\"token function\">then</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">(</span><span class=\"token parameter\">data</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">=></span> <span class=\"token punctuation\">{</span>\n    \t<span class=\"token keyword\">if</span> <span class=\"token punctuation\">(</span><span class=\"token operator\">!</span>data<span class=\"token punctuation\">.</span>length<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n            <span class=\"token comment\">//no color was found</span>\n            <span class=\"token function\">suggest</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">[</span><span class=\"token punctuation\">]</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n        <span class=\"token punctuation\">}</span> <span class=\"token keyword\">else</span> <span class=\"token punctuation\">{</span>\n            <span class=\"token comment\">//TODO send suggestion when color exists</span>\n        <span class=\"token punctuation\">}</span>\n<span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span></code></pre></div><p>In case no color was found, we're sending back an empty array to the <code class=\"language-text\">suggest</code> callback. We'll need to handle the case that color was found next and how the suggestion should be sent.</p><p>The suggestion object has 3 properties:</p><ol><li><code class=\"language-text\">content</code>: Holds the text that will be placed inside the Omnibox when the user points at the suggestion.</li><li><code class=\"language-text\">deletable</code>: Whether the user can delete the suggestion from the suggestions history. This property is optional.</li><li><code class=\"language-text\">description</code>: similar to the description property we used in <code class=\"language-text\">setDefaultSuggestion</code>, this will be the text that the user will see in the list of suggestions. It can be styled using the XML tags just as we mentioned in the previous section.</li></ol><p>When a color is received, we'll set the <code class=\"language-text\">content</code> inside the Omnibox to be the RGB value and the URL to the color in case the user wants to see additional information. We'll set the <code class=\"language-text\">description</code> to the color's name, the hex code, and the URL to see more information.</p><p>Add the following code in place of the <code class=\"language-text\">TODO</code> we added in the previous code block:</p><div class=\"kg-card kg-code-card gatsby-highlight\" data-language=\"js\"><pre class=\"language-js\"><code class=\"language-js\"><span class=\"token function\">suggest</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">[</span>\n    <span class=\"token punctuation\">{</span>\n        <span class=\"token literal-property property\">content</span><span class=\"token operator\">:</span> <span class=\"token template-string\"><span class=\"token template-punctuation string\">`</span><span class=\"token interpolation\"><span class=\"token interpolation-punctuation punctuation\">${</span>data<span class=\"token punctuation\">[</span><span class=\"token number\">0</span><span class=\"token punctuation\">]</span><span class=\"token punctuation\">.</span>rgb<span class=\"token punctuation\">.</span>red<span class=\"token interpolation-punctuation punctuation\">}</span></span><span class=\"token string\">, </span><span class=\"token interpolation\"><span class=\"token interpolation-punctuation punctuation\">${</span>data<span class=\"token punctuation\">[</span><span class=\"token number\">0</span><span class=\"token punctuation\">]</span><span class=\"token punctuation\">.</span>rgb<span class=\"token punctuation\">.</span>green<span class=\"token interpolation-punctuation punctuation\">}</span></span><span class=\"token string\">, </span><span class=\"token interpolation\"><span class=\"token interpolation-punctuation punctuation\">${</span>data<span class=\"token punctuation\">[</span><span class=\"token number\">0</span><span class=\"token punctuation\">]</span><span class=\"token punctuation\">.</span>rgb<span class=\"token punctuation\">.</span>blue<span class=\"token interpolation-punctuation punctuation\">}</span></span><span class=\"token string\"> url: </span><span class=\"token interpolation\"><span class=\"token interpolation-punctuation punctuation\">${</span>data<span class=\"token punctuation\">[</span><span class=\"token number\">0</span><span class=\"token punctuation\">]</span><span class=\"token punctuation\">.</span>url<span class=\"token interpolation-punctuation punctuation\">}</span></span><span class=\"token template-punctuation string\">`</span></span><span class=\"token punctuation\">,</span>\n        <span class=\"token literal-property property\">deletable</span><span class=\"token operator\">:</span> <span class=\"token boolean\">true</span><span class=\"token punctuation\">,</span>\n        <span class=\"token literal-property property\">description</span><span class=\"token operator\">:</span> <span class=\"token template-string\"><span class=\"token template-punctuation string\">`</span><span class=\"token string\">Color name: </span><span class=\"token interpolation\"><span class=\"token interpolation-punctuation punctuation\">${</span>data<span class=\"token punctuation\">[</span><span class=\"token number\">0</span><span class=\"token punctuation\">]</span><span class=\"token punctuation\">.</span>title<span class=\"token interpolation-punctuation punctuation\">}</span></span><span class=\"token string\">, hex: &#x3C;match></span><span class=\"token interpolation\"><span class=\"token interpolation-punctuation punctuation\">${</span>data<span class=\"token punctuation\">[</span><span class=\"token number\">0</span><span class=\"token punctuation\">]</span><span class=\"token punctuation\">.</span>hex<span class=\"token interpolation-punctuation punctuation\">}</span></span><span class=\"token string\">&#x3C;/match>, more information: &#x3C;url></span><span class=\"token interpolation\"><span class=\"token interpolation-punctuation punctuation\">${</span>data<span class=\"token punctuation\">[</span><span class=\"token number\">0</span><span class=\"token punctuation\">]</span><span class=\"token punctuation\">.</span>url<span class=\"token interpolation-punctuation punctuation\">}</span></span><span class=\"token string\">&#x3C;/url> </span><span class=\"token template-punctuation string\">`</span></span>\n    <span class=\"token punctuation\">}</span>\n<span class=\"token punctuation\">]</span><span class=\"token punctuation\">)</span></code></pre></div><p>Our code to convert the hex to RGB is done. Again, refresh the extension from <code class=\"language-text\">chrome://extensions</code>, open a new tab, type <code class=\"language-text\">color</code> in the Omnibox, and hit TAB. Then, enter a hex color. For example, enter <code class=\"language-text\">f5f5f5</code>. You should see a new suggestion below. Try pointing at the suggestion using the keyboard up and down arrows, and you'll see that the content of the Omnibox has changed to the text we set in <code class=\"language-text\">content</code>.</p><figure class=\"kg-card kg-image-card\"><img src=\"https://res-3.cloudinary.com/hbqmf3mbz/image/upload/q_auto/v1/ghost-blog-images/Screen-Shot-2021-08-04-at-3.42.17-PM.png\" class=\"kg-image\" alt=\"\" loading=\"lazy\" width=\"1820\" height=\"214\"></figure><h2 id=\"handle-choosing-suggestion\">Handle Choosing Suggestion</h2><p>The last event we need to handle is when the user selects the suggestion either by clicking on it or hitting enter. This event is <code class=\"language-text\">chrome.omnibox.onInputEntered</code>. The listener to this event receives two parameters: the <code class=\"language-text\">text</code> which is the text inside the Omnibox, and <code class=\"language-text\">OnInputEnteredDisposition</code>, which suggests if the new URL should be opened (if that's the action to be performed by the extension) in the <code class=\"language-text\">currentTab</code>, <code class=\"language-text\">newForegroundTab</code> or <code class=\"language-text\">newBackgroundTab</code>.</p><p>What we'll do is extract the URL from the <code class=\"language-text\">text</code>, then if <code class=\"language-text\">OnInputEnteredDisposition</code> is <code class=\"language-text\">currentTab</code>, we'll replace the URL of the current tab with the URL extracted. Else, we'll open a new tab with the URL.</p><p>In <code class=\"language-text\">background.js</code> add the following:</p><div class=\"kg-card kg-code-card gatsby-highlight\" data-language=\"js\"><pre class=\"language-js\"><code class=\"language-js\">chrome<span class=\"token punctuation\">.</span>omnibox<span class=\"token punctuation\">.</span>onInputEntered<span class=\"token punctuation\">.</span><span class=\"token function\">addListener</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">(</span><span class=\"token parameter\">text<span class=\"token punctuation\">,</span> OnInputEnteredDisposition</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">=></span> <span class=\"token punctuation\">{</span>\n    <span class=\"token keyword\">const</span> prefixIndex <span class=\"token operator\">=</span> text<span class=\"token punctuation\">.</span><span class=\"token function\">indexOf</span><span class=\"token punctuation\">(</span><span class=\"token string\">'url: '</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n    <span class=\"token keyword\">if</span> <span class=\"token punctuation\">(</span>prefixIndex <span class=\"token operator\">!==</span> <span class=\"token operator\">-</span><span class=\"token number\">1</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n        <span class=\"token keyword\">const</span> url <span class=\"token operator\">=</span> text<span class=\"token punctuation\">.</span><span class=\"token function\">substring</span><span class=\"token punctuation\">(</span>prefixIndex <span class=\"token operator\">+</span> <span class=\"token string\">'url: '</span><span class=\"token punctuation\">.</span>length<span class=\"token punctuation\">)</span>\n        console<span class=\"token punctuation\">.</span><span class=\"token function\">log</span><span class=\"token punctuation\">(</span>url<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n        <span class=\"token keyword\">if</span> <span class=\"token punctuation\">(</span>OnInputEnteredDisposition <span class=\"token operator\">===</span> <span class=\"token string\">'currentTab'</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n            chrome<span class=\"token punctuation\">.</span>tabs<span class=\"token punctuation\">.</span><span class=\"token function\">create</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">{</span>url<span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n        <span class=\"token punctuation\">}</span> <span class=\"token keyword\">else</span> <span class=\"token punctuation\">{</span>\n            chrome<span class=\"token punctuation\">.</span>tabs<span class=\"token punctuation\">.</span><span class=\"token function\">update</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">{</span>url<span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n        <span class=\"token punctuation\">}</span>\n    <span class=\"token punctuation\">}</span>\n<span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span></code></pre></div><p>As explained earlier, we're extracting the <code class=\"language-text\">URL</code> from <code class=\"language-text\">text</code> based on the format we wrote in <code class=\"language-text\">content</code>. Then, we're opening the URL based on the value of <code class=\"language-text\">OnInputEnteredDisposition</code>.</p><p>Let's test it out. Again, refresh the extension from <code class=\"language-text\">chrome://extensions</code>, open a new tab, type <code class=\"language-text\">color</code> in the Omnibox, and hit TAB. Then, enter a hex color. For example, enter <code class=\"language-text\">f5f5f5</code>. Click on the suggestion shown for the color, or point at it with the up and down arrows on your keyboard and hit enter. You'll be taken to the page in COLOURlovers with the information about the color.</p><figure class=\"kg-card kg-image-card\"><img src=\"https://res-5.cloudinary.com/hbqmf3mbz/image/upload/q_auto/v1/ghost-blog-images/Screen-Shot-2021-08-04-at-3.49.37-PM.png\" class=\"kg-image\" alt=\"\" loading=\"lazy\" width=\"2880\" height=\"1464\"></figure><h2 id=\"conclusion\">Conclusion</h2><p>Using Chrome's Omnibox API, you can add functionalities to your extension to make it easier for your user to find something or perform certain tasks. Make sure to check out the full <a href=\"https://developer.chrome.com/docs/extensions/reference/omnibox/#type-DescriptionStyleType\">API reference</a> to know more about what you can do with this API.</p>","htmlAst":{"type":"root","children":[{"type":"element","tagName":"p","properties":{},"children":[{"type":"text","value":"Using Chrome's API, you can do a lot of things in your extension. One of them is registering a keyword in Chrome's address bar, also knows as the "},{"type":"element","tagName":"a","properties":{"href":"https://developer.chrome.com/docs/extensions/reference/omnibox/#type-DescriptionStyleType"},"children":[{"type":"text","value":"Omnibox"}]},{"type":"text","value":", to handle the user's input when the user uses the keyword."}]},{"type":"element","tagName":"p","properties":{},"children":[{"type":"text","value":"In this tutorial, we'll go over how to register a keyword with Chrome's address bar or Omnibox in your extension to handle the user's input. The extension we're creating will register the keyword "},{"type":"element","tagName":"code","properties":{"className":["language-text"]},"children":[{"type":"text","value":"color"}]},{"type":"text","value":", then when the user uses this keyword in the Omnibox, they can enter a hex color of 6 characters. Once the user enters the hex, the extension will use "},{"type":"element","tagName":"a","properties":{"href":"http://www.colourlovers.com"},"children":[{"type":"text","value":"COLOURlovers' API"}]},{"type":"text","value":" to get information about the color and give the user the RGB equivalent of the color."}]},{"type":"element","tagName":"p","properties":{},"children":[{"type":"text","value":"It should be noted that this tutorial will create a manifest V2 extension instead of V3. This is due to "},{"type":"element","tagName":"a","properties":{"href":"https://github.com/GoogleChrome/chrome-extensions-samples/issues/541"},"children":[{"type":"text","value":"an error that occurs with the Omnibox API in manifest V3"}]},{"type":"text","value":" at the time of writing this article."}]},{"type":"element","tagName":"p","properties":{},"children":[{"type":"text","value":"You can find the full code of the extension in this article in "},{"type":"element","tagName":"a","properties":{"href":"https://github.com/shahednasser/chrome-omnibox-tutorial"},"children":[{"type":"text","value":"this GitHub repository"}]},{"type":"text","value":"."}]},{"type":"element","tagName":"h2","properties":{"id":"create-the-extension"},"children":[{"type":"text","value":"Create the Extension"}]},{"type":"element","tagName":"p","properties":{},"children":[{"type":"text","value":"We'll first create the extension. First, create the directory that will hold the extension. Then, inside the directory, create "},{"type":"element","tagName":"code","properties":{"className":["language-text"]},"children":[{"type":"text","value":"manifest.json"}]},{"type":"text","value":" with the following content:"}]},{"type":"element","tagName":"div","properties":{"className":["kg-card","kg-code-card","gatsby-highlight"],"dataLanguage":"json"},"children":[{"type":"element","tagName":"pre","properties":{"className":["language-json"]},"children":[{"type":"element","tagName":"code","properties":{"className":["language-json"]},"children":[{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"{"}]},{"type":"text","value":"\n    "},{"type":"element","tagName":"span","properties":{"className":["token","property"]},"children":[{"type":"text","value":"\"name\""}]},{"type":"element","tagName":"span","properties":{"className":["token","operator"]},"children":[{"type":"text","value":":"}]},{"type":"text","value":" "},{"type":"element","tagName":"span","properties":{"className":["token","string"]},"children":[{"type":"text","value":"\"hex-to-rgb\""}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":","}]},{"type":"text","value":"\n    "},{"type":"element","tagName":"span","properties":{"className":["token","property"]},"children":[{"type":"text","value":"\"description\""}]},{"type":"element","tagName":"span","properties":{"className":["token","operator"]},"children":[{"type":"text","value":":"}]},{"type":"text","value":" "},{"type":"element","tagName":"span","properties":{"className":["token","string"]},"children":[{"type":"text","value":"\"Convert Hexadecimal colors to RGB right from Chrome's Omnibox\""}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":","}]},{"type":"text","value":"\n    "},{"type":"element","tagName":"span","properties":{"className":["token","property"]},"children":[{"type":"text","value":"\"version\""}]},{"type":"element","tagName":"span","properties":{"className":["token","operator"]},"children":[{"type":"text","value":":"}]},{"type":"text","value":" "},{"type":"element","tagName":"span","properties":{"className":["token","string"]},"children":[{"type":"text","value":"\"0.0.1\""}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":","}]},{"type":"text","value":"\n    "},{"type":"element","tagName":"span","properties":{"className":["token","property"]},"children":[{"type":"text","value":"\"manifest_version\""}]},{"type":"element","tagName":"span","properties":{"className":["token","operator"]},"children":[{"type":"text","value":":"}]},{"type":"text","value":" "},{"type":"element","tagName":"span","properties":{"className":["token","number"]},"children":[{"type":"text","value":"2"}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":","}]},{"type":"text","value":"\n    "},{"type":"element","tagName":"span","properties":{"className":["token","property"]},"children":[{"type":"text","value":"\"icons\""}]},{"type":"element","tagName":"span","properties":{"className":["token","operator"]},"children":[{"type":"text","value":":"}]},{"type":"text","value":" "},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"{"}]},{"type":"text","value":"\n        "},{"type":"element","tagName":"span","properties":{"className":["token","property"]},"children":[{"type":"text","value":"\"16\""}]},{"type":"element","tagName":"span","properties":{"className":["token","operator"]},"children":[{"type":"text","value":":"}]},{"type":"text","value":" "},{"type":"element","tagName":"span","properties":{"className":["token","string"]},"children":[{"type":"text","value":"\"images/icon16.png\""}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":","}]},{"type":"text","value":"\n        "},{"type":"element","tagName":"span","properties":{"className":["token","property"]},"children":[{"type":"text","value":"\"32\""}]},{"type":"element","tagName":"span","properties":{"className":["token","operator"]},"children":[{"type":"text","value":":"}]},{"type":"text","value":" "},{"type":"element","tagName":"span","properties":{"className":["token","string"]},"children":[{"type":"text","value":"\"images/icon32.png\""}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":","}]},{"type":"text","value":"\n        "},{"type":"element","tagName":"span","properties":{"className":["token","property"]},"children":[{"type":"text","value":"\"48\""}]},{"type":"element","tagName":"span","properties":{"className":["token","operator"]},"children":[{"type":"text","value":":"}]},{"type":"text","value":" "},{"type":"element","tagName":"span","properties":{"className":["token","string"]},"children":[{"type":"text","value":"\"images/icon48.png\""}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":","}]},{"type":"text","value":"\n        "},{"type":"element","tagName":"span","properties":{"className":["token","property"]},"children":[{"type":"text","value":"\"128\""}]},{"type":"element","tagName":"span","properties":{"className":["token","operator"]},"children":[{"type":"text","value":":"}]},{"type":"text","value":" "},{"type":"element","tagName":"span","properties":{"className":["token","string"]},"children":[{"type":"text","value":"\"images/icon128.png\""}]},{"type":"text","value":"\n    "},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"}"}]},{"type":"text","value":"\n"},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"}"}]}]}]}]},{"type":"element","tagName":"p","properties":{},"children":[{"type":"text","value":"Note that for the icons I'm using an "},{"type":"element","tagName":"a","properties":{"href":"https://iconscout.com/icons/color"},"children":[{"type":"text","value":"icon"}]},{"type":"text","value":" by "},{"type":"element","tagName":"a","properties":{"href":"https://iconscout.com/contributors/thalita-torres"},"children":[{"type":"text","value":"Thalita Torres"}]},{"type":"text","value":" on "},{"type":"element","tagName":"a","properties":{"href":"https://iconscout.com"},"children":[{"type":"text","value":"Iconscout"}]},{"type":"text","value":". Make sure to download it either from the website or from the GitHub repository and place them just like detailed in the "},{"type":"element","tagName":"code","properties":{"className":["language-text"]},"children":[{"type":"text","value":"manifest.json"}]},{"type":"text","value":"."}]},{"type":"element","tagName":"p","properties":{},"children":[{"type":"text","value":"Next, open Chrome and go to "},{"type":"element","tagName":"code","properties":{"className":["language-text"]},"children":[{"type":"text","value":"chrome://extensions"}]},{"type":"text","value":". Then, enable "},{"type":"element","tagName":"em","properties":{},"children":[{"type":"text","value":"Developer Mode "}]},{"type":"text","value":"from the top right if it's not enabled. After that, click on "},{"type":"element","tagName":"em","properties":{},"children":[{"type":"text","value":"Load Unpacked "}]},{"type":"text","value":"and choose the directory that you just created for the extension. You should see the extension after that in the list of extensions."}]},{"type":"element","tagName":"figure","properties":{"className":["kg-card","kg-image-card"]},"children":[{"type":"element","tagName":"img","properties":{"src":"https://res-1.cloudinary.com/hbqmf3mbz/image/upload/q_auto/v1/ghost-blog-images/Screen-Shot-2021-08-04-at-2.18.29-PM.png","className":["kg-image"],"alt":"","loading":"lazy","width":838,"height":464},"children":[]}]},{"type":"element","tagName":"h2","properties":{"id":"register-omnibox-keyword"},"children":[{"type":"text","value":"Register Omnibox Keyword"}]},{"type":"element","tagName":"p","properties":{},"children":[{"type":"text","value":"To register a keyword in your extension, you'll need to add a new key "},{"type":"element","tagName":"code","properties":{"className":["language-text"]},"children":[{"type":"text","value":"omnibox"}]},{"type":"text","value":" in your "},{"type":"element","tagName":"code","properties":{"className":["language-text"]},"children":[{"type":"text","value":"manifest.json"}]},{"type":"text","value":". The value of "},{"type":"element","tagName":"code","properties":{"className":["language-text"]},"children":[{"type":"text","value":"omnibox"}]},{"type":"text","value":" is an object with the key "},{"type":"element","tagName":"code","properties":{"className":["language-text"]},"children":[{"type":"text","value":"keyword"}]},{"type":"text","value":". In our extension, we'll use the keyword "},{"type":"element","tagName":"code","properties":{"className":["language-text"]},"children":[{"type":"text","value":"color"}]},{"type":"text","value":". Add the following in your "},{"type":"element","tagName":"code","properties":{"className":["language-text"]},"children":[{"type":"text","value":"manifest.json"}]},{"type":"text","value":":"}]},{"type":"element","tagName":"div","properties":{"className":["kg-card","kg-code-card","gatsby-highlight"],"dataLanguage":"json"},"children":[{"type":"element","tagName":"pre","properties":{"className":["language-json"]},"children":[{"type":"element","tagName":"code","properties":{"className":["language-json"]},"children":[{"type":"element","tagName":"span","properties":{"className":["token","property"]},"children":[{"type":"text","value":"\"omnibox\""}]},{"type":"element","tagName":"span","properties":{"className":["token","operator"]},"children":[{"type":"text","value":":"}]},{"type":"text","value":" "},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"{"}]},{"type":"text","value":"\n\t"},{"type":"element","tagName":"span","properties":{"className":["token","property"]},"children":[{"type":"text","value":"\"keyword\""}]},{"type":"element","tagName":"span","properties":{"className":["token","operator"]},"children":[{"type":"text","value":":"}]},{"type":"text","value":" "},{"type":"element","tagName":"span","properties":{"className":["token","string"]},"children":[{"type":"text","value":"\"color\""}]},{"type":"text","value":"\n"},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"}"}]}]}]}]},{"type":"element","tagName":"p","properties":{},"children":[{"type":"text","value":"Next, we'll need to add a background script that will listen to when the user enters the keyword and then handle the user's input. Add the following in your "},{"type":"element","tagName":"code","properties":{"className":["language-text"]},"children":[{"type":"text","value":"manifest.json"}]},{"type":"text","value":" to add a background script:"}]},{"type":"element","tagName":"div","properties":{"className":["kg-card","kg-code-card","gatsby-highlight"],"dataLanguage":"json"},"children":[{"type":"element","tagName":"pre","properties":{"className":["language-json"]},"children":[{"type":"element","tagName":"code","properties":{"className":["language-json"]},"children":[{"type":"element","tagName":"span","properties":{"className":["token","property"]},"children":[{"type":"text","value":"\"background\""}]},{"type":"element","tagName":"span","properties":{"className":["token","operator"]},"children":[{"type":"text","value":":"}]},{"type":"text","value":" "},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"{"}]},{"type":"text","value":"\n    "},{"type":"element","tagName":"span","properties":{"className":["token","property"]},"children":[{"type":"text","value":"\"persistent\""}]},{"type":"element","tagName":"span","properties":{"className":["token","operator"]},"children":[{"type":"text","value":":"}]},{"type":"text","value":" "},{"type":"element","tagName":"span","properties":{"className":["token","boolean"]},"children":[{"type":"text","value":"false"}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":","}]},{"type":"text","value":"\n\t"},{"type":"element","tagName":"span","properties":{"className":["token","property"]},"children":[{"type":"text","value":"\"scripts\""}]},{"type":"element","tagName":"span","properties":{"className":["token","operator"]},"children":[{"type":"text","value":":"}]},{"type":"text","value":" "},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"["}]},{"type":"element","tagName":"span","properties":{"className":["token","string"]},"children":[{"type":"text","value":"\"background.js\""}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"]"}]},{"type":"text","value":"\n"},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"}"}]}]}]}]},{"type":"element","tagName":"p","properties":{},"children":[{"type":"text","value":"For now, create an empty file "},{"type":"element","tagName":"code","properties":{"className":["language-text"]},"children":[{"type":"text","value":"background.js"}]},{"type":"text","value":" in the root of the directory."}]},{"type":"element","tagName":"p","properties":{},"children":[{"type":"text","value":"Then, go to "},{"type":"element","tagName":"code","properties":{"className":["language-text"]},"children":[{"type":"text","value":"chrome://extensions"}]},{"type":"text","value":" again and click on the refresh button for your extension. You should see a new background page registered."}]},{"type":"element","tagName":"figure","properties":{"className":["kg-card","kg-image-card"]},"children":[{"type":"element","tagName":"img","properties":{"src":"https://res-3.cloudinary.com/hbqmf3mbz/image/upload/q_auto/v1/ghost-blog-images/Screen-Shot-2021-08-04-at-2.35.21-PM.png","className":["kg-image"],"alt":"","loading":"lazy","width":848,"height":458},"children":[]}]},{"type":"element","tagName":"p","properties":{},"children":[{"type":"text","value":"Let's test out the keyword we just added. Open a new tab, then enter in the address bar (the Omnibox) "},{"type":"element","tagName":"code","properties":{"className":["language-text"]},"children":[{"type":"text","value":"color"}]},{"type":"text","value":". You should see a suggestion for "},{"type":"element","tagName":"code","properties":{"className":["language-text"]},"children":[{"type":"text","value":"hex-to-rgb"}]},{"type":"text","value":" which is the extension we just created."}]},{"type":"element","tagName":"figure","properties":{"className":["kg-card","kg-image-card"]},"children":[{"type":"element","tagName":"img","properties":{"src":"https://res-4.cloudinary.com/hbqmf3mbz/image/upload/q_auto/v1/ghost-blog-images/Screen-Shot-2021-08-04-at-2.35.48-PM.png","className":["kg-image"],"alt":"","loading":"lazy","width":1818,"height":220},"children":[]}]},{"type":"element","tagName":"p","properties":{},"children":[{"type":"text","value":"If you press TAB, you'll see that the extension's name is now added on the left in the Omnibox with the extension's icon. This means that now the extension will start receiving the input being entered. As we still haven't added listeners to handle the user's input, nothing will happen at the moment if you enter anything."}]},{"type":"element","tagName":"figure","properties":{"className":["kg-card","kg-image-card"]},"children":[{"type":"element","tagName":"img","properties":{"src":"https://res-3.cloudinary.com/hbqmf3mbz/image/upload/q_auto/v1/ghost-blog-images/Screen-Shot-2021-08-04-at-2.35.55-PM.png","className":["kg-image"],"alt":"","loading":"lazy","width":1822,"height":222},"children":[]}]},{"type":"element","tagName":"h2","properties":{"id":"add-a-default-suggestion"},"children":[{"type":"text","value":"Add a Default Suggestion"}]},{"type":"element","tagName":"p","properties":{},"children":[{"type":"text","value":"When using the Omnibox API, you can specify a default suggestion that will appear regardless of what the user enters. To do that, you can use the method "},{"type":"element","tagName":"code","properties":{"className":["language-text"]},"children":[{"type":"text","value":"chrome.omnibox.setDefaultSuggestion"}]},{"type":"text","value":". This method accepts an object with one property "},{"type":"element","tagName":"code","properties":{"className":["language-text"]},"children":[{"type":"text","value":"description"}]},{"type":"text","value":"."}]},{"type":"element","tagName":"p","properties":{},"children":[{"type":"text","value":"The description property accepts XML styling. This means you can use XML tags to apply minimal styling to the suggestion you're showing to the user. These tags are:"}]},{"type":"element","tagName":"ol","properties":{},"children":[{"type":"element","tagName":"li","properties":{},"children":[{"type":"element","tagName":"code","properties":{"className":["language-text"]},"children":[{"type":"text","value":"<url>"}]},{"type":"text","value":": shows the text inside it styled as a link."}]},{"type":"element","tagName":"li","properties":{},"children":[{"type":"element","tagName":"code","properties":{"className":["language-text"]},"children":[{"type":"text","value":"<match>"}]},{"type":"text","value":": shows the text inside it highlighted."}]},{"type":"element","tagName":"li","properties":{},"children":[{"type":"element","tagName":"code","properties":{"className":["language-text"]},"children":[{"type":"text","value":"<dim>"}]},{"type":"text","value":": shows the text inside it dimmed."}]}]},{"type":"element","tagName":"p","properties":{},"children":[{"type":"text","value":"It should be noted that you can also nest the XML tags. For example, you can make a URL also highlighted."}]},{"type":"element","tagName":"p","properties":{},"children":[{"type":"text","value":"At the beginning of our background script, we'll add a default suggestion that will let the user know what they need to enter to get a result. Add the following at the beginning of "},{"type":"element","tagName":"code","properties":{"className":["language-text"]},"children":[{"type":"text","value":"background.js"}]},{"type":"text","value":":"}]},{"type":"element","tagName":"div","properties":{"className":["kg-card","kg-code-card","gatsby-highlight"],"dataLanguage":"js"},"children":[{"type":"element","tagName":"pre","properties":{"className":["language-js"]},"children":[{"type":"element","tagName":"code","properties":{"className":["language-js"]},"children":[{"type":"text","value":"chrome"},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"."}]},{"type":"text","value":"omnibox"},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"."}]},{"type":"element","tagName":"span","properties":{"className":["token","function"]},"children":[{"type":"text","value":"setDefaultSuggestion"}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"("}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"{"}]},{"type":"text","value":"\n    "},{"type":"element","tagName":"span","properties":{"className":["token","literal-property","property"]},"children":[{"type":"text","value":"description"}]},{"type":"element","tagName":"span","properties":{"className":["token","operator"]},"children":[{"type":"text","value":":"}]},{"type":"text","value":" "},{"type":"element","tagName":"span","properties":{"className":["token","string"]},"children":[{"type":"text","value":"'Enter a hex code of 6 characters to convert to RGB (for example, <match>ffffff</match>)'"}]},{"type":"text","value":"\n"},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"}"}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":")"}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":";"}]}]}]}]},{"type":"element","tagName":"p","properties":{},"children":[{"type":"text","value":"Notice that in the "},{"type":"element","tagName":"code","properties":{"className":["language-text"]},"children":[{"type":"text","value":"description"}]},{"type":"text","value":", we've used the XML tag "},{"type":"element","tagName":"code","properties":{"className":["language-text"]},"children":[{"type":"text","value":"<match>ffffff</match>"}]},{"type":"text","value":". This means that the suggestion "},{"type":"element","tagName":"code","properties":{"className":["language-text"]},"children":[{"type":"text","value":"ffffff"}]},{"type":"text","value":" will be highlighted."}]},{"type":"element","tagName":"p","properties":{},"children":[{"type":"text","value":"Let's test it. First, refresh the extension from "},{"type":"element","tagName":"code","properties":{"className":["language-text"]},"children":[{"type":"text","value":"chrome://extensions"}]},{"type":"text","value":" like we did before. Then, type "},{"type":"element","tagName":"code","properties":{"className":["language-text"]},"children":[{"type":"text","value":"color"}]},{"type":"text","value":" in a new tab and hit "},{"type":"element","tagName":"code","properties":{"className":["language-text"]},"children":[{"type":"text","value":"TAB"}]},{"type":"text","value":". Try entering a character and you'll see the suggestion we just defined in "},{"type":"element","tagName":"code","properties":{"className":["language-text"]},"children":[{"type":"text","value":"background.js"}]},{"type":"text","value":". Note that the default suggestion does not appear until at least one character is entered."}]},{"type":"element","tagName":"figure","properties":{"className":["kg-card","kg-image-card"]},"children":[{"type":"element","tagName":"img","properties":{"src":"https://res-5.cloudinary.com/hbqmf3mbz/image/upload/q_auto/v1/ghost-blog-images/Screen-Shot-2021-08-04-at-2.46.53-PM.png","className":["kg-image"],"alt":"","loading":"lazy","width":1812,"height":140},"children":[]}]},{"type":"element","tagName":"h2","properties":{"id":"handle-input"},"children":[{"type":"text","value":"Handle Input"}]},{"type":"element","tagName":"p","properties":{},"children":[{"type":"text","value":"In this section, we'll handle the input the user enters after entering the keyword. To do that, we'll add an event listener to the event "},{"type":"element","tagName":"code","properties":{"className":["language-text"]},"children":[{"type":"text","value":"chrome.omnibox.onInputChanged"}]},{"type":"text","value":". The listener will receive 2 parameters, "},{"type":"element","tagName":"code","properties":{"className":["language-text"]},"children":[{"type":"text","value":"text"}]},{"type":"text","value":" which is the text the user entered, and "},{"type":"element","tagName":"code","properties":{"className":["language-text"]},"children":[{"type":"text","value":"suggest"}]},{"type":"text","value":" which is a callback function we should use to send our suggestions that we want to show the user back to Chrome. This event is triggered every time the user enters a character."}]},{"type":"element","tagName":"p","properties":{},"children":[{"type":"text","value":"Add the following to "},{"type":"element","tagName":"code","properties":{"className":["language-text"]},"children":[{"type":"text","value":"background.js"}]},{"type":"text","value":":"}]},{"type":"element","tagName":"div","properties":{"className":["kg-card","kg-code-card","gatsby-highlight"],"dataLanguage":"js"},"children":[{"type":"element","tagName":"pre","properties":{"className":["language-js"]},"children":[{"type":"element","tagName":"code","properties":{"className":["language-js"]},"children":[{"type":"text","value":"chrome"},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"."}]},{"type":"text","value":"omnibox"},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"."}]},{"type":"text","value":"onInputChanged"},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"."}]},{"type":"element","tagName":"span","properties":{"className":["token","function"]},"children":[{"type":"text","value":"addListener"}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"("}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"("}]},{"type":"element","tagName":"span","properties":{"className":["token","parameter"]},"children":[{"type":"text","value":"text"},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":","}]},{"type":"text","value":" suggest"}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":")"}]},{"type":"text","value":" "},{"type":"element","tagName":"span","properties":{"className":["token","operator"]},"children":[{"type":"text","value":"=>"}]},{"type":"text","value":" "},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"{"}]},{"type":"text","value":"\n    "},{"type":"element","tagName":"span","properties":{"className":["token","comment"]},"children":[{"type":"text","value":"//check that the length of the text is 6 characters"}]},{"type":"text","value":"\n    "},{"type":"element","tagName":"span","properties":{"className":["token","keyword"]},"children":[{"type":"text","value":"if"}]},{"type":"text","value":" "},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"("}]},{"type":"text","value":"text"},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"."}]},{"type":"text","value":"length "},{"type":"element","tagName":"span","properties":{"className":["token","operator"]},"children":[{"type":"text","value":"!=="}]},{"type":"text","value":" "},{"type":"element","tagName":"span","properties":{"className":["token","number"]},"children":[{"type":"text","value":"6"}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":")"}]},{"type":"text","value":" "},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"{"}]},{"type":"text","value":"\n        "},{"type":"element","tagName":"span","properties":{"className":["token","function"]},"children":[{"type":"text","value":"suggest"}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"("}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"["}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"]"}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":")"}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":";"}]},{"type":"text","value":"\n        "},{"type":"element","tagName":"span","properties":{"className":["token","keyword"]},"children":[{"type":"text","value":"return"}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":";"}]},{"type":"text","value":"\n    "},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"}"}]},{"type":"text","value":"\n    \n    "},{"type":"element","tagName":"span","properties":{"className":["token","comment"]},"children":[{"type":"text","value":"//TODO send the text to the API"}]},{"type":"text","value":"\n"},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"}"}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":")"}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":";"}]}]}]}]},{"type":"element","tagName":"p","properties":{},"children":[{"type":"text","value":"In our listener, for now, we're just checking if the text's length is not 6, then we're calling the "},{"type":"element","tagName":"code","properties":{"className":["language-text"]},"children":[{"type":"text","value":"suggest"}]},{"type":"text","value":" callback passing it an empty array."}]},{"type":"element","tagName":"p","properties":{},"children":[{"type":"text","value":"Next, we'll send a request to the COLOURlovers API and retrieve the color's information. But before we do that, we need to add the API's URL in the permissions array in "},{"type":"element","tagName":"code","properties":{"className":["language-text"]},"children":[{"type":"text","value":"manifest.json"}]},{"type":"text","value":" so that requests to the API will be allowed. Add the following to "},{"type":"element","tagName":"code","properties":{"className":["language-text"]},"children":[{"type":"text","value":"manifest.json"}]},{"type":"text","value":":"}]},{"type":"element","tagName":"div","properties":{"className":["kg-card","kg-code-card","gatsby-highlight"],"dataLanguage":"json"},"children":[{"type":"element","tagName":"pre","properties":{"className":["language-json"]},"children":[{"type":"element","tagName":"code","properties":{"className":["language-json"]},"children":[{"type":"element","tagName":"span","properties":{"className":["token","property"]},"children":[{"type":"text","value":"\"permissions\""}]},{"type":"element","tagName":"span","properties":{"className":["token","operator"]},"children":[{"type":"text","value":":"}]},{"type":"text","value":" "},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"["}]},{"type":"text","value":"\n   "},{"type":"element","tagName":"span","properties":{"className":["token","string"]},"children":[{"type":"text","value":"\"http://www.colourlovers.com/*\""}]},{"type":"text","value":"\n "},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"]"}]}]}]}]},{"type":"element","tagName":"p","properties":{},"children":[{"type":"text","value":"Back to our background script, we'll send a request to the endpoint "},{"type":"element","tagName":"a","properties":{"href":"http://www.colourlovers.com/api/color/"},"children":[{"type":"text","value":"http://www.colourlovers.com/api/color/"}]},{"type":"text","value":" using the "},{"type":"element","tagName":"a","properties":{"href":"https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API"},"children":[{"type":"text","value":"Fetch API"}]},{"type":"text","value":". This endpoint accepts the color as a parameter, then returns an array. The array is either empty if the color is not found (for example, if it's an invalid color) or an array that will hold an object with details about the color. For example, try opening the URL "},{"type":"element","tagName":"a","properties":{"href":"http://www.colourlovers.com/api/color/ffffff?format=json"},"children":[{"type":"text","value":"http://www.colourlovers.com/api/color/ffffff?format=json"}]},{"type":"text","value":" in your browser and see how the response will look like."}]},{"type":"element","tagName":"p","properties":{},"children":[{"type":"text","value":"Add the following to your background script inside the listener:"}]},{"type":"element","tagName":"div","properties":{"className":["kg-card","kg-code-card","gatsby-highlight"],"dataLanguage":"js"},"children":[{"type":"element","tagName":"pre","properties":{"className":["language-js"]},"children":[{"type":"element","tagName":"code","properties":{"className":["language-js"]},"children":[{"type":"element","tagName":"span","properties":{"className":["token","comment"]},"children":[{"type":"text","value":"//send text to API"}]},{"type":"text","value":"\n"},{"type":"element","tagName":"span","properties":{"className":["token","function"]},"children":[{"type":"text","value":"fetch"}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"("}]},{"type":"element","tagName":"span","properties":{"className":["token","string"]},"children":[{"type":"text","value":"'http://www.colourlovers.com/api/color/'"}]},{"type":"text","value":" "},{"type":"element","tagName":"span","properties":{"className":["token","operator"]},"children":[{"type":"text","value":"+"}]},{"type":"text","value":" text "},{"type":"element","tagName":"span","properties":{"className":["token","operator"]},"children":[{"type":"text","value":"+"}]},{"type":"text","value":" "},{"type":"element","tagName":"span","properties":{"className":["token","string"]},"children":[{"type":"text","value":"'?format=json'"}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":")"}]},{"type":"text","value":"\n    "},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"."}]},{"type":"element","tagName":"span","properties":{"className":["token","function"]},"children":[{"type":"text","value":"then"}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"("}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"("}]},{"type":"element","tagName":"span","properties":{"className":["token","parameter"]},"children":[{"type":"text","value":"response"}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":")"}]},{"type":"text","value":" "},{"type":"element","tagName":"span","properties":{"className":["token","operator"]},"children":[{"type":"text","value":"=>"}]},{"type":"text","value":" response"},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"."}]},{"type":"element","tagName":"span","properties":{"className":["token","function"]},"children":[{"type":"text","value":"json"}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"("}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":")"}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":")"}]},{"type":"text","value":"\n    "},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"."}]},{"type":"element","tagName":"span","properties":{"className":["token","function"]},"children":[{"type":"text","value":"then"}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"("}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"("}]},{"type":"element","tagName":"span","properties":{"className":["token","parameter"]},"children":[{"type":"text","value":"data"}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":")"}]},{"type":"text","value":" "},{"type":"element","tagName":"span","properties":{"className":["token","operator"]},"children":[{"type":"text","value":"=>"}]},{"type":"text","value":" "},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"{"}]},{"type":"text","value":"\n    \t"},{"type":"element","tagName":"span","properties":{"className":["token","keyword"]},"children":[{"type":"text","value":"if"}]},{"type":"text","value":" "},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"("}]},{"type":"element","tagName":"span","properties":{"className":["token","operator"]},"children":[{"type":"text","value":"!"}]},{"type":"text","value":"data"},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"."}]},{"type":"text","value":"length"},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":")"}]},{"type":"text","value":" "},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"{"}]},{"type":"text","value":"\n            "},{"type":"element","tagName":"span","properties":{"className":["token","comment"]},"children":[{"type":"text","value":"//no color was found"}]},{"type":"text","value":"\n            "},{"type":"element","tagName":"span","properties":{"className":["token","function"]},"children":[{"type":"text","value":"suggest"}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"("}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"["}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"]"}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":")"}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":";"}]},{"type":"text","value":"\n        "},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"}"}]},{"type":"text","value":" "},{"type":"element","tagName":"span","properties":{"className":["token","keyword"]},"children":[{"type":"text","value":"else"}]},{"type":"text","value":" "},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"{"}]},{"type":"text","value":"\n            "},{"type":"element","tagName":"span","properties":{"className":["token","comment"]},"children":[{"type":"text","value":"//TODO send suggestion when color exists"}]},{"type":"text","value":"\n        "},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"}"}]},{"type":"text","value":"\n"},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"}"}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":")"}]}]}]}]},{"type":"element","tagName":"p","properties":{},"children":[{"type":"text","value":"In case no color was found, we're sending back an empty array to the "},{"type":"element","tagName":"code","properties":{"className":["language-text"]},"children":[{"type":"text","value":"suggest"}]},{"type":"text","value":" callback. We'll need to handle the case that color was found next and how the suggestion should be sent."}]},{"type":"element","tagName":"p","properties":{},"children":[{"type":"text","value":"The suggestion object has 3 properties:"}]},{"type":"element","tagName":"ol","properties":{},"children":[{"type":"element","tagName":"li","properties":{},"children":[{"type":"element","tagName":"code","properties":{"className":["language-text"]},"children":[{"type":"text","value":"content"}]},{"type":"text","value":": Holds the text that will be placed inside the Omnibox when the user points at the suggestion."}]},{"type":"element","tagName":"li","properties":{},"children":[{"type":"element","tagName":"code","properties":{"className":["language-text"]},"children":[{"type":"text","value":"deletable"}]},{"type":"text","value":": Whether the user can delete the suggestion from the suggestions history. This property is optional."}]},{"type":"element","tagName":"li","properties":{},"children":[{"type":"element","tagName":"code","properties":{"className":["language-text"]},"children":[{"type":"text","value":"description"}]},{"type":"text","value":": similar to the description property we used in "},{"type":"element","tagName":"code","properties":{"className":["language-text"]},"children":[{"type":"text","value":"setDefaultSuggestion"}]},{"type":"text","value":", this will be the text that the user will see in the list of suggestions. It can be styled using the XML tags just as we mentioned in the previous section."}]}]},{"type":"element","tagName":"p","properties":{},"children":[{"type":"text","value":"When a color is received, we'll set the "},{"type":"element","tagName":"code","properties":{"className":["language-text"]},"children":[{"type":"text","value":"content"}]},{"type":"text","value":" inside the Omnibox to be the RGB value and the URL to the color in case the user wants to see additional information. We'll set the "},{"type":"element","tagName":"code","properties":{"className":["language-text"]},"children":[{"type":"text","value":"description"}]},{"type":"text","value":" to the color's name, the hex code, and the URL to see more information."}]},{"type":"element","tagName":"p","properties":{},"children":[{"type":"text","value":"Add the following code in place of the "},{"type":"element","tagName":"code","properties":{"className":["language-text"]},"children":[{"type":"text","value":"TODO"}]},{"type":"text","value":" we added in the previous code block:"}]},{"type":"element","tagName":"div","properties":{"className":["kg-card","kg-code-card","gatsby-highlight"],"dataLanguage":"js"},"children":[{"type":"element","tagName":"pre","properties":{"className":["language-js"]},"children":[{"type":"element","tagName":"code","properties":{"className":["language-js"]},"children":[{"type":"element","tagName":"span","properties":{"className":["token","function"]},"children":[{"type":"text","value":"suggest"}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"("}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"["}]},{"type":"text","value":"\n    "},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"{"}]},{"type":"text","value":"\n        "},{"type":"element","tagName":"span","properties":{"className":["token","literal-property","property"]},"children":[{"type":"text","value":"content"}]},{"type":"element","tagName":"span","properties":{"className":["token","operator"]},"children":[{"type":"text","value":":"}]},{"type":"text","value":" "},{"type":"element","tagName":"span","properties":{"className":["token","template-string"]},"children":[{"type":"element","tagName":"span","properties":{"className":["token","template-punctuation","string"]},"children":[{"type":"text","value":"`"}]},{"type":"element","tagName":"span","properties":{"className":["token","interpolation"]},"children":[{"type":"element","tagName":"span","properties":{"className":["token","interpolation-punctuation","punctuation"]},"children":[{"type":"text","value":"${"}]},{"type":"text","value":"data"},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"["}]},{"type":"element","tagName":"span","properties":{"className":["token","number"]},"children":[{"type":"text","value":"0"}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"]"}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"."}]},{"type":"text","value":"rgb"},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"."}]},{"type":"text","value":"red"},{"type":"element","tagName":"span","properties":{"className":["token","interpolation-punctuation","punctuation"]},"children":[{"type":"text","value":"}"}]}]},{"type":"element","tagName":"span","properties":{"className":["token","string"]},"children":[{"type":"text","value":", "}]},{"type":"element","tagName":"span","properties":{"className":["token","interpolation"]},"children":[{"type":"element","tagName":"span","properties":{"className":["token","interpolation-punctuation","punctuation"]},"children":[{"type":"text","value":"${"}]},{"type":"text","value":"data"},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"["}]},{"type":"element","tagName":"span","properties":{"className":["token","number"]},"children":[{"type":"text","value":"0"}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"]"}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"."}]},{"type":"text","value":"rgb"},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"."}]},{"type":"text","value":"green"},{"type":"element","tagName":"span","properties":{"className":["token","interpolation-punctuation","punctuation"]},"children":[{"type":"text","value":"}"}]}]},{"type":"element","tagName":"span","properties":{"className":["token","string"]},"children":[{"type":"text","value":", "}]},{"type":"element","tagName":"span","properties":{"className":["token","interpolation"]},"children":[{"type":"element","tagName":"span","properties":{"className":["token","interpolation-punctuation","punctuation"]},"children":[{"type":"text","value":"${"}]},{"type":"text","value":"data"},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"["}]},{"type":"element","tagName":"span","properties":{"className":["token","number"]},"children":[{"type":"text","value":"0"}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"]"}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"."}]},{"type":"text","value":"rgb"},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"."}]},{"type":"text","value":"blue"},{"type":"element","tagName":"span","properties":{"className":["token","interpolation-punctuation","punctuation"]},"children":[{"type":"text","value":"}"}]}]},{"type":"element","tagName":"span","properties":{"className":["token","string"]},"children":[{"type":"text","value":" url: "}]},{"type":"element","tagName":"span","properties":{"className":["token","interpolation"]},"children":[{"type":"element","tagName":"span","properties":{"className":["token","interpolation-punctuation","punctuation"]},"children":[{"type":"text","value":"${"}]},{"type":"text","value":"data"},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"["}]},{"type":"element","tagName":"span","properties":{"className":["token","number"]},"children":[{"type":"text","value":"0"}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"]"}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"."}]},{"type":"text","value":"url"},{"type":"element","tagName":"span","properties":{"className":["token","interpolation-punctuation","punctuation"]},"children":[{"type":"text","value":"}"}]}]},{"type":"element","tagName":"span","properties":{"className":["token","template-punctuation","string"]},"children":[{"type":"text","value":"`"}]}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":","}]},{"type":"text","value":"\n        "},{"type":"element","tagName":"span","properties":{"className":["token","literal-property","property"]},"children":[{"type":"text","value":"deletable"}]},{"type":"element","tagName":"span","properties":{"className":["token","operator"]},"children":[{"type":"text","value":":"}]},{"type":"text","value":" "},{"type":"element","tagName":"span","properties":{"className":["token","boolean"]},"children":[{"type":"text","value":"true"}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":","}]},{"type":"text","value":"\n        "},{"type":"element","tagName":"span","properties":{"className":["token","literal-property","property"]},"children":[{"type":"text","value":"description"}]},{"type":"element","tagName":"span","properties":{"className":["token","operator"]},"children":[{"type":"text","value":":"}]},{"type":"text","value":" "},{"type":"element","tagName":"span","properties":{"className":["token","template-string"]},"children":[{"type":"element","tagName":"span","properties":{"className":["token","template-punctuation","string"]},"children":[{"type":"text","value":"`"}]},{"type":"element","tagName":"span","properties":{"className":["token","string"]},"children":[{"type":"text","value":"Color name: "}]},{"type":"element","tagName":"span","properties":{"className":["token","interpolation"]},"children":[{"type":"element","tagName":"span","properties":{"className":["token","interpolation-punctuation","punctuation"]},"children":[{"type":"text","value":"${"}]},{"type":"text","value":"data"},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"["}]},{"type":"element","tagName":"span","properties":{"className":["token","number"]},"children":[{"type":"text","value":"0"}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"]"}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"."}]},{"type":"text","value":"title"},{"type":"element","tagName":"span","properties":{"className":["token","interpolation-punctuation","punctuation"]},"children":[{"type":"text","value":"}"}]}]},{"type":"element","tagName":"span","properties":{"className":["token","string"]},"children":[{"type":"text","value":", hex: <match>"}]},{"type":"element","tagName":"span","properties":{"className":["token","interpolation"]},"children":[{"type":"element","tagName":"span","properties":{"className":["token","interpolation-punctuation","punctuation"]},"children":[{"type":"text","value":"${"}]},{"type":"text","value":"data"},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"["}]},{"type":"element","tagName":"span","properties":{"className":["token","number"]},"children":[{"type":"text","value":"0"}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"]"}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"."}]},{"type":"text","value":"hex"},{"type":"element","tagName":"span","properties":{"className":["token","interpolation-punctuation","punctuation"]},"children":[{"type":"text","value":"}"}]}]},{"type":"element","tagName":"span","properties":{"className":["token","string"]},"children":[{"type":"text","value":"</match>, more information: <url>"}]},{"type":"element","tagName":"span","properties":{"className":["token","interpolation"]},"children":[{"type":"element","tagName":"span","properties":{"className":["token","interpolation-punctuation","punctuation"]},"children":[{"type":"text","value":"${"}]},{"type":"text","value":"data"},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"["}]},{"type":"element","tagName":"span","properties":{"className":["token","number"]},"children":[{"type":"text","value":"0"}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"]"}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"."}]},{"type":"text","value":"url"},{"type":"element","tagName":"span","properties":{"className":["token","interpolation-punctuation","punctuation"]},"children":[{"type":"text","value":"}"}]}]},{"type":"element","tagName":"span","properties":{"className":["token","string"]},"children":[{"type":"text","value":"</url> "}]},{"type":"element","tagName":"span","properties":{"className":["token","template-punctuation","string"]},"children":[{"type":"text","value":"`"}]}]},{"type":"text","value":"\n    "},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"}"}]},{"type":"text","value":"\n"},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"]"}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":")"}]}]}]}]},{"type":"element","tagName":"p","properties":{},"children":[{"type":"text","value":"Our code to convert the hex to RGB is done. Again, refresh the extension from "},{"type":"element","tagName":"code","properties":{"className":["language-text"]},"children":[{"type":"text","value":"chrome://extensions"}]},{"type":"text","value":", open a new tab, type "},{"type":"element","tagName":"code","properties":{"className":["language-text"]},"children":[{"type":"text","value":"color"}]},{"type":"text","value":" in the Omnibox, and hit TAB. Then, enter a hex color. For example, enter "},{"type":"element","tagName":"code","properties":{"className":["language-text"]},"children":[{"type":"text","value":"f5f5f5"}]},{"type":"text","value":". You should see a new suggestion below. Try pointing at the suggestion using the keyboard up and down arrows, and you'll see that the content of the Omnibox has changed to the text we set in "},{"type":"element","tagName":"code","properties":{"className":["language-text"]},"children":[{"type":"text","value":"content"}]},{"type":"text","value":"."}]},{"type":"element","tagName":"figure","properties":{"className":["kg-card","kg-image-card"]},"children":[{"type":"element","tagName":"img","properties":{"src":"https://res-3.cloudinary.com/hbqmf3mbz/image/upload/q_auto/v1/ghost-blog-images/Screen-Shot-2021-08-04-at-3.42.17-PM.png","className":["kg-image"],"alt":"","loading":"lazy","width":1820,"height":214},"children":[]}]},{"type":"element","tagName":"h2","properties":{"id":"handle-choosing-suggestion"},"children":[{"type":"text","value":"Handle Choosing Suggestion"}]},{"type":"element","tagName":"p","properties":{},"children":[{"type":"text","value":"The last event we need to handle is when the user selects the suggestion either by clicking on it or hitting enter. This event is "},{"type":"element","tagName":"code","properties":{"className":["language-text"]},"children":[{"type":"text","value":"chrome.omnibox.onInputEntered"}]},{"type":"text","value":". The listener to this event receives two parameters: the "},{"type":"element","tagName":"code","properties":{"className":["language-text"]},"children":[{"type":"text","value":"text"}]},{"type":"text","value":" which is the text inside the Omnibox, and "},{"type":"element","tagName":"code","properties":{"className":["language-text"]},"children":[{"type":"text","value":"OnInputEnteredDisposition"}]},{"type":"text","value":", which suggests if the new URL should be opened (if that's the action to be performed by the extension) in the "},{"type":"element","tagName":"code","properties":{"className":["language-text"]},"children":[{"type":"text","value":"currentTab"}]},{"type":"text","value":", "},{"type":"element","tagName":"code","properties":{"className":["language-text"]},"children":[{"type":"text","value":"newForegroundTab"}]},{"type":"text","value":" or "},{"type":"element","tagName":"code","properties":{"className":["language-text"]},"children":[{"type":"text","value":"newBackgroundTab"}]},{"type":"text","value":"."}]},{"type":"element","tagName":"p","properties":{},"children":[{"type":"text","value":"What we'll do is extract the URL from the "},{"type":"element","tagName":"code","properties":{"className":["language-text"]},"children":[{"type":"text","value":"text"}]},{"type":"text","value":", then if "},{"type":"element","tagName":"code","properties":{"className":["language-text"]},"children":[{"type":"text","value":"OnInputEnteredDisposition"}]},{"type":"text","value":" is "},{"type":"element","tagName":"code","properties":{"className":["language-text"]},"children":[{"type":"text","value":"currentTab"}]},{"type":"text","value":", we'll replace the URL of the current tab with the URL extracted. Else, we'll open a new tab with the URL."}]},{"type":"element","tagName":"p","properties":{},"children":[{"type":"text","value":"In "},{"type":"element","tagName":"code","properties":{"className":["language-text"]},"children":[{"type":"text","value":"background.js"}]},{"type":"text","value":" add the following:"}]},{"type":"element","tagName":"div","properties":{"className":["kg-card","kg-code-card","gatsby-highlight"],"dataLanguage":"js"},"children":[{"type":"element","tagName":"pre","properties":{"className":["language-js"]},"children":[{"type":"element","tagName":"code","properties":{"className":["language-js"]},"children":[{"type":"text","value":"chrome"},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"."}]},{"type":"text","value":"omnibox"},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"."}]},{"type":"text","value":"onInputEntered"},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"."}]},{"type":"element","tagName":"span","properties":{"className":["token","function"]},"children":[{"type":"text","value":"addListener"}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"("}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"("}]},{"type":"element","tagName":"span","properties":{"className":["token","parameter"]},"children":[{"type":"text","value":"text"},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":","}]},{"type":"text","value":" OnInputEnteredDisposition"}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":")"}]},{"type":"text","value":" "},{"type":"element","tagName":"span","properties":{"className":["token","operator"]},"children":[{"type":"text","value":"=>"}]},{"type":"text","value":" "},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"{"}]},{"type":"text","value":"\n    "},{"type":"element","tagName":"span","properties":{"className":["token","keyword"]},"children":[{"type":"text","value":"const"}]},{"type":"text","value":" prefixIndex "},{"type":"element","tagName":"span","properties":{"className":["token","operator"]},"children":[{"type":"text","value":"="}]},{"type":"text","value":" text"},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"."}]},{"type":"element","tagName":"span","properties":{"className":["token","function"]},"children":[{"type":"text","value":"indexOf"}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"("}]},{"type":"element","tagName":"span","properties":{"className":["token","string"]},"children":[{"type":"text","value":"'url: '"}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":")"}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":";"}]},{"type":"text","value":"\n    "},{"type":"element","tagName":"span","properties":{"className":["token","keyword"]},"children":[{"type":"text","value":"if"}]},{"type":"text","value":" "},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"("}]},{"type":"text","value":"prefixIndex "},{"type":"element","tagName":"span","properties":{"className":["token","operator"]},"children":[{"type":"text","value":"!=="}]},{"type":"text","value":" "},{"type":"element","tagName":"span","properties":{"className":["token","operator"]},"children":[{"type":"text","value":"-"}]},{"type":"element","tagName":"span","properties":{"className":["token","number"]},"children":[{"type":"text","value":"1"}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":")"}]},{"type":"text","value":" "},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"{"}]},{"type":"text","value":"\n        "},{"type":"element","tagName":"span","properties":{"className":["token","keyword"]},"children":[{"type":"text","value":"const"}]},{"type":"text","value":" url "},{"type":"element","tagName":"span","properties":{"className":["token","operator"]},"children":[{"type":"text","value":"="}]},{"type":"text","value":" text"},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"."}]},{"type":"element","tagName":"span","properties":{"className":["token","function"]},"children":[{"type":"text","value":"substring"}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"("}]},{"type":"text","value":"prefixIndex "},{"type":"element","tagName":"span","properties":{"className":["token","operator"]},"children":[{"type":"text","value":"+"}]},{"type":"text","value":" "},{"type":"element","tagName":"span","properties":{"className":["token","string"]},"children":[{"type":"text","value":"'url: '"}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"."}]},{"type":"text","value":"length"},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":")"}]},{"type":"text","value":"\n        console"},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"."}]},{"type":"element","tagName":"span","properties":{"className":["token","function"]},"children":[{"type":"text","value":"log"}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"("}]},{"type":"text","value":"url"},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":")"}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":";"}]},{"type":"text","value":"\n        "},{"type":"element","tagName":"span","properties":{"className":["token","keyword"]},"children":[{"type":"text","value":"if"}]},{"type":"text","value":" "},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"("}]},{"type":"text","value":"OnInputEnteredDisposition "},{"type":"element","tagName":"span","properties":{"className":["token","operator"]},"children":[{"type":"text","value":"==="}]},{"type":"text","value":" "},{"type":"element","tagName":"span","properties":{"className":["token","string"]},"children":[{"type":"text","value":"'currentTab'"}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":")"}]},{"type":"text","value":" "},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"{"}]},{"type":"text","value":"\n            chrome"},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"."}]},{"type":"text","value":"tabs"},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"."}]},{"type":"element","tagName":"span","properties":{"className":["token","function"]},"children":[{"type":"text","value":"create"}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"("}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"{"}]},{"type":"text","value":"url"},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"}"}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":")"}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":";"}]},{"type":"text","value":"\n        "},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"}"}]},{"type":"text","value":" "},{"type":"element","tagName":"span","properties":{"className":["token","keyword"]},"children":[{"type":"text","value":"else"}]},{"type":"text","value":" "},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"{"}]},{"type":"text","value":"\n            chrome"},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"."}]},{"type":"text","value":"tabs"},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"."}]},{"type":"element","tagName":"span","properties":{"className":["token","function"]},"children":[{"type":"text","value":"update"}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"("}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"{"}]},{"type":"text","value":"url"},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"}"}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":")"}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":";"}]},{"type":"text","value":"\n        "},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"}"}]},{"type":"text","value":"\n    "},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"}"}]},{"type":"text","value":"\n"},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"}"}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":")"}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":";"}]}]}]}]},{"type":"element","tagName":"p","properties":{},"children":[{"type":"text","value":"As explained earlier, we're extracting the "},{"type":"element","tagName":"code","properties":{"className":["language-text"]},"children":[{"type":"text","value":"URL"}]},{"type":"text","value":" from "},{"type":"element","tagName":"code","properties":{"className":["language-text"]},"children":[{"type":"text","value":"text"}]},{"type":"text","value":" based on the format we wrote in "},{"type":"element","tagName":"code","properties":{"className":["language-text"]},"children":[{"type":"text","value":"content"}]},{"type":"text","value":". Then, we're opening the URL based on the value of "},{"type":"element","tagName":"code","properties":{"className":["language-text"]},"children":[{"type":"text","value":"OnInputEnteredDisposition"}]},{"type":"text","value":"."}]},{"type":"element","tagName":"p","properties":{},"children":[{"type":"text","value":"Let's test it out. Again, refresh the extension from "},{"type":"element","tagName":"code","properties":{"className":["language-text"]},"children":[{"type":"text","value":"chrome://extensions"}]},{"type":"text","value":", open a new tab, type "},{"type":"element","tagName":"code","properties":{"className":["language-text"]},"children":[{"type":"text","value":"color"}]},{"type":"text","value":" in the Omnibox, and hit TAB. Then, enter a hex color. For example, enter "},{"type":"element","tagName":"code","properties":{"className":["language-text"]},"children":[{"type":"text","value":"f5f5f5"}]},{"type":"text","value":". Click on the suggestion shown for the color, or point at it with the up and down arrows on your keyboard and hit enter. You'll be taken to the page in COLOURlovers with the information about the color."}]},{"type":"element","tagName":"figure","properties":{"className":["kg-card","kg-image-card"]},"children":[{"type":"element","tagName":"img","properties":{"src":"https://res-5.cloudinary.com/hbqmf3mbz/image/upload/q_auto/v1/ghost-blog-images/Screen-Shot-2021-08-04-at-3.49.37-PM.png","className":["kg-image"],"alt":"","loading":"lazy","width":2880,"height":1464},"children":[]}]},{"type":"element","tagName":"h2","properties":{"id":"conclusion"},"children":[{"type":"text","value":"Conclusion"}]},{"type":"element","tagName":"p","properties":{},"children":[{"type":"text","value":"Using Chrome's Omnibox API, you can add functionalities to your extension to make it easier for your user to find something or perform certain tasks. Make sure to check out the full "},{"type":"element","tagName":"a","properties":{"href":"https://developer.chrome.com/docs/extensions/reference/omnibox/#type-DescriptionStyleType"},"children":[{"type":"text","value":"API reference"}]},{"type":"text","value":" to know more about what you can do with this API."}]}],"data":{"quirksMode":false}},"tableOfContents":[{"id":"create-the-extension","heading":"Create the Extension"},{"id":"register-omnibox-keyword","heading":"Register Omnibox Keyword"},{"id":"add-a-default-suggestion","heading":"Add a Default Suggestion"},{"id":"handle-input","heading":"Handle Input"},{"id":"handle-choosing-suggestion","heading":"Handle Choosing Suggestion"},{"id":"conclusion","heading":"Conclusion"}]},"featureImageSharp":{"base":"Register-a-Keyword-in-Chrome-s-Omnibox-2.png","publicURL":"/static/89f51a737e7d3c44ffdd7183076e42a1/Register-a-Keyword-in-Chrome-s-Omnibox-2.png","imageMeta":{"width":1080,"height":1080},"childImageSharp":{"fluid":{"base64":"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAUCAIAAAAC64paAAAACXBIWXMAAAsTAAALEwEAmpwYAAAEUElEQVQ4yz3PaVNTZxTA8fvK6bRv2mkngw1LUgpVKxUVVExFweKCJMhi2LMvQoAk9yZ33+/NTaBFQAgFikWmUotE0AyodFrtKLUtWEiFJEAVdfoB6kfoBGKf+c95d35zHoDlGZZnOIFlBS6ZyHEix4s8L/KiJPgCvu2kgCR1+Lfzd/j9AT9AMyTNkgxHJ+NphmcYnmEFhhXYpCKJvCSKflEM+MSkJUkBCaAogqIJiiEphqRpImExJMNSW9DWvsDyQoLgfDzvF4WtxICPl3gAJzGcxAgKIyicoDCSxhPWFpckODrxL5HjfBznSxCCXyRZqs3hBnASfRNGvFG2CZomWZZmOYYXeYxEKY5OLAuc4OPcHtRqbAEwHE5EwBiBJBUqMQkKw0nUBbU0O8wNTRfsLRaCISiOYnkGJXG93m412AEE9SAIhKIeFPOiCQX5P5LCHU5LQdGeDz9+52x5cTvYWl2r8cCgwWgG21Af3QnAMOTxuj1etxcGYQSCEQjZnqgHxjwMSx8/fWDHe0DWgfcVOW/vzpdVaTUVmkoCERCQBiDI6YXB5PE4jONIIiIZy9FGa51i71u7Du34NF9W06jJyd2tUqlITBwOjgNSQHrx8sXq6kosFt3YWF9bi6+vr8Vi0fWN9Xg8tp54a78tPHj069xqNPL639fxeHzpyeKrV//0dQcBCnauxVYezT9cWPgjElmORJaXl5dWnv61Fo+tPo0sLf4e+XNhc/PF5ubL58+fR6PRv589W47HFqPRCqsduFCSpzlxUFupqamp1mqrrBa9G3I63e1O0NnWbLBUFZuqSwb6esLhGZbjlEpF5kcZ2YWFu8rPZ5eVA7baspO5iuLP80pOHD118lib4QLtNhPtRgayY831pvKChnOqgb7e6xMThw/ny+U7lRlypTIt/VD+J4f3AwEM0hzJzs1O3b83s6KkoAO0dEHmr0BzkAa/hMzmM581nD449u03kAdMSZFlZSqUCnlGqiwzU7knZz9wcyqMtttLiwvKSk8RoGM02N3dIV7u6hwZHurtFB31amOtJtjfr9ao0+UpCrksW5mapUzNSJPvO5APDF4ZvTf/5PrtufFb98Zv/Th595fBsR9avZTuosvYAupsrerKmka9keT9Ti9+rLBQkbYzQy6TyT5w2EyAuqRIkAKPV18+XH52fzH282JsPrLx0+OlnsGrCCWirN/e7mkyWZtdsNQ9MHB1wkOJquPH9+Xsnr5xExgbvaZVn7HqG3uDQ5cHr3T1f93R3dt5qUfq6EQwzAvDTpfT5XYbDIa6ujqbzWaxWPQ6HY3CM+EwMB0KhyanW5q0FUVH6spO1pwtqj9X3KT5wlRV2tJQ6TLWuk31oLkBsevsdRVHc/eky95V5eVSnNRkMAJ3wndmb8/Mzd6dmQqFvhsdHw6OD/WPBS9dGxm8deP6zNTk7dDE7HRo/sH970dHLuq0laVF5adOOMx62tv+H2sPOHgKihNyAAAAAElFTkSuQmCC","aspectRatio":1,"src":"/static/89f51a737e7d3c44ffdd7183076e42a1/60290/Register-a-Keyword-in-Chrome-s-Omnibox-2.png","srcSet":"/static/89f51a737e7d3c44ffdd7183076e42a1/847ef/Register-a-Keyword-in-Chrome-s-Omnibox-2.png 175w,\n/static/89f51a737e7d3c44ffdd7183076e42a1/91cba/Register-a-Keyword-in-Chrome-s-Omnibox-2.png 350w,\n/static/89f51a737e7d3c44ffdd7183076e42a1/60290/Register-a-Keyword-in-Chrome-s-Omnibox-2.png 700w,\n/static/89f51a737e7d3c44ffdd7183076e42a1/f5f50/Register-a-Keyword-in-Chrome-s-Omnibox-2.png 1050w,\n/static/89f51a737e7d3c44ffdd7183076e42a1/14010/Register-a-Keyword-in-Chrome-s-Omnibox-2.png 1080w","srcWebp":"/static/89f51a737e7d3c44ffdd7183076e42a1/89afa/Register-a-Keyword-in-Chrome-s-Omnibox-2.webp","srcSetWebp":"/static/89f51a737e7d3c44ffdd7183076e42a1/9fca7/Register-a-Keyword-in-Chrome-s-Omnibox-2.webp 175w,\n/static/89f51a737e7d3c44ffdd7183076e42a1/37a4e/Register-a-Keyword-in-Chrome-s-Omnibox-2.webp 350w,\n/static/89f51a737e7d3c44ffdd7183076e42a1/89afa/Register-a-Keyword-in-Chrome-s-Omnibox-2.webp 700w,\n/static/89f51a737e7d3c44ffdd7183076e42a1/78e7a/Register-a-Keyword-in-Chrome-s-Omnibox-2.webp 1050w,\n/static/89f51a737e7d3c44ffdd7183076e42a1/788b4/Register-a-Keyword-in-Chrome-s-Omnibox-2.webp 1080w","sizes":"(max-width: 700px) 100vw, 700px"}}}}},{"node":{"id":"Ghost__Post__6127ba1b3ed159214d382eb1","title":"How to Add Keyboard Shortcuts In a Chrome Extension","slug":"how-to-add-keyboard-shortcuts-in-a-chrome-extension","featured":false,"feature_image":"https://res-4.cloudinary.com/hbqmf3mbz/image/upload/q_auto/v1/ghost-blog-images/clay-banks-PXaQXThG1FY-unsplash--2---1-.jpg","excerpt":"In this tutorial, we'll go over how to add keyboard shortcuts in a Chrome extension Manifest versions 3 and 2.","custom_excerpt":"In this tutorial, we'll go over how to add keyboard shortcuts in a Chrome extension Manifest versions 3 and 2.","visibility":"public","created_at_pretty":"7 Jul 2021","published_at_pretty":"8 Jul 2021","updated_at_pretty":"26 Aug 2021","created_at":"2021-07-07T17:25:36.000+00:00","published_at":"2021-07-08T18:04:19.000+00:00","updated_at":"2021-08-26T17:35:45.000+00:00","meta_title":null,"meta_description":null,"og_description":null,"og_image":null,"og_title":null,"twitter_description":null,"twitter_image":null,"twitter_title":null,"authors":[{"slug":"shahed","url":"https://backend.shahednasser.com/author/shahed/","name":"Shahed Nasser","bio":null,"cover_image":null,"profile_image":"https://backend.shahednasser.com/content/images/2022/03/IMG_0591.jpg","location":null,"website":null,"twitter":null,"facebook":null,"meta_title":null,"meta_description":null,"coverImageSharp":null,"profileImageSharp":null}],"primary_author":{"slug":"shahed","url":"https://backend.shahednasser.com/author/shahed/","name":"Shahed Nasser","bio":null,"cover_image":null,"profile_image":"https://backend.shahednasser.com/content/images/2022/03/IMG_0591.jpg","location":null,"website":null,"twitter":null,"facebook":null,"meta_title":null,"meta_description":null,"coverImageSharp":null,"profileImageSharp":{"base":"IMG_0591.jpg","publicURL":"/static/ceb49c3c631485453e71e00d7f84b069/IMG_0591.jpg","imageMeta":{"width":1182,"height":1179},"childImageSharp":{"fluid":{"base64":"data:image/jpeg;base64,/9j/2wBDABALDA4MChAODQ4SERATGCgaGBYWGDEjJR0oOjM9PDkzODdASFxOQERXRTc4UG1RV19iZ2hnPk1xeXBkeFxlZ2P/2wBDARESEhgVGC8aGi9jQjhCY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2P/wgARCAAUABQDASIAAhEBAxEB/8QAGAABAQEBAQAAAAAAAAAAAAAAAAMEAQL/xAAWAQEBAQAAAAAAAAAAAAAAAAADBAL/2gAMAwEAAhADEAAAAdXiFM6i0CohUWXoKn//xAAcEAACAgIDAAAAAAAAAAAAAAACAwESBBEhM0H/2gAIAQEAAQUCWySE3WEr7SzbXjAj4iKty+sOQ//EABYRAQEBAAAAAAAAAAAAAAAAAAERIP/aAAgBAwEBPwEhj//EABQRAQAAAAAAAAAAAAAAAAAAACD/2gAIAQIBAT8BH//EAB4QAAIBBAMBAAAAAAAAAAAAAAABIRESMUECECJx/9oACAEBAAY/ApVGWvOjzgtUwLlTZA0sdL4f/8QAHBAAAwACAwEAAAAAAAAAAAAAAAERITFBkbHB/9oACAEBAAE/IahkCy+N2GwZpjQiJHJCspUFY0QrSi+HqiW2rgf/2gAMAwEAAgADAAAAEPw3/wD/xAAYEQEBAAMAAAAAAAAAAAAAAAAAARExQf/aAAgBAwEBPxCtjDqP/8QAFxEBAQEBAAAAAAAAAAAAAAAAAQAxEf/aAAgBAgEBPxBFus6Tt//EAB8QAQEAAgIBBQAAAAAAAAAAAAERACExQWFRcYGR0f/aAAgBAQABPxAuaBPPzkO1wyX7F4wkwXanfZrFQgeqE9JgS14vVOvrERIJomVBKwt2jebAeP0yVa8h1n//2Q==","aspectRatio":1,"src":"/static/ceb49c3c631485453e71e00d7f84b069/31709/IMG_0591.jpg","srcSet":"/static/ceb49c3c631485453e71e00d7f84b069/f340b/IMG_0591.jpg 28w,\n/static/ceb49c3c631485453e71e00d7f84b069/22d64/IMG_0591.jpg 55w,\n/static/ceb49c3c631485453e71e00d7f84b069/31709/IMG_0591.jpg 110w,\n/static/ceb49c3c631485453e71e00d7f84b069/aa249/IMG_0591.jpg 165w,\n/static/ceb49c3c631485453e71e00d7f84b069/0dc33/IMG_0591.jpg 220w,\n/static/ceb49c3c631485453e71e00d7f84b069/d8257/IMG_0591.jpg 1182w","srcWebp":"/static/ceb49c3c631485453e71e00d7f84b069/8678c/IMG_0591.webp","srcSetWebp":"/static/ceb49c3c631485453e71e00d7f84b069/59cda/IMG_0591.webp 28w,\n/static/ceb49c3c631485453e71e00d7f84b069/7da75/IMG_0591.webp 55w,\n/static/ceb49c3c631485453e71e00d7f84b069/8678c/IMG_0591.webp 110w,\n/static/ceb49c3c631485453e71e00d7f84b069/f282e/IMG_0591.webp 165w,\n/static/ceb49c3c631485453e71e00d7f84b069/a7b21/IMG_0591.webp 220w,\n/static/ceb49c3c631485453e71e00d7f84b069/63099/IMG_0591.webp 1182w","sizes":"(max-width: 110px) 100vw, 110px"}}}},"primary_tag":{"slug":"browser-extensions","url":"https://backend.shahednasser.com/tag/browser-extensions/","name":"Browser Extensions","visibility":"public","feature_image":"https://backend.shahednasser.com/content/images/2022/01/photo-1616499370260-485b3e5ed653-2.jpeg","description":"Learn more about Browser Extensions through tutorials, articles, and tips.","meta_title":null,"meta_description":null,"featureImageSharp":{"base":"photo-1616499370260-485b3e5ed653-2.jpeg","publicURL":"/static/17d3062927434b0d9d22f3e45ece68b8/photo-1616499370260-485b3e5ed653-2.jpeg","imageMeta":{"width":2000,"height":1334},"childImageSharp":{"fluid":{"base64":"data:image/jpeg;base64,/9j/2wBDABALDA4MChAODQ4SERATGCgaGBYWGDEjJR0oOjM9PDkzODdASFxOQERXRTc4UG1RV19iZ2hnPk1xeXBkeFxlZ2P/2wBDARESEhgVGC8aGi9jQjhCY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2P/wgARCAANABQDASIAAhEBAxEB/8QAGAAAAgMAAAAAAAAAAAAAAAAAAAMCBAX/xAAVAQEBAAAAAAAAAAAAAAAAAAAAAv/aAAwDAQACEAMQAAABQ+hsRUBAf//EABoQAQADAAMAAAAAAAAAAAAAAAIAAQMEEyL/2gAIAQEAAQUCKpA3nefSo/JALzvlaXP/xAAVEQEBAAAAAAAAAAAAAAAAAAAQEf/aAAgBAwEBPwGH/8QAFREBAQAAAAAAAAAAAAAAAAAAERD/2gAIAQIBAT8BSf/EAB4QAAEDBAMAAAAAAAAAAAAAAAEAAiEDEBEiMVFh/9oACAEBAAY/Ai1x9CG2/VmEcoVCJwogL//EABoQAQEBAQADAAAAAAAAAAAAAAERACFBcYH/2gAIAQEAAT8h6pD2YW6ifbqrwMDk3W6VFUxyFZ4ib//aAAwDAQACAAMAAAAQLB//xAAXEQEAAwAAAAAAAAAAAAAAAAAAAREh/9oACAEDAQE/EJs1/8QAFREBAQAAAAAAAAAAAAAAAAAAABH/2gAIAQIBAT8QFf/EABwQAQEAAwEAAwAAAAAAAAAAAAERACExQVFhgf/aAAgBAQABPxCsM1ex6ZRBIol9InPbigkXRbrDW2E9042KoTWHL8fWeOKEM/Xuf//Z","aspectRatio":1.5028901734104045,"src":"/static/17d3062927434b0d9d22f3e45ece68b8/d5c54/photo-1616499370260-485b3e5ed653-2.jpg","srcSet":"/static/17d3062927434b0d9d22f3e45ece68b8/65d8c/photo-1616499370260-485b3e5ed653-2.jpg 260w,\n/static/17d3062927434b0d9d22f3e45ece68b8/c5f21/photo-1616499370260-485b3e5ed653-2.jpg 520w,\n/static/17d3062927434b0d9d22f3e45ece68b8/d5c54/photo-1616499370260-485b3e5ed653-2.jpg 1040w,\n/static/17d3062927434b0d9d22f3e45ece68b8/81a53/photo-1616499370260-485b3e5ed653-2.jpg 1560w,\n/static/17d3062927434b0d9d22f3e45ece68b8/4e5f3/photo-1616499370260-485b3e5ed653-2.jpg 2000w","srcWebp":"/static/17d3062927434b0d9d22f3e45ece68b8/e4875/photo-1616499370260-485b3e5ed653-2.webp","srcSetWebp":"/static/17d3062927434b0d9d22f3e45ece68b8/dc8f3/photo-1616499370260-485b3e5ed653-2.webp 260w,\n/static/17d3062927434b0d9d22f3e45ece68b8/2db4b/photo-1616499370260-485b3e5ed653-2.webp 520w,\n/static/17d3062927434b0d9d22f3e45ece68b8/e4875/photo-1616499370260-485b3e5ed653-2.webp 1040w,\n/static/17d3062927434b0d9d22f3e45ece68b8/f5845/photo-1616499370260-485b3e5ed653-2.webp 1560w,\n/static/17d3062927434b0d9d22f3e45ece68b8/49d6b/photo-1616499370260-485b3e5ed653-2.webp 2000w","sizes":"(max-width: 1040px) 100vw, 1040px"}}}},"tags":[{"slug":"browser-extensions","url":"https://backend.shahednasser.com/tag/browser-extensions/","name":"Browser Extensions","visibility":"public","feature_image":"https://backend.shahednasser.com/content/images/2022/01/photo-1616499370260-485b3e5ed653-2.jpeg","description":"Learn more about Browser Extensions through tutorials, articles, and tips.","meta_title":null,"meta_description":null,"featureImageSharp":null},{"slug":"js","url":"https://backend.shahednasser.com/tag/js/","name":"Javascript","visibility":"public","feature_image":"https://backend.shahednasser.com/content/images/2022/01/photo-1592609931095-54a2168ae893-2.jpeg","description":"Learn more about Javascript through tutorials, articles, and tips.","meta_title":null,"meta_description":"Learn more about Javascript through tutorials, articles and tips.","featureImageSharp":null}],"plaintext":"Chrome Extensions have a lot of features and functionalities that help\ndevelopers create extensions and tools with great user experience.\n\nIn this tutorial, we'll go over how to add keyboard shortcuts in a Chrome\nextension. This tutorial will cover how it can be done for both Manifest\nVersions 2 (MV2) and 3 (MV3). \n\nSuggested Read: Chrome Extension Tutorial: Migrating to Manifest V3 from V2\n[https://blog.shahednasser.com/chrome-extension-tutorial-migrating-to-manifest-v3-from-v2]\n\nYou can find the full code for this tutorial on this GitHub repository\n[https://github.com/shahednasser/chrome-commands-tutorial].\n\n\n--------------------------------------------------------------------------------\n\nCreating The Extension\nIn case you already have an extension ready, you can skip this step. We'll\nquickly go over how to create a Chrome extension.\n\nSuggested Read: Learn how to create a Chrome extension in the tutorial Chrome\nExtension Tutorial — Replace Images in Any Website with Pikachu\n[https://blog.shahednasser.com/chrome-extension-tutorial-replace-images-in-any-website-with-pikachu]\n.\n\nTo create a Chrome extension, first, create a directory that will hold all the\nextension files in it:\n\nmkdir new-extension\ncd new-extension\n\nThen, create a manifest.json file with the following content:\n\n{\n    \"name\": \"Commands\",\n    \"version\": \"0.0.1\",\n    \"description\": \"Assign Keyboard Commands\",\n    \"manifest_version\": 3,\n    \"icons\": {\n        \"16\": \"/assets/icon-16.png\",\n        \"32\": \"/assets/icon-32.png\",\n        \"48\": \"/assets/icon-48.png\",\n        \"128\": \"/assets/icon-128.png\"\n    },\n}\n\nIf you're creating a Manifest V2 extension, make sure to change the manifest\nversion:\n\n\"manifest_version\": 2,\n\nAlso, make sure to download the icons from the GitHub repository first, or add\nyour own icons.\n\nThen, go to chrome://extensions/ on your Chrome browser, enable Developer Mode\nif you haven't from the top right, click on \"Load unpacked\" and choose the\nfolder that you created for your extension. Once done, the extension will be\nadded to your Chrome browser.\n\n\n--------------------------------------------------------------------------------\n\nWhat Are Chrome Commands\nChrome commands allow an extension to register commands, or keyboard shortcuts,\nto provide ease of use for the users of that extension to perform certain\nactions through those shortcuts. An extension can have at most 4 commands.\n\nThere are two kinds of commands. A standard command and an action command. A\nstandard command runs in the background script or service worker and has no\naccess to the current page or tab opened. Standard commands trigger the event \nchrome.commands.onCommand, and you can listen to it in your background script or\nservice worker.\n\nAction commands map the shortcuts to your extension's action (if MV3) or browser\naction or page action (if MV2). Action commands can have access to the current\ntab and can execute a script on the current page. Action commands do not trigger\nthe event chrome.commands.onCommand. Instead, in MV3 they trigger \nchrome.action.onClicked , and in MV2 they trigger chrome.browserAction.onClicked \nor chrome.pageAction.onClicked.\n\nCommands are defined in an extension's manifest.json under the commands key.\nStandard commands are defined in the following format:\n\n\"commands\": {\n\t\"firstCommand\": {\n    \t\"suggested_key\": \"CTRL+F\",\n        \"description\": \"My first command\"\n    }\n}\n\nWhere suggested_key can be an object or a string (we'll get to that in a bit)\nand description is required.\n\nAction commands are defined in the same way but under the _execute_action key in\nMV3, or _execute_browser_action or _execute_page_action keys in MV2:\n\n\"commands\": {\n\t\"_execute_action\": {\n    \t\"suggested_key\": \"CTRL+F\",\n        \"description\": \"My first command\"\n    }\n}\n\ndescription for action commands is optional.\n\nSuggested Key\nSome of the keys that are allowed to be used are: Ctrl, Alt, Shift, 0...9, and \nA...Z. For a full list of keys, you can see them in the chrome.commands\nreference\n[https://developer.chrome.com/docs/extensions/reference/commands/#supported-keys]\n.\n\nsuggested_key can either be a string or an object. If a string, then the same\nshortcut will be applied for all Operating Systems. In this case, certain keys\nwill be replaced with their equivalent on the Operating Systems. This means that \nCtrl on Mac OS will be substituted by Command.\n\nTo specify the shortcut for an operating system, you should pass an object where\nthe key is the operating system's name. The allowed keys are default which\napplies the shortcut to all operating systems not listed in the object, chromeos\n, linux, mac, and windows.\n\nIf you want to use Ctrl on Mac instead of Command, you can use MacCtrl for the\nvalue of mac in the object:\n\n\"suggested_key\": {\n\t\"default\": \"Ctrl+F\",\n    \"mac\": \"MacCtrl+F\"\n}\n\nThis will make the shortcut Ctrl+F for all operating systems, but for Mac\ninstead of the conventional Command substitute, it will use the Ctrl key on Mac.\n\nIt should be noted that the value for the keyboard shortcuts is case-sensitive.\nIf you set the value to CTRL+F instead of Ctrl+F, the shortcut will not be\nregistered.\n\nGlobal Shortcuts\nBy default, keyboard shortcuts are executed only when the Chrome browser is in\nfocus. To allow shortcuts to work even when Chrome is in the background, you can\nmake the command \"global\" by adding the global key in the definition:\n\n\"commands\": {\n\t\"first\": {\n    \t\"suggested_key\": \"Ctrl+Shift+F\",\n        \"description\": \"First command\",\n        \"global\": true\n    }\n}\n\nGlobal shortcuts are very limited in the amount of keys you can use in \nsuggested_key to make sure your shortcut does not overlap with a system\nshortcut. For that reason, the format should be Ctrl+Shift+[0...9].\n\nIf the shortcut is already defined and you later set it to global, upon testing\nit I noticed that the shortcut will not be updated. To update it, you will need\nto remove and reinstall the extension.\n\n\n--------------------------------------------------------------------------------\n\nAdding an Action Command\nWe'll first add an action command. The action command will toggle \"Dark Mode\" on\nthe current website. We'll do it in a very basic way, which is just changing the\nbackground color to black and text color to white, as this is not the purpose of\nthis tutorial.\n\nDefine The Command\n\nLet's first define the action command. It will be triggered at the press of \nCtrl+Shift+L. \n\nMV3\n\nFor MV3, the definition will be:\n\n\"commands\": {\n\t\"_execute_action\": {\n\t\t\"suggested_key\": \"Ctrl+Shift+L\",\n\t\t\"description\": \"Toggle dark body\"\n\t},\n}\n\nAs we are adding an Action command, we need to also add an action key:\n\n\"action\": {},\n\nMV2\n\nFor MV2 we just need to change _execute_action to _execute_browser_action:\n\n\"commands\": {\n\t\"_execute_browser_action\": {\n            \"suggested_key\": \"Ctrl+Shift+L\",\n            \"description\": \"Toggle dark body\"\n\t},\n}\n\nand add a browser_action key:\n\n\"browser_action\": {},\n\nThis will define our command that toggles dark mode at the press of Ctrl+Shift+L\n. Make sure that the suggested_key value is correct as it is case-sensitive. If\nit's incorrect, the shortcut will not be registered.\n\nAdd Event Listeners\n\nNext, we need to listen to when these keys are pressed. Inside the listener, we\nneed to execute a Javascript code that will just toggle the background color and\ntext color of the document's body.\n\nMV3\n\nTo do that, first, add a service worker:\n\n\"background\": {\n   \"service_worker\": \"background.js\"\n},\n\nThen, in the service worker or background script, we'll add a listener to handle\nthe press of the keys. In the service worker, we'll add a listener to \nchrome.action.onClicked event:\n\nchrome.action.onClicked.addListener((tab) => {\n    //TODO toggle dark mode in the tab\n});\n\nInside the listener, we'll use chrome.scripting.executeScript\n[https://developer.chrome.com/docs/extensions/reference/scripting/#method-executeScript]\n. executeScript accepts an object of options including tabId for the id of the\ntab to execute the script on and function for the Javascript function to\nexecute. Add in the service worker the following function:\n\nfunction toggleDark () {\n    if (!document.body.getAttribute('data-ext-dark')) {\n        document.body.setAttribute('data-ext-dark', true);\n        document.body.style.backgroundColor = '#000';\n        document.body.style.color = '#fff';\n    } else {\n        document.body.setAttribute('data-ext-dark', false);\n        document.body.style.backgroundColor = '#fff';\n        document.body.style.color = '#000';\n    }\n}\n\nThen, inside the listener we created earlier, we'll use executeScript to execute\nthe function on the current tab:\n\nchrome.scripting.executeScript({\n    target: {tabId: tab.id},\n    function: toggleDark\n});\n\nMV2\n\nIf you're creating an MV2 extension, you need to add a background script\ninstead:\n\n\"background\": {\n   \"scripts\": [\"background.js\"],\n   \"persistent\": false\n },\n\nIn  the background script, we'll add a listener to \nchrome.browserAction.onClicked event:\n\nchrome.browserAction.onClicked.addListener((tab) => {\n    //TODO toggle dark mode in the tab\n});\n\nchrome.scripting is not compatible with MV2. Instead, we'll use \nchrome.tabs.executeScript\n[https://developer.chrome.com/docs/extensions/reference/tabs/#method-executeScript]\n.  This method accepts 2 arguments, the first one the id of the tab to execute\nthe script on, and the second is an object of options including the file option\nwhich allows you to specify a Javascript file to inject into the page and\nexecute. So, create a new file called toggleDark.js in the root of the extension\nwith the following content:\n\nif (!document.body.getAttribute('data-ext-dark') || document.body.getAttribute('data-ext-dark') === 'false') {\n    document.body.setAttribute('data-ext-dark', true);\n    document.body.style.backgroundColor = '#000';\n    document.body.style.color = '#fff';\n} else {\n    document.body.setAttribute('data-ext-dark', false);\n    document.body.style.backgroundColor = '#fff';\n    document.body.style.color = '#000';\n}\n\nAnd in the listener we created earlier, use chrome.tabs.executeScript to run the\ncode in toggleDark.js:\n\nchrome.browserAction.onClicked.addListener((tab) => {\n    chrome.tabs.executeScript(tab.id, {\n        file: 'toggleDark.js'\n    });\n});\n\nAdding Permissions\n\nNow we're ready to handle when the shortcut is pressed. There's one last thing\nwe need to do which is add permissions. Permissions are defined in manifest.json \nand include a set of permissions that the user has to allow for the extension to\nrun.\n\nMV3\n\nIf you've been following along with the MV3 instructions, then you'll need two\npermissions: activeTab to access the current tab and scripting to use the \nchrome.scripting API. So, add the following in manifest.json:\n\n\"permissions\": [\"activeTab\", \"scripting\"],\n\nMV2\n\nAs for MV2, we'll need the activeTab permission to access the current tab and \ntabs to use the chrome.tabs API. So, add the following in manifest.json:\n\n\"permissions\": [\"activeTab\", \"tabs\"],\n\nTesting\n\nLet's test our code. First, go to chrome://extensions and refresh the extension.\nThen, in an open tab (if you're creating an MV2 extension you'll need to refresh\nthe page), press Ctrl+Shift+L, and the document's body background color will\nchange to black and the text to white. Remember that we're taking a simple\napproach to dark mode, so obviously it will not actually look good. If you keep\npressing the same shortcut, it will toggle between light and dark mode.\n\nIf it doesn't work, make sure that Ctrl+Shift+P in your manifest.json is written\ncorrectly, as this is case-sensitive. Also, it should be noted that if other\nextensions that were installed previously use this shortcut, they'll have\nprecedence to use the shortcut.  Similarly, if the shortcut is used by the\nbrowser or your operating system, then it will not be triggered. So, if the\ncommand does not work for you, try changing the shortcut in the manifest, then\nrefresh the extension and try again.\n\n\n--------------------------------------------------------------------------------\n\nAdding a Standard Command\nNext, we'll add a standard command. The standard command we'll create will use\nthe shortcut Ctrl+Shift+P to print to the console of the service worker or\nbackground script \"Hello there!\". It will run as long as Chrome is in focus,\nregardless of what page or tab is open.\n\nThis section is the same for both MV3 and MV2, so you can follow along\nregardless of what version your extension is. \n\nIn manifest.json, add another command under the commands key:\n\n\"commands\": {\n\t//...\n    \"hello\": {\n       \"suggested_key\": \"Ctrl+Shift+P\",\n       \"description\": \"Say Hello\"\n    },\n}\n\nThen, in the service worker or background script, add a listener to the \nchrome.commands.onCommand event:\n\nchrome.commands.onCommand.addListener((command) => {\n    //TODO handle event\n});\n\nIn the listener, we'll first check which command is triggered. This is necessary\nonce you have more than one standard command, as they all trigger this same\nevent.\n\nThe command argument passed is the name of the command. So, if the user presses \nCtrl+Shift+P, the value for command will be hello.\n\nIf the command is hello, we'll log to the console \"Hello there!\":\n\nif (command === 'hello') {\n\tconsole.log(\"Hello there!\");\n}\n\nThat's it. Go to chrome://extensions and refresh the extension. Then, for MV3 \nextensions click on \"Inspect views service worker\"\n\nFor MV2 extensions click on \"Inspect views background page\"\n\nThis will open the devtools for the service worker or background script. Click\non the Console tab. Now, try clicking the shortcut Ctrl+Shift+P and you'll see\n\"Hello there!\" logged in the console.\n\nIf it doesn't work, make sure that Ctrl+Shift+P in your manifest.json is written\ncorrectly, as this is case-sensitive. Also, it should be noted that if other\nextensions that were installed previously use this shortcut, they'll have\nprecedence to use the shortcut.  Similarly, if the shortcut is used by the\nbrowser or your operating system, then it will not be triggered. So, if the\ncommand does not work for you, try changing the shortcut in the manifest, then\nrefresh the extension and try again.\n\n\n--------------------------------------------------------------------------------\n\nMaking a Command Global\nIn this section, we'll create a global standard command that when the user\npresses Ctrl+Shift+8, it will log to the console of the service worker or\nbackground script the current date.\n\nThis section is the same for both MV3 and MV2, so you can follow along\nregardless of what version your extension is.\n\nTo make a command global, we just need to add the key global to its definition\nin manifest.json and set it to true:\n\n\"date\": {\n\t\"suggested_key\": \"Ctrl+Shift+8\",\n\t\"description\": \"Show Date\",\n    \"global\": true\n}\n\nThen, in your service worker or background script, add an else block to the\nprevious if statement that checked if the command name was \"hello\". In the else\nblock, we'll log to the console the current date:\n\nchrome.commands.onCommand.addListener((command) => {\n    if (command === 'hello') {\n        console.log(\"Hello there!\");\n    } else {\n        console.log(\"Date: \" + (new Date).toDateString());\n    }\n});\n\nOur new command is ready. During development, I noticed that the global command,\nwhether it's a newly added command or an older command that we added the global \nkey to it, later on, the shortcut was not added or updated unless the extension\nwas removed and re-loaded. So, make sure to do that.\n\nThen, open the console for the service worker or background script as mentioned\nin the previous section. If you now change to any window that's not your Chrome\nbrowser and press Ctrl+Shift+8, then go back to the console for the service\nworker or background script, you'll see that the date is logged.\n\nAgain, if the command does not work, make sure that the command in the manifest\nis correct as it is case-sensitive. Also, if this command is used by your system\nfor another shortcut, you'll need to change it to something else.\n\n\n--------------------------------------------------------------------------------\n\nConclusion: Next Steps\nAs you have noticed while going through this tutorial, if an extension that was\nadded prior to yours uses the same shortcut, your shortcuts will not be\nregistered, so they will not work. To avoid that, you can check if your\nshortcuts are all added on installing the extension, and if any are missing you\ncan notify the user that they can set the shortcuts in their Chrome browser on \nchrome://extensions/shortcuts. For more information and an example on how to do\nthat, check the example on chrome.command reference\n[https://developer.chrome.com/docs/extensions/reference/commands/#verify-commands-registered]\n.","html":"<p>Chrome Extensions have a lot of features and functionalities that help developers create extensions and tools with great user experience.</p><p>In this tutorial, we'll go over how to add keyboard shortcuts in a Chrome extension. This tutorial will cover how it can be done for both Manifest Versions 2 (MV2) and 3 (MV3). </p><p><em>Suggested Read: <a href=\"https://blog.shahednasser.com/chrome-extension-tutorial-migrating-to-manifest-v3-from-v2\">Chrome Extension Tutorial: Migrating to Manifest V3 from V2</a></em></p><p>You can find the full code for this tutorial on <a href=\"https://github.com/shahednasser/chrome-commands-tutorial\">this GitHub repository</a>.</p><hr><h3 id=\"creating-the-extension\">Creating The Extension</h3><p>In case you already have an extension ready, you can skip this step. We'll quickly go over how to create a Chrome extension.</p><p><em>Suggested Read: Learn how to create a Chrome extension in the tutorial <a href=\"https://blog.shahednasser.com/chrome-extension-tutorial-replace-images-in-any-website-with-pikachu\">Chrome Extension Tutorial — Replace Images in Any Website with Pikachu</a>.</em></p><p>To create a Chrome extension, first, create a directory that will hold all the extension files in it:</p><pre><code class=\"language-bash\">mkdir new-extension\ncd new-extension</code></pre><p>Then, create a <code>manifest.json</code> file with the following content:</p><pre><code class=\"language-json\">{\n    \"name\": \"Commands\",\n    \"version\": \"0.0.1\",\n    \"description\": \"Assign Keyboard Commands\",\n    \"manifest_version\": 3,\n    \"icons\": {\n        \"16\": \"/assets/icon-16.png\",\n        \"32\": \"/assets/icon-32.png\",\n        \"48\": \"/assets/icon-48.png\",\n        \"128\": \"/assets/icon-128.png\"\n    },\n}</code></pre><p>If you're creating a <strong>Manifest V2</strong> extension, make sure to change the manifest version:</p><pre><code class=\"language-json\">\"manifest_version\": 2,</code></pre><p>Also, make sure to download the icons from the GitHub repository first, or add your own icons.</p><p>Then, go to <a href=\"chrome://extensions/\">chrome://extensions/</a> on your Chrome browser, enable Developer Mode if you haven't from the top right, click on \"Load unpacked\" and choose the folder that you created for your extension. Once done, the extension will be added to your Chrome browser.</p><hr><h3 id=\"what-are-chrome-commands\">What Are Chrome Commands</h3><p>Chrome commands allow an extension to register commands, or keyboard shortcuts, to provide ease of use for the users of that extension to perform certain actions through those shortcuts. An extension can have at most 4 commands.</p><p>There are two kinds of commands. A standard command and an action command. A standard command runs in the background script or service worker and has no access to the current page or tab opened. Standard commands trigger the event <code>chrome.commands.onCommand</code>, and you can listen to it in your background script or service worker.</p><p>Action commands map the shortcuts to your extension's action (if <strong>MV3</strong>) or browser action or page action (if <strong>MV2</strong>). Action commands can have access to the current tab and can execute a script on the current page. Action commands <strong>do not </strong>trigger the event <code>chrome.commands.onCommand</code>. Instead, in <strong>MV3</strong> they trigger <code>chrome.action.onClicked</code> , and in <strong>MV2</strong> they trigger <code>chrome.browserAction.onClicked</code> or <code>chrome.pageAction.onClicked</code>.</p><p>Commands are defined in an extension's <code>manifest.json</code> under the <code>commands</code> key. Standard commands are defined in the following format:</p><pre><code class=\"language-json\">\"commands\": {\n\t\"firstCommand\": {\n    \t\"suggested_key\": \"CTRL+F\",\n        \"description\": \"My first command\"\n    }\n}</code></pre><p>Where <code>suggested_key</code> can be an object or a string (we'll get to that in a bit) and <code>description</code> is required.</p><p>Action commands are defined in the same way but under the <code>_execute_action</code> key in MV3, or <code>_execute_browser_action</code> or <code>_execute_page_action</code> keys in MV2:</p><pre><code class=\"language-json\">\"commands\": {\n\t\"_execute_action\": {\n    \t\"suggested_key\": \"CTRL+F\",\n        \"description\": \"My first command\"\n    }\n}</code></pre><p><code>description</code> for action commands is optional.</p><h3 id=\"suggested-key\">Suggested Key</h3><p>Some of the keys that are allowed to be used are: <code>Ctrl</code>, <code>Alt</code>, <code>Shift</code>, <code>0...9</code>, and <code>A...Z</code>. For a full list of keys, you can see them in the <a href=\"https://developer.chrome.com/docs/extensions/reference/commands/#supported-keys\">chrome.commands reference</a>.</p><p><code>suggested_key</code> can either be a string or an object. If a string, then the same shortcut will be applied for all Operating Systems. In this case, certain keys will be replaced with their equivalent on the Operating Systems. This means that <code>Ctrl</code> on Mac OS will be substituted by <code>Command</code>.</p><p>To specify the shortcut for an operating system, you should pass an object where the key is the operating system's name. The allowed keys are <code>default</code> which applies the shortcut to all operating systems not listed in the object, <code>chromeos</code>, <code>linux</code>, <code>mac</code>, and <code>windows</code>.</p><p>If you want to use <code>Ctrl</code> on Mac instead of <code>Command</code>, you can use <code>MacCtrl</code> for the value of <code>mac</code> in the object:</p><pre><code class=\"language-json\">\"suggested_key\": {\n\t\"default\": \"Ctrl+F\",\n    \"mac\": \"MacCtrl+F\"\n}</code></pre><p>This will make the shortcut <code>Ctrl+F</code> for all operating systems, but for Mac instead of the conventional <code>Command</code> substitute, it will use the <code>Ctrl</code> key on Mac.</p><p>It should be noted that the value for the keyboard shortcuts is <strong>case-sensitive</strong>. If you set the value to <code>CTRL+F</code> instead of <code>Ctrl+F</code>, the shortcut will not be registered.</p><h3 id=\"global-shortcuts\">Global Shortcuts</h3><p>By default, keyboard shortcuts are executed only when the Chrome browser is in focus. To allow shortcuts to work even when Chrome is in the background, you can make the command \"global\" by adding the <code>global</code> key in the definition:</p><pre><code class=\"language-json\">\"commands\": {\n\t\"first\": {\n    \t\"suggested_key\": \"Ctrl+Shift+F\",\n        \"description\": \"First command\",\n        \"global\": true\n    }\n}</code></pre><p>Global shortcuts are very limited in the amount of keys you can use in <code>suggested_key</code> to make sure your shortcut does not overlap with a system shortcut. For that reason, the format should be <code>Ctrl+Shift+[0...9]</code>.</p><p>If the shortcut is already defined and you later set it to global, upon testing it I noticed that the shortcut will not be updated. To update it, you will need to remove and reinstall the extension.</p><hr><h3 id=\"adding-an-action-command\">Adding an Action Command</h3><p>We'll first add an action command. The action command will toggle \"Dark Mode\" on the current website. We'll do it in a very basic way, which is just changing the background color to black and text color to white, as this is not the purpose of this tutorial.</p><p><strong>Define The Command</strong></p><p>Let's first define the action command. It will be triggered at the press of <code>Ctrl+Shift+L</code>. </p><p><strong>MV3</strong></p><p>For <strong>MV3</strong>, the definition will be:</p><pre><code class=\"language-json\">\"commands\": {\n\t\"_execute_action\": {\n\t\t\"suggested_key\": \"Ctrl+Shift+L\",\n\t\t\"description\": \"Toggle dark body\"\n\t},\n}</code></pre><p>As we are adding an Action command, we need to also add an <code>action</code> key<strong>:</strong></p><pre><code class=\"language-json\">\"action\": {},</code></pre><p><strong>MV2</strong></p><p>For <strong>MV2 </strong>we just need to change <code>_execute_action</code> to <code>_execute_browser_action</code>:</p><pre><code class=\"language-json\">\"commands\": {\n\t\"_execute_browser_action\": {\n            \"suggested_key\": \"Ctrl+Shift+L\",\n            \"description\": \"Toggle dark body\"\n\t},\n}</code></pre><p>and add a <code>browser_action</code> key<strong>:</strong></p><pre><code class=\"language-json\">\"browser_action\": {},</code></pre><p>This will define our command that toggles dark mode at the press of <code>Ctrl+Shift+L</code>. Make sure that the <code>suggested_key</code> value is correct as it is <strong>case-sensitive</strong>. If it's incorrect, the shortcut will not be registered.</p><p><strong>Add Event Listeners</strong></p><p>Next, we need to listen to when these keys are pressed. Inside the listener, we need to execute a Javascript code that will just toggle the background color and text color of the document's body.</p><p><strong>MV3</strong></p><p>To do that, first, add a service worker:</p><pre><code class=\"language-json\">\"background\": {\n   \"service_worker\": \"background.js\"\n},</code></pre><p>Then, in the service worker or background script, we'll add a listener to handle the press of the keys. In the service worker, we'll add a listener to <code>chrome.action.onClicked</code> event:</p><pre><code class=\"language-js\">chrome.action.onClicked.addListener((tab) =&gt; {\n    //TODO toggle dark mode in the tab\n});</code></pre><p>Inside the listener, we'll use <a href=\"https://developer.chrome.com/docs/extensions/reference/scripting/#method-executeScript\">chrome.scripting.executeScript</a><strong>. </strong><code>executeScript</code> accepts an object of options including <code>tabId</code> for the id of the tab to execute the script on and <code>function</code> for the Javascript function to execute. Add in the service worker the following function:</p><pre><code class=\"language-js\">function toggleDark () {\n    if (!document.body.getAttribute('data-ext-dark')) {\n        document.body.setAttribute('data-ext-dark', true);\n        document.body.style.backgroundColor = '#000';\n        document.body.style.color = '#fff';\n    } else {\n        document.body.setAttribute('data-ext-dark', false);\n        document.body.style.backgroundColor = '#fff';\n        document.body.style.color = '#000';\n    }\n}</code></pre><p>Then, inside the listener we created earlier, we'll use <code>executeScript</code> to execute the function on the current tab:</p><pre><code class=\"language-js\">chrome.scripting.executeScript({\n    target: {tabId: tab.id},\n    function: toggleDark\n});</code></pre><p><strong>MV2</strong></p><p>If you're creating an <strong>MV2 </strong>extension, you need to add a background script instead:</p><pre><code class=\"language-json\">\"background\": {\n   \"scripts\": [\"background.js\"],\n   \"persistent\": false\n },</code></pre><p>In  the background script, we'll add a listener to <code>chrome.browserAction.onClicked</code> event:</p><pre><code class=\"language-js\">chrome.browserAction.onClicked.addListener((tab) =&gt; {\n    //TODO toggle dark mode in the tab\n});</code></pre><p><code>chrome.scripting</code> is not compatible with <strong>MV2</strong>. Instead, we'll use <a href=\"https://developer.chrome.com/docs/extensions/reference/tabs/#method-executeScript\">chrome.tabs.executeScript</a>.  This method accepts 2 arguments, the first one the id of the tab to execute the script on, and the second is an object of options including the <code>file</code> option which allows you to specify a Javascript file to inject into the page and execute. So, create a new file called <code>toggleDark.js</code> in the root of the extension with the following content:</p><pre><code class=\"language-js\">if (!document.body.getAttribute('data-ext-dark') || document.body.getAttribute('data-ext-dark') === 'false') {\n    document.body.setAttribute('data-ext-dark', true);\n    document.body.style.backgroundColor = '#000';\n    document.body.style.color = '#fff';\n} else {\n    document.body.setAttribute('data-ext-dark', false);\n    document.body.style.backgroundColor = '#fff';\n    document.body.style.color = '#000';\n}</code></pre><p>And in the listener we created earlier, use <code>chrome.tabs.executeScript</code> to run the code in <code>toggleDark.js</code>:</p><pre><code class=\"language-js\">chrome.browserAction.onClicked.addListener((tab) =&gt; {\n    chrome.tabs.executeScript(tab.id, {\n        file: 'toggleDark.js'\n    });\n});</code></pre><p><strong>Adding Permissions</strong></p><p>Now we're ready to handle when the shortcut is pressed. There's one last thing we need to do which is add permissions. Permissions are defined in <code>manifest.json</code> and include a set of permissions that the user has to allow for the extension to run.</p><p><strong>MV3</strong></p><p>If you've been following along with the <strong>MV3 </strong>instructions, then you'll need two permissions: <code>activeTab</code> to access the current tab and <code>scripting</code> to use the <code>chrome.scripting</code> API. So, add the following in <code>manifest.json</code>:</p><pre><code class=\"language-json\">\"permissions\": [\"activeTab\", \"scripting\"],</code></pre><p><strong>MV2</strong></p><p>As for <strong>MV2, </strong>we'll need the <code>activeTab</code> permission to access the current tab and <code>tabs</code> to use the <code>chrome.tabs</code> API. So, add the following in <code>manifest.json</code>:</p><pre><code class=\"language-json\">\"permissions\": [\"activeTab\", \"tabs\"],</code></pre><p><strong>Testing</strong></p><p>Let's test our code. First, go to <code>chrome://extensions</code> and refresh the extension. Then, in an open tab (if you're creating an MV2 extension you'll need to refresh the page), press <code>Ctrl+Shift+L</code>, and the document's body background color will change to black and the text to white. Remember that we're taking a simple approach to dark mode, so obviously it will not actually look good. If you keep pressing the same shortcut, it will toggle between light and dark mode.</p><figure class=\"kg-card kg-image-card\"><img src=\"https://res-1.cloudinary.com/hbqmf3mbz/image/upload/q_auto/v1/ghost-blog-images/Screen-Shot-2021-07-08-at-6.54.33-PM.png\" class=\"kg-image\" alt loading=\"lazy\"></figure><p>If it doesn't work, make sure that <code>Ctrl+Shift+P</code> in your <code>manifest.json</code> is written correctly, as this is case-sensitive. Also, it should be noted that if other extensions that were installed previously use this shortcut, they'll have precedence to use the shortcut.  Similarly, if the shortcut is used by the browser or your operating system, then it will not be triggered. So, if the command does not work for you, try changing the shortcut in the manifest, then refresh the extension and try again.</p><hr><h3 id=\"adding-a-standard-command\">Adding a Standard Command</h3><p>Next, we'll add a standard command. The standard command we'll create will use the shortcut <code>Ctrl+Shift+P</code> to print to the console of the service worker or background script \"Hello there!\". It will run as long as Chrome is in focus, regardless of what page or tab is open.</p><p>This section is the same for both <strong>MV3 </strong>and <strong>MV2</strong>, so you can follow along regardless of what version your extension is. </p><p>In <code>manifest.json</code>, add another command under the <code>commands</code> key:</p><pre><code class=\"language-json\">\"commands\": {\n\t//...\n    \"hello\": {\n       \"suggested_key\": \"Ctrl+Shift+P\",\n       \"description\": \"Say Hello\"\n    },\n}</code></pre><p>Then, in the service worker or background script, add a listener to the <code>chrome.commands.onCommand</code> event:</p><pre><code class=\"language-js\">chrome.commands.onCommand.addListener((command) =&gt; {\n    //TODO handle event\n});</code></pre><p>In the listener, we'll first check which command is triggered. This is necessary once you have more than one standard command, as they all trigger this same event.</p><p>The <code>command</code> argument passed is the name of the command. So, if the user presses <code>Ctrl+Shift+P</code>, the value for <code>command</code> will be <code>hello</code>.</p><p>If the <code>command</code> is <code>hello</code>, we'll log to the console \"Hello there!\":</p><pre><code class=\"language-js\">if (command === 'hello') {\n\tconsole.log(\"Hello there!\");\n}</code></pre><p>That's it. Go to <a href=\"chrome://extensions\">chrome://extensions</a> and refresh the extension. Then, for <strong>MV3 </strong>extensions click on \"Inspect views service worker\"</p><figure class=\"kg-card kg-image-card\"><img src=\"https://res-1.cloudinary.com/hbqmf3mbz/image/upload/q_auto/v1/ghost-blog-images/Screen-Shot-2021-07-08-at-7.10.25-PM.png\" class=\"kg-image\" alt loading=\"lazy\"></figure><p>For <strong>MV2 </strong>extensions click on \"Inspect views background page\"</p><figure class=\"kg-card kg-image-card\"><img src=\"https://res-5.cloudinary.com/hbqmf3mbz/image/upload/q_auto/v1/ghost-blog-images/Screen-Shot-2021-07-08-at-7.10.31-PM.png\" class=\"kg-image\" alt loading=\"lazy\"></figure><p>This will open the devtools for the service worker or background script. Click on the Console tab. Now, try clicking the shortcut <code>Ctrl+Shift+P</code> and you'll see \"Hello there!\" logged in the console.</p><figure class=\"kg-card kg-image-card\"><img src=\"https://res-3.cloudinary.com/hbqmf3mbz/image/upload/q_auto/v1/ghost-blog-images/Screen-Shot-2021-07-08-at-7.13.14-PM.png\" class=\"kg-image\" alt loading=\"lazy\"></figure><p>If it doesn't work, make sure that <code>Ctrl+Shift+P</code> in your <code>manifest.json</code> is written correctly, as this is case-sensitive. Also, it should be noted that if other extensions that were installed previously use this shortcut, they'll have precedence to use the shortcut.  Similarly, if the shortcut is used by the browser or your operating system, then it will not be triggered. So, if the command does not work for you, try changing the shortcut in the manifest, then refresh the extension and try again.</p><hr><h3 id=\"making-a-command-global\">Making a Command Global</h3><p>In this section, we'll create a global standard command that when the user presses <code>Ctrl+Shift+8</code>, it will log to the console of the service worker or background script the current date.</p><p>This section is the same for both <strong>MV3 </strong>and <strong>MV2</strong>, so you can follow along regardless of what version your extension is.</p><p>To make a command global, we just need to add the key <code>global</code> to its definition in <code>manifest.json</code> and set it to true:</p><pre><code class=\"language-json\">\"date\": {\n\t\"suggested_key\": \"Ctrl+Shift+8\",\n\t\"description\": \"Show Date\",\n    \"global\": true\n}</code></pre><p>Then, in your service worker or background script, add an else block to the previous if statement that checked if the command name was \"hello\". In the else block, we'll log to the console the current date:</p><pre><code class=\"language-js\">chrome.commands.onCommand.addListener((command) =&gt; {\n    if (command === 'hello') {\n        console.log(\"Hello there!\");\n    } else {\n        console.log(\"Date: \" + (new Date).toDateString());\n    }\n});</code></pre><p>Our new command is ready. During development, I noticed that the global command, whether it's a newly added command or an older command that we added the <code>global</code> key to it, later on, the shortcut was not added or updated unless the extension was removed and re-loaded. So, make sure to do that.</p><p>Then, open the console for the service worker or background script as mentioned in the previous section. If you now change to any window that's not your Chrome browser and press <code>Ctrl+Shift+8</code>, then go back to the console for the service worker or background script, you'll see that the date is logged.</p><figure class=\"kg-card kg-image-card\"><img src=\"https://res-5.cloudinary.com/hbqmf3mbz/image/upload/q_auto/v1/ghost-blog-images/Screen-Shot-2021-07-08-at-7.19.23-PM.png\" class=\"kg-image\" alt loading=\"lazy\"></figure><p>Again, if the command does not work, make sure that the command in the manifest is correct as it is <strong>case-sensitive</strong>. Also, if this command is used by your system for another shortcut, you'll need to change it to something else.</p><hr><h3 id=\"conclusion-next-steps\">Conclusion: Next Steps</h3><p>As you have noticed while going through this tutorial, if an extension that was added prior to yours uses the same shortcut, your shortcuts will not be registered, so they will not work. To avoid that, you can check if your shortcuts are all added on installing the extension, and if any are missing you can notify the user that they can set the shortcuts in their Chrome browser on <a href=\"chrome://extensions/shortcuts\">chrome://extensions/shortcuts</a>. For more information and an example on how to do that, check <a href=\"https://developer.chrome.com/docs/extensions/reference/commands/#verify-commands-registered\">the example on chrome.command reference</a>.</p>","url":"https://backend.shahednasser.com/how-to-add-keyboard-shortcuts-in-a-chrome-extension/","canonical_url":null,"uuid":"454cb436-1f87-4c72-a735-478465c426c7","codeinjection_foot":null,"codeinjection_head":null,"codeinjection_styles":null,"comment_id":"60e5e3909a8b5a001e01263e","reading_time":10,"send_email_when_published":null,"email_subject":null,"childHtmlRehype":{"html":"<p>Chrome Extensions have a lot of features and functionalities that help developers create extensions and tools with great user experience.</p><p>In this tutorial, we'll go over how to add keyboard shortcuts in a Chrome extension. This tutorial will cover how it can be done for both Manifest Versions 2 (MV2) and 3 (MV3). </p><p><em>Suggested Read: <a href=\"https://blog.shahednasser.com/chrome-extension-tutorial-migrating-to-manifest-v3-from-v2\">Chrome Extension Tutorial: Migrating to Manifest V3 from V2</a></em></p><p>You can find the full code for this tutorial on <a href=\"https://github.com/shahednasser/chrome-commands-tutorial\">this GitHub repository</a>.</p><hr><h3 id=\"creating-the-extension\">Creating The Extension</h3><p>In case you already have an extension ready, you can skip this step. We'll quickly go over how to create a Chrome extension.</p><p><em>Suggested Read: Learn how to create a Chrome extension in the tutorial <a href=\"https://blog.shahednasser.com/chrome-extension-tutorial-replace-images-in-any-website-with-pikachu\">Chrome Extension Tutorial — Replace Images in Any Website with Pikachu</a>.</em></p><p>To create a Chrome extension, first, create a directory that will hold all the extension files in it:</p><div class=\"kg-card kg-code-card gatsby-highlight\" data-language=\"bash\"><pre class=\"language-bash\"><code class=\"language-bash\"><span class=\"token function\">mkdir</span> new-extension\n<span class=\"token builtin class-name\">cd</span> new-extension</code></pre></div><p>Then, create a <code class=\"language-text\">manifest.json</code> file with the following content:</p><div class=\"kg-card kg-code-card gatsby-highlight\" data-language=\"json\"><pre class=\"language-json\"><code class=\"language-json\"><span class=\"token punctuation\">{</span>\n    <span class=\"token property\">\"name\"</span><span class=\"token operator\">:</span> <span class=\"token string\">\"Commands\"</span><span class=\"token punctuation\">,</span>\n    <span class=\"token property\">\"version\"</span><span class=\"token operator\">:</span> <span class=\"token string\">\"0.0.1\"</span><span class=\"token punctuation\">,</span>\n    <span class=\"token property\">\"description\"</span><span class=\"token operator\">:</span> <span class=\"token string\">\"Assign Keyboard Commands\"</span><span class=\"token punctuation\">,</span>\n    <span class=\"token property\">\"manifest_version\"</span><span class=\"token operator\">:</span> <span class=\"token number\">3</span><span class=\"token punctuation\">,</span>\n    <span class=\"token property\">\"icons\"</span><span class=\"token operator\">:</span> <span class=\"token punctuation\">{</span>\n        <span class=\"token property\">\"16\"</span><span class=\"token operator\">:</span> <span class=\"token string\">\"/assets/icon-16.png\"</span><span class=\"token punctuation\">,</span>\n        <span class=\"token property\">\"32\"</span><span class=\"token operator\">:</span> <span class=\"token string\">\"/assets/icon-32.png\"</span><span class=\"token punctuation\">,</span>\n        <span class=\"token property\">\"48\"</span><span class=\"token operator\">:</span> <span class=\"token string\">\"/assets/icon-48.png\"</span><span class=\"token punctuation\">,</span>\n        <span class=\"token property\">\"128\"</span><span class=\"token operator\">:</span> <span class=\"token string\">\"/assets/icon-128.png\"</span>\n    <span class=\"token punctuation\">}</span><span class=\"token punctuation\">,</span>\n<span class=\"token punctuation\">}</span></code></pre></div><p>If you're creating a <strong>Manifest V2</strong> extension, make sure to change the manifest version:</p><div class=\"kg-card kg-code-card gatsby-highlight\" data-language=\"json\"><pre class=\"language-json\"><code class=\"language-json\"><span class=\"token property\">\"manifest_version\"</span><span class=\"token operator\">:</span> <span class=\"token number\">2</span><span class=\"token punctuation\">,</span></code></pre></div><p>Also, make sure to download the icons from the GitHub repository first, or add your own icons.</p><p>Then, go to <a href=\"chrome://extensions/\">chrome://extensions/</a> on your Chrome browser, enable Developer Mode if you haven't from the top right, click on \"Load unpacked\" and choose the folder that you created for your extension. Once done, the extension will be added to your Chrome browser.</p><hr><h3 id=\"what-are-chrome-commands\">What Are Chrome Commands</h3><p>Chrome commands allow an extension to register commands, or keyboard shortcuts, to provide ease of use for the users of that extension to perform certain actions through those shortcuts. An extension can have at most 4 commands.</p><p>There are two kinds of commands. A standard command and an action command. A standard command runs in the background script or service worker and has no access to the current page or tab opened. Standard commands trigger the event <code class=\"language-text\">chrome.commands.onCommand</code>, and you can listen to it in your background script or service worker.</p><p>Action commands map the shortcuts to your extension's action (if <strong>MV3</strong>) or browser action or page action (if <strong>MV2</strong>). Action commands can have access to the current tab and can execute a script on the current page. Action commands <strong>do not </strong>trigger the event <code class=\"language-text\">chrome.commands.onCommand</code>. Instead, in <strong>MV3</strong> they trigger <code class=\"language-text\">chrome.action.onClicked</code> , and in <strong>MV2</strong> they trigger <code class=\"language-text\">chrome.browserAction.onClicked</code> or <code class=\"language-text\">chrome.pageAction.onClicked</code>.</p><p>Commands are defined in an extension's <code class=\"language-text\">manifest.json</code> under the <code class=\"language-text\">commands</code> key. Standard commands are defined in the following format:</p><div class=\"kg-card kg-code-card gatsby-highlight\" data-language=\"json\"><pre class=\"language-json\"><code class=\"language-json\"><span class=\"token property\">\"commands\"</span><span class=\"token operator\">:</span> <span class=\"token punctuation\">{</span>\n\t<span class=\"token property\">\"firstCommand\"</span><span class=\"token operator\">:</span> <span class=\"token punctuation\">{</span>\n    \t<span class=\"token property\">\"suggested_key\"</span><span class=\"token operator\">:</span> <span class=\"token string\">\"CTRL+F\"</span><span class=\"token punctuation\">,</span>\n        <span class=\"token property\">\"description\"</span><span class=\"token operator\">:</span> <span class=\"token string\">\"My first command\"</span>\n    <span class=\"token punctuation\">}</span>\n<span class=\"token punctuation\">}</span></code></pre></div><p>Where <code class=\"language-text\">suggested_key</code> can be an object or a string (we'll get to that in a bit) and <code class=\"language-text\">description</code> is required.</p><p>Action commands are defined in the same way but under the <code class=\"language-text\">_execute_action</code> key in MV3, or <code class=\"language-text\">_execute_browser_action</code> or <code class=\"language-text\">_execute_page_action</code> keys in MV2:</p><div class=\"kg-card kg-code-card gatsby-highlight\" data-language=\"json\"><pre class=\"language-json\"><code class=\"language-json\"><span class=\"token property\">\"commands\"</span><span class=\"token operator\">:</span> <span class=\"token punctuation\">{</span>\n\t<span class=\"token property\">\"_execute_action\"</span><span class=\"token operator\">:</span> <span class=\"token punctuation\">{</span>\n    \t<span class=\"token property\">\"suggested_key\"</span><span class=\"token operator\">:</span> <span class=\"token string\">\"CTRL+F\"</span><span class=\"token punctuation\">,</span>\n        <span class=\"token property\">\"description\"</span><span class=\"token operator\">:</span> <span class=\"token string\">\"My first command\"</span>\n    <span class=\"token punctuation\">}</span>\n<span class=\"token punctuation\">}</span></code></pre></div><p><code class=\"language-text\">description</code> for action commands is optional.</p><h3 id=\"suggested-key\">Suggested Key</h3><p>Some of the keys that are allowed to be used are: <code class=\"language-text\">Ctrl</code>, <code class=\"language-text\">Alt</code>, <code class=\"language-text\">Shift</code>, <code class=\"language-text\">0...9</code>, and <code class=\"language-text\">A...Z</code>. For a full list of keys, you can see them in the <a href=\"https://developer.chrome.com/docs/extensions/reference/commands/#supported-keys\">chrome.commands reference</a>.</p><p><code class=\"language-text\">suggested_key</code> can either be a string or an object. If a string, then the same shortcut will be applied for all Operating Systems. In this case, certain keys will be replaced with their equivalent on the Operating Systems. This means that <code class=\"language-text\">Ctrl</code> on Mac OS will be substituted by <code class=\"language-text\">Command</code>.</p><p>To specify the shortcut for an operating system, you should pass an object where the key is the operating system's name. The allowed keys are <code class=\"language-text\">default</code> which applies the shortcut to all operating systems not listed in the object, <code class=\"language-text\">chromeos</code>, <code class=\"language-text\">linux</code>, <code class=\"language-text\">mac</code>, and <code class=\"language-text\">windows</code>.</p><p>If you want to use <code class=\"language-text\">Ctrl</code> on Mac instead of <code class=\"language-text\">Command</code>, you can use <code class=\"language-text\">MacCtrl</code> for the value of <code class=\"language-text\">mac</code> in the object:</p><div class=\"kg-card kg-code-card gatsby-highlight\" data-language=\"json\"><pre class=\"language-json\"><code class=\"language-json\"><span class=\"token property\">\"suggested_key\"</span><span class=\"token operator\">:</span> <span class=\"token punctuation\">{</span>\n\t<span class=\"token property\">\"default\"</span><span class=\"token operator\">:</span> <span class=\"token string\">\"Ctrl+F\"</span><span class=\"token punctuation\">,</span>\n    <span class=\"token property\">\"mac\"</span><span class=\"token operator\">:</span> <span class=\"token string\">\"MacCtrl+F\"</span>\n<span class=\"token punctuation\">}</span></code></pre></div><p>This will make the shortcut <code class=\"language-text\">Ctrl+F</code> for all operating systems, but for Mac instead of the conventional <code class=\"language-text\">Command</code> substitute, it will use the <code class=\"language-text\">Ctrl</code> key on Mac.</p><p>It should be noted that the value for the keyboard shortcuts is <strong>case-sensitive</strong>. If you set the value to <code class=\"language-text\">CTRL+F</code> instead of <code class=\"language-text\">Ctrl+F</code>, the shortcut will not be registered.</p><h3 id=\"global-shortcuts\">Global Shortcuts</h3><p>By default, keyboard shortcuts are executed only when the Chrome browser is in focus. To allow shortcuts to work even when Chrome is in the background, you can make the command \"global\" by adding the <code class=\"language-text\">global</code> key in the definition:</p><div class=\"kg-card kg-code-card gatsby-highlight\" data-language=\"json\"><pre class=\"language-json\"><code class=\"language-json\"><span class=\"token property\">\"commands\"</span><span class=\"token operator\">:</span> <span class=\"token punctuation\">{</span>\n\t<span class=\"token property\">\"first\"</span><span class=\"token operator\">:</span> <span class=\"token punctuation\">{</span>\n    \t<span class=\"token property\">\"suggested_key\"</span><span class=\"token operator\">:</span> <span class=\"token string\">\"Ctrl+Shift+F\"</span><span class=\"token punctuation\">,</span>\n        <span class=\"token property\">\"description\"</span><span class=\"token operator\">:</span> <span class=\"token string\">\"First command\"</span><span class=\"token punctuation\">,</span>\n        <span class=\"token property\">\"global\"</span><span class=\"token operator\">:</span> <span class=\"token boolean\">true</span>\n    <span class=\"token punctuation\">}</span>\n<span class=\"token punctuation\">}</span></code></pre></div><p>Global shortcuts are very limited in the amount of keys you can use in <code class=\"language-text\">suggested_key</code> to make sure your shortcut does not overlap with a system shortcut. For that reason, the format should be <code class=\"language-text\">Ctrl+Shift+[0...9]</code>.</p><p>If the shortcut is already defined and you later set it to global, upon testing it I noticed that the shortcut will not be updated. To update it, you will need to remove and reinstall the extension.</p><hr><h3 id=\"adding-an-action-command\">Adding an Action Command</h3><p>We'll first add an action command. The action command will toggle \"Dark Mode\" on the current website. We'll do it in a very basic way, which is just changing the background color to black and text color to white, as this is not the purpose of this tutorial.</p><p><strong>Define The Command</strong></p><p>Let's first define the action command. It will be triggered at the press of <code class=\"language-text\">Ctrl+Shift+L</code>. </p><p><strong>MV3</strong></p><p>For <strong>MV3</strong>, the definition will be:</p><div class=\"kg-card kg-code-card gatsby-highlight\" data-language=\"json\"><pre class=\"language-json\"><code class=\"language-json\"><span class=\"token property\">\"commands\"</span><span class=\"token operator\">:</span> <span class=\"token punctuation\">{</span>\n\t<span class=\"token property\">\"_execute_action\"</span><span class=\"token operator\">:</span> <span class=\"token punctuation\">{</span>\n\t\t<span class=\"token property\">\"suggested_key\"</span><span class=\"token operator\">:</span> <span class=\"token string\">\"Ctrl+Shift+L\"</span><span class=\"token punctuation\">,</span>\n\t\t<span class=\"token property\">\"description\"</span><span class=\"token operator\">:</span> <span class=\"token string\">\"Toggle dark body\"</span>\n\t<span class=\"token punctuation\">}</span><span class=\"token punctuation\">,</span>\n<span class=\"token punctuation\">}</span></code></pre></div><p>As we are adding an Action command, we need to also add an <code class=\"language-text\">action</code> key<strong>:</strong></p><div class=\"kg-card kg-code-card gatsby-highlight\" data-language=\"json\"><pre class=\"language-json\"><code class=\"language-json\"><span class=\"token property\">\"action\"</span><span class=\"token operator\">:</span> <span class=\"token punctuation\">{</span><span class=\"token punctuation\">}</span><span class=\"token punctuation\">,</span></code></pre></div><p><strong>MV2</strong></p><p>For <strong>MV2 </strong>we just need to change <code class=\"language-text\">_execute_action</code> to <code class=\"language-text\">_execute_browser_action</code>:</p><div class=\"kg-card kg-code-card gatsby-highlight\" data-language=\"json\"><pre class=\"language-json\"><code class=\"language-json\"><span class=\"token property\">\"commands\"</span><span class=\"token operator\">:</span> <span class=\"token punctuation\">{</span>\n\t<span class=\"token property\">\"_execute_browser_action\"</span><span class=\"token operator\">:</span> <span class=\"token punctuation\">{</span>\n            <span class=\"token property\">\"suggested_key\"</span><span class=\"token operator\">:</span> <span class=\"token string\">\"Ctrl+Shift+L\"</span><span class=\"token punctuation\">,</span>\n            <span class=\"token property\">\"description\"</span><span class=\"token operator\">:</span> <span class=\"token string\">\"Toggle dark body\"</span>\n\t<span class=\"token punctuation\">}</span><span class=\"token punctuation\">,</span>\n<span class=\"token punctuation\">}</span></code></pre></div><p>and add a <code class=\"language-text\">browser_action</code> key<strong>:</strong></p><div class=\"kg-card kg-code-card gatsby-highlight\" data-language=\"json\"><pre class=\"language-json\"><code class=\"language-json\"><span class=\"token property\">\"browser_action\"</span><span class=\"token operator\">:</span> <span class=\"token punctuation\">{</span><span class=\"token punctuation\">}</span><span class=\"token punctuation\">,</span></code></pre></div><p>This will define our command that toggles dark mode at the press of <code class=\"language-text\">Ctrl+Shift+L</code>. Make sure that the <code class=\"language-text\">suggested_key</code> value is correct as it is <strong>case-sensitive</strong>. If it's incorrect, the shortcut will not be registered.</p><p><strong>Add Event Listeners</strong></p><p>Next, we need to listen to when these keys are pressed. Inside the listener, we need to execute a Javascript code that will just toggle the background color and text color of the document's body.</p><p><strong>MV3</strong></p><p>To do that, first, add a service worker:</p><div class=\"kg-card kg-code-card gatsby-highlight\" data-language=\"json\"><pre class=\"language-json\"><code class=\"language-json\"><span class=\"token property\">\"background\"</span><span class=\"token operator\">:</span> <span class=\"token punctuation\">{</span>\n   <span class=\"token property\">\"service_worker\"</span><span class=\"token operator\">:</span> <span class=\"token string\">\"background.js\"</span>\n<span class=\"token punctuation\">}</span><span class=\"token punctuation\">,</span></code></pre></div><p>Then, in the service worker or background script, we'll add a listener to handle the press of the keys. In the service worker, we'll add a listener to <code class=\"language-text\">chrome.action.onClicked</code> event:</p><div class=\"kg-card kg-code-card gatsby-highlight\" data-language=\"js\"><pre class=\"language-js\"><code class=\"language-js\">chrome<span class=\"token punctuation\">.</span>action<span class=\"token punctuation\">.</span>onClicked<span class=\"token punctuation\">.</span><span class=\"token function\">addListener</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">(</span><span class=\"token parameter\">tab</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">=></span> <span class=\"token punctuation\">{</span>\n    <span class=\"token comment\">//TODO toggle dark mode in the tab</span>\n<span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span></code></pre></div><p>Inside the listener, we'll use <a href=\"https://developer.chrome.com/docs/extensions/reference/scripting/#method-executeScript\">chrome.scripting.executeScript</a><strong>. </strong><code class=\"language-text\">executeScript</code> accepts an object of options including <code class=\"language-text\">tabId</code> for the id of the tab to execute the script on and <code class=\"language-text\">function</code> for the Javascript function to execute. Add in the service worker the following function:</p><div class=\"kg-card kg-code-card gatsby-highlight\" data-language=\"js\"><pre class=\"language-js\"><code class=\"language-js\"><span class=\"token keyword\">function</span> <span class=\"token function\">toggleDark</span> <span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token keyword\">if</span> <span class=\"token punctuation\">(</span><span class=\"token operator\">!</span>document<span class=\"token punctuation\">.</span>body<span class=\"token punctuation\">.</span><span class=\"token function\">getAttribute</span><span class=\"token punctuation\">(</span><span class=\"token string\">'data-ext-dark'</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n        document<span class=\"token punctuation\">.</span>body<span class=\"token punctuation\">.</span><span class=\"token function\">setAttribute</span><span class=\"token punctuation\">(</span><span class=\"token string\">'data-ext-dark'</span><span class=\"token punctuation\">,</span> <span class=\"token boolean\">true</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n        document<span class=\"token punctuation\">.</span>body<span class=\"token punctuation\">.</span>style<span class=\"token punctuation\">.</span>backgroundColor <span class=\"token operator\">=</span> <span class=\"token string\">'#000'</span><span class=\"token punctuation\">;</span>\n        document<span class=\"token punctuation\">.</span>body<span class=\"token punctuation\">.</span>style<span class=\"token punctuation\">.</span>color <span class=\"token operator\">=</span> <span class=\"token string\">'#fff'</span><span class=\"token punctuation\">;</span>\n    <span class=\"token punctuation\">}</span> <span class=\"token keyword\">else</span> <span class=\"token punctuation\">{</span>\n        document<span class=\"token punctuation\">.</span>body<span class=\"token punctuation\">.</span><span class=\"token function\">setAttribute</span><span class=\"token punctuation\">(</span><span class=\"token string\">'data-ext-dark'</span><span class=\"token punctuation\">,</span> <span class=\"token boolean\">false</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n        document<span class=\"token punctuation\">.</span>body<span class=\"token punctuation\">.</span>style<span class=\"token punctuation\">.</span>backgroundColor <span class=\"token operator\">=</span> <span class=\"token string\">'#fff'</span><span class=\"token punctuation\">;</span>\n        document<span class=\"token punctuation\">.</span>body<span class=\"token punctuation\">.</span>style<span class=\"token punctuation\">.</span>color <span class=\"token operator\">=</span> <span class=\"token string\">'#000'</span><span class=\"token punctuation\">;</span>\n    <span class=\"token punctuation\">}</span>\n<span class=\"token punctuation\">}</span></code></pre></div><p>Then, inside the listener we created earlier, we'll use <code class=\"language-text\">executeScript</code> to execute the function on the current tab:</p><div class=\"kg-card kg-code-card gatsby-highlight\" data-language=\"js\"><pre class=\"language-js\"><code class=\"language-js\">chrome<span class=\"token punctuation\">.</span>scripting<span class=\"token punctuation\">.</span><span class=\"token function\">executeScript</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">{</span>\n    <span class=\"token literal-property property\">target</span><span class=\"token operator\">:</span> <span class=\"token punctuation\">{</span><span class=\"token literal-property property\">tabId</span><span class=\"token operator\">:</span> tab<span class=\"token punctuation\">.</span>id<span class=\"token punctuation\">}</span><span class=\"token punctuation\">,</span>\n    <span class=\"token keyword\">function</span><span class=\"token operator\">:</span> toggleDark\n<span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span></code></pre></div><p><strong>MV2</strong></p><p>If you're creating an <strong>MV2 </strong>extension, you need to add a background script instead:</p><div class=\"kg-card kg-code-card gatsby-highlight\" data-language=\"json\"><pre class=\"language-json\"><code class=\"language-json\"><span class=\"token property\">\"background\"</span><span class=\"token operator\">:</span> <span class=\"token punctuation\">{</span>\n   <span class=\"token property\">\"scripts\"</span><span class=\"token operator\">:</span> <span class=\"token punctuation\">[</span><span class=\"token string\">\"background.js\"</span><span class=\"token punctuation\">]</span><span class=\"token punctuation\">,</span>\n   <span class=\"token property\">\"persistent\"</span><span class=\"token operator\">:</span> <span class=\"token boolean\">false</span>\n <span class=\"token punctuation\">}</span><span class=\"token punctuation\">,</span></code></pre></div><p>In  the background script, we'll add a listener to <code class=\"language-text\">chrome.browserAction.onClicked</code> event:</p><div class=\"kg-card kg-code-card gatsby-highlight\" data-language=\"js\"><pre class=\"language-js\"><code class=\"language-js\">chrome<span class=\"token punctuation\">.</span>browserAction<span class=\"token punctuation\">.</span>onClicked<span class=\"token punctuation\">.</span><span class=\"token function\">addListener</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">(</span><span class=\"token parameter\">tab</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">=></span> <span class=\"token punctuation\">{</span>\n    <span class=\"token comment\">//TODO toggle dark mode in the tab</span>\n<span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span></code></pre></div><p><code class=\"language-text\">chrome.scripting</code> is not compatible with <strong>MV2</strong>. Instead, we'll use <a href=\"https://developer.chrome.com/docs/extensions/reference/tabs/#method-executeScript\">chrome.tabs.executeScript</a>.  This method accepts 2 arguments, the first one the id of the tab to execute the script on, and the second is an object of options including the <code class=\"language-text\">file</code> option which allows you to specify a Javascript file to inject into the page and execute. So, create a new file called <code class=\"language-text\">toggleDark.js</code> in the root of the extension with the following content:</p><div class=\"kg-card kg-code-card gatsby-highlight\" data-language=\"js\"><pre class=\"language-js\"><code class=\"language-js\"><span class=\"token keyword\">if</span> <span class=\"token punctuation\">(</span><span class=\"token operator\">!</span>document<span class=\"token punctuation\">.</span>body<span class=\"token punctuation\">.</span><span class=\"token function\">getAttribute</span><span class=\"token punctuation\">(</span><span class=\"token string\">'data-ext-dark'</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">||</span> document<span class=\"token punctuation\">.</span>body<span class=\"token punctuation\">.</span><span class=\"token function\">getAttribute</span><span class=\"token punctuation\">(</span><span class=\"token string\">'data-ext-dark'</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">===</span> <span class=\"token string\">'false'</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n    document<span class=\"token punctuation\">.</span>body<span class=\"token punctuation\">.</span><span class=\"token function\">setAttribute</span><span class=\"token punctuation\">(</span><span class=\"token string\">'data-ext-dark'</span><span class=\"token punctuation\">,</span> <span class=\"token boolean\">true</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n    document<span class=\"token punctuation\">.</span>body<span class=\"token punctuation\">.</span>style<span class=\"token punctuation\">.</span>backgroundColor <span class=\"token operator\">=</span> <span class=\"token string\">'#000'</span><span class=\"token punctuation\">;</span>\n    document<span class=\"token punctuation\">.</span>body<span class=\"token punctuation\">.</span>style<span class=\"token punctuation\">.</span>color <span class=\"token operator\">=</span> <span class=\"token string\">'#fff'</span><span class=\"token punctuation\">;</span>\n<span class=\"token punctuation\">}</span> <span class=\"token keyword\">else</span> <span class=\"token punctuation\">{</span>\n    document<span class=\"token punctuation\">.</span>body<span class=\"token punctuation\">.</span><span class=\"token function\">setAttribute</span><span class=\"token punctuation\">(</span><span class=\"token string\">'data-ext-dark'</span><span class=\"token punctuation\">,</span> <span class=\"token boolean\">false</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n    document<span class=\"token punctuation\">.</span>body<span class=\"token punctuation\">.</span>style<span class=\"token punctuation\">.</span>backgroundColor <span class=\"token operator\">=</span> <span class=\"token string\">'#fff'</span><span class=\"token punctuation\">;</span>\n    document<span class=\"token punctuation\">.</span>body<span class=\"token punctuation\">.</span>style<span class=\"token punctuation\">.</span>color <span class=\"token operator\">=</span> <span class=\"token string\">'#000'</span><span class=\"token punctuation\">;</span>\n<span class=\"token punctuation\">}</span></code></pre></div><p>And in the listener we created earlier, use <code class=\"language-text\">chrome.tabs.executeScript</code> to run the code in <code class=\"language-text\">toggleDark.js</code>:</p><div class=\"kg-card kg-code-card gatsby-highlight\" data-language=\"js\"><pre class=\"language-js\"><code class=\"language-js\">chrome<span class=\"token punctuation\">.</span>browserAction<span class=\"token punctuation\">.</span>onClicked<span class=\"token punctuation\">.</span><span class=\"token function\">addListener</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">(</span><span class=\"token parameter\">tab</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">=></span> <span class=\"token punctuation\">{</span>\n    chrome<span class=\"token punctuation\">.</span>tabs<span class=\"token punctuation\">.</span><span class=\"token function\">executeScript</span><span class=\"token punctuation\">(</span>tab<span class=\"token punctuation\">.</span>id<span class=\"token punctuation\">,</span> <span class=\"token punctuation\">{</span>\n        <span class=\"token literal-property property\">file</span><span class=\"token operator\">:</span> <span class=\"token string\">'toggleDark.js'</span>\n    <span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n<span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span></code></pre></div><p><strong>Adding Permissions</strong></p><p>Now we're ready to handle when the shortcut is pressed. There's one last thing we need to do which is add permissions. Permissions are defined in <code class=\"language-text\">manifest.json</code> and include a set of permissions that the user has to allow for the extension to run.</p><p><strong>MV3</strong></p><p>If you've been following along with the <strong>MV3 </strong>instructions, then you'll need two permissions: <code class=\"language-text\">activeTab</code> to access the current tab and <code class=\"language-text\">scripting</code> to use the <code class=\"language-text\">chrome.scripting</code> API. So, add the following in <code class=\"language-text\">manifest.json</code>:</p><div class=\"kg-card kg-code-card gatsby-highlight\" data-language=\"json\"><pre class=\"language-json\"><code class=\"language-json\"><span class=\"token property\">\"permissions\"</span><span class=\"token operator\">:</span> <span class=\"token punctuation\">[</span><span class=\"token string\">\"activeTab\"</span><span class=\"token punctuation\">,</span> <span class=\"token string\">\"scripting\"</span><span class=\"token punctuation\">]</span><span class=\"token punctuation\">,</span></code></pre></div><p><strong>MV2</strong></p><p>As for <strong>MV2, </strong>we'll need the <code class=\"language-text\">activeTab</code> permission to access the current tab and <code class=\"language-text\">tabs</code> to use the <code class=\"language-text\">chrome.tabs</code> API. So, add the following in <code class=\"language-text\">manifest.json</code>:</p><div class=\"kg-card kg-code-card gatsby-highlight\" data-language=\"json\"><pre class=\"language-json\"><code class=\"language-json\"><span class=\"token property\">\"permissions\"</span><span class=\"token operator\">:</span> <span class=\"token punctuation\">[</span><span class=\"token string\">\"activeTab\"</span><span class=\"token punctuation\">,</span> <span class=\"token string\">\"tabs\"</span><span class=\"token punctuation\">]</span><span class=\"token punctuation\">,</span></code></pre></div><p><strong>Testing</strong></p><p>Let's test our code. First, go to <code class=\"language-text\">chrome://extensions</code> and refresh the extension. Then, in an open tab (if you're creating an MV2 extension you'll need to refresh the page), press <code class=\"language-text\">Ctrl+Shift+L</code>, and the document's body background color will change to black and the text to white. Remember that we're taking a simple approach to dark mode, so obviously it will not actually look good. If you keep pressing the same shortcut, it will toggle between light and dark mode.</p><figure class=\"kg-card kg-image-card\"><img src=\"https://res-1.cloudinary.com/hbqmf3mbz/image/upload/q_auto/v1/ghost-blog-images/Screen-Shot-2021-07-08-at-6.54.33-PM.png\" class=\"kg-image\" alt=\"\" loading=\"lazy\"></figure><p>If it doesn't work, make sure that <code class=\"language-text\">Ctrl+Shift+P</code> in your <code class=\"language-text\">manifest.json</code> is written correctly, as this is case-sensitive. Also, it should be noted that if other extensions that were installed previously use this shortcut, they'll have precedence to use the shortcut.  Similarly, if the shortcut is used by the browser or your operating system, then it will not be triggered. So, if the command does not work for you, try changing the shortcut in the manifest, then refresh the extension and try again.</p><hr><h3 id=\"adding-a-standard-command\">Adding a Standard Command</h3><p>Next, we'll add a standard command. The standard command we'll create will use the shortcut <code class=\"language-text\">Ctrl+Shift+P</code> to print to the console of the service worker or background script \"Hello there!\". It will run as long as Chrome is in focus, regardless of what page or tab is open.</p><p>This section is the same for both <strong>MV3 </strong>and <strong>MV2</strong>, so you can follow along regardless of what version your extension is. </p><p>In <code class=\"language-text\">manifest.json</code>, add another command under the <code class=\"language-text\">commands</code> key:</p><div class=\"kg-card kg-code-card gatsby-highlight\" data-language=\"json\"><pre class=\"language-json\"><code class=\"language-json\"><span class=\"token property\">\"commands\"</span><span class=\"token operator\">:</span> <span class=\"token punctuation\">{</span>\n\t<span class=\"token comment\">//...</span>\n    <span class=\"token property\">\"hello\"</span><span class=\"token operator\">:</span> <span class=\"token punctuation\">{</span>\n       <span class=\"token property\">\"suggested_key\"</span><span class=\"token operator\">:</span> <span class=\"token string\">\"Ctrl+Shift+P\"</span><span class=\"token punctuation\">,</span>\n       <span class=\"token property\">\"description\"</span><span class=\"token operator\">:</span> <span class=\"token string\">\"Say Hello\"</span>\n    <span class=\"token punctuation\">}</span><span class=\"token punctuation\">,</span>\n<span class=\"token punctuation\">}</span></code></pre></div><p>Then, in the service worker or background script, add a listener to the <code class=\"language-text\">chrome.commands.onCommand</code> event:</p><div class=\"kg-card kg-code-card gatsby-highlight\" data-language=\"js\"><pre class=\"language-js\"><code class=\"language-js\">chrome<span class=\"token punctuation\">.</span>commands<span class=\"token punctuation\">.</span>onCommand<span class=\"token punctuation\">.</span><span class=\"token function\">addListener</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">(</span><span class=\"token parameter\">command</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">=></span> <span class=\"token punctuation\">{</span>\n    <span class=\"token comment\">//TODO handle event</span>\n<span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span></code></pre></div><p>In the listener, we'll first check which command is triggered. This is necessary once you have more than one standard command, as they all trigger this same event.</p><p>The <code class=\"language-text\">command</code> argument passed is the name of the command. So, if the user presses <code class=\"language-text\">Ctrl+Shift+P</code>, the value for <code class=\"language-text\">command</code> will be <code class=\"language-text\">hello</code>.</p><p>If the <code class=\"language-text\">command</code> is <code class=\"language-text\">hello</code>, we'll log to the console \"Hello there!\":</p><div class=\"kg-card kg-code-card gatsby-highlight\" data-language=\"js\"><pre class=\"language-js\"><code class=\"language-js\"><span class=\"token keyword\">if</span> <span class=\"token punctuation\">(</span>command <span class=\"token operator\">===</span> <span class=\"token string\">'hello'</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n\tconsole<span class=\"token punctuation\">.</span><span class=\"token function\">log</span><span class=\"token punctuation\">(</span><span class=\"token string\">\"Hello there!\"</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n<span class=\"token punctuation\">}</span></code></pre></div><p>That's it. Go to <a href=\"chrome://extensions\">chrome://extensions</a> and refresh the extension. Then, for <strong>MV3 </strong>extensions click on \"Inspect views service worker\"</p><figure class=\"kg-card kg-image-card\"><img src=\"https://res-1.cloudinary.com/hbqmf3mbz/image/upload/q_auto/v1/ghost-blog-images/Screen-Shot-2021-07-08-at-7.10.25-PM.png\" class=\"kg-image\" alt=\"\" loading=\"lazy\"></figure><p>For <strong>MV2 </strong>extensions click on \"Inspect views background page\"</p><figure class=\"kg-card kg-image-card\"><img src=\"https://res-5.cloudinary.com/hbqmf3mbz/image/upload/q_auto/v1/ghost-blog-images/Screen-Shot-2021-07-08-at-7.10.31-PM.png\" class=\"kg-image\" alt=\"\" loading=\"lazy\"></figure><p>This will open the devtools for the service worker or background script. Click on the Console tab. Now, try clicking the shortcut <code class=\"language-text\">Ctrl+Shift+P</code> and you'll see \"Hello there!\" logged in the console.</p><figure class=\"kg-card kg-image-card\"><img src=\"https://res-3.cloudinary.com/hbqmf3mbz/image/upload/q_auto/v1/ghost-blog-images/Screen-Shot-2021-07-08-at-7.13.14-PM.png\" class=\"kg-image\" alt=\"\" loading=\"lazy\"></figure><p>If it doesn't work, make sure that <code class=\"language-text\">Ctrl+Shift+P</code> in your <code class=\"language-text\">manifest.json</code> is written correctly, as this is case-sensitive. Also, it should be noted that if other extensions that were installed previously use this shortcut, they'll have precedence to use the shortcut.  Similarly, if the shortcut is used by the browser or your operating system, then it will not be triggered. So, if the command does not work for you, try changing the shortcut in the manifest, then refresh the extension and try again.</p><hr><h3 id=\"making-a-command-global\">Making a Command Global</h3><p>In this section, we'll create a global standard command that when the user presses <code class=\"language-text\">Ctrl+Shift+8</code>, it will log to the console of the service worker or background script the current date.</p><p>This section is the same for both <strong>MV3 </strong>and <strong>MV2</strong>, so you can follow along regardless of what version your extension is.</p><p>To make a command global, we just need to add the key <code class=\"language-text\">global</code> to its definition in <code class=\"language-text\">manifest.json</code> and set it to true:</p><div class=\"kg-card kg-code-card gatsby-highlight\" data-language=\"json\"><pre class=\"language-json\"><code class=\"language-json\"><span class=\"token property\">\"date\"</span><span class=\"token operator\">:</span> <span class=\"token punctuation\">{</span>\n\t<span class=\"token property\">\"suggested_key\"</span><span class=\"token operator\">:</span> <span class=\"token string\">\"Ctrl+Shift+8\"</span><span class=\"token punctuation\">,</span>\n\t<span class=\"token property\">\"description\"</span><span class=\"token operator\">:</span> <span class=\"token string\">\"Show Date\"</span><span class=\"token punctuation\">,</span>\n    <span class=\"token property\">\"global\"</span><span class=\"token operator\">:</span> <span class=\"token boolean\">true</span>\n<span class=\"token punctuation\">}</span></code></pre></div><p>Then, in your service worker or background script, add an else block to the previous if statement that checked if the command name was \"hello\". In the else block, we'll log to the console the current date:</p><div class=\"kg-card kg-code-card gatsby-highlight\" data-language=\"js\"><pre class=\"language-js\"><code class=\"language-js\">chrome<span class=\"token punctuation\">.</span>commands<span class=\"token punctuation\">.</span>onCommand<span class=\"token punctuation\">.</span><span class=\"token function\">addListener</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">(</span><span class=\"token parameter\">command</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">=></span> <span class=\"token punctuation\">{</span>\n    <span class=\"token keyword\">if</span> <span class=\"token punctuation\">(</span>command <span class=\"token operator\">===</span> <span class=\"token string\">'hello'</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n        console<span class=\"token punctuation\">.</span><span class=\"token function\">log</span><span class=\"token punctuation\">(</span><span class=\"token string\">\"Hello there!\"</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n    <span class=\"token punctuation\">}</span> <span class=\"token keyword\">else</span> <span class=\"token punctuation\">{</span>\n        console<span class=\"token punctuation\">.</span><span class=\"token function\">log</span><span class=\"token punctuation\">(</span><span class=\"token string\">\"Date: \"</span> <span class=\"token operator\">+</span> <span class=\"token punctuation\">(</span><span class=\"token keyword\">new</span> <span class=\"token class-name\">Date</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">.</span><span class=\"token function\">toDateString</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n    <span class=\"token punctuation\">}</span>\n<span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span></code></pre></div><p>Our new command is ready. During development, I noticed that the global command, whether it's a newly added command or an older command that we added the <code class=\"language-text\">global</code> key to it, later on, the shortcut was not added or updated unless the extension was removed and re-loaded. So, make sure to do that.</p><p>Then, open the console for the service worker or background script as mentioned in the previous section. If you now change to any window that's not your Chrome browser and press <code class=\"language-text\">Ctrl+Shift+8</code>, then go back to the console for the service worker or background script, you'll see that the date is logged.</p><figure class=\"kg-card kg-image-card\"><img src=\"https://res-5.cloudinary.com/hbqmf3mbz/image/upload/q_auto/v1/ghost-blog-images/Screen-Shot-2021-07-08-at-7.19.23-PM.png\" class=\"kg-image\" alt=\"\" loading=\"lazy\"></figure><p>Again, if the command does not work, make sure that the command in the manifest is correct as it is <strong>case-sensitive</strong>. Also, if this command is used by your system for another shortcut, you'll need to change it to something else.</p><hr><h3 id=\"conclusion-next-steps\">Conclusion: Next Steps</h3><p>As you have noticed while going through this tutorial, if an extension that was added prior to yours uses the same shortcut, your shortcuts will not be registered, so they will not work. To avoid that, you can check if your shortcuts are all added on installing the extension, and if any are missing you can notify the user that they can set the shortcuts in their Chrome browser on <a href=\"chrome://extensions/shortcuts\">chrome://extensions/shortcuts</a>. For more information and an example on how to do that, check <a href=\"https://developer.chrome.com/docs/extensions/reference/commands/#verify-commands-registered\">the example on chrome.command reference</a>.</p>","htmlAst":{"type":"root","children":[{"type":"element","tagName":"p","properties":{},"children":[{"type":"text","value":"Chrome Extensions have a lot of features and functionalities that help developers create extensions and tools with great user experience."}]},{"type":"element","tagName":"p","properties":{},"children":[{"type":"text","value":"In this tutorial, we'll go over how to add keyboard shortcuts in a Chrome extension. This tutorial will cover how it can be done for both Manifest Versions 2 (MV2) and 3 (MV3). "}]},{"type":"element","tagName":"p","properties":{},"children":[{"type":"element","tagName":"em","properties":{},"children":[{"type":"text","value":"Suggested Read: "},{"type":"element","tagName":"a","properties":{"href":"https://blog.shahednasser.com/chrome-extension-tutorial-migrating-to-manifest-v3-from-v2"},"children":[{"type":"text","value":"Chrome Extension Tutorial: Migrating to Manifest V3 from V2"}]}]}]},{"type":"element","tagName":"p","properties":{},"children":[{"type":"text","value":"You can find the full code for this tutorial on "},{"type":"element","tagName":"a","properties":{"href":"https://github.com/shahednasser/chrome-commands-tutorial"},"children":[{"type":"text","value":"this GitHub repository"}]},{"type":"text","value":"."}]},{"type":"element","tagName":"hr","properties":{},"children":[]},{"type":"element","tagName":"h3","properties":{"id":"creating-the-extension"},"children":[{"type":"text","value":"Creating The Extension"}]},{"type":"element","tagName":"p","properties":{},"children":[{"type":"text","value":"In case you already have an extension ready, you can skip this step. We'll quickly go over how to create a Chrome extension."}]},{"type":"element","tagName":"p","properties":{},"children":[{"type":"element","tagName":"em","properties":{},"children":[{"type":"text","value":"Suggested Read: Learn how to create a Chrome extension in the tutorial "},{"type":"element","tagName":"a","properties":{"href":"https://blog.shahednasser.com/chrome-extension-tutorial-replace-images-in-any-website-with-pikachu"},"children":[{"type":"text","value":"Chrome Extension Tutorial — Replace Images in Any Website with Pikachu"}]},{"type":"text","value":"."}]}]},{"type":"element","tagName":"p","properties":{},"children":[{"type":"text","value":"To create a Chrome extension, first, create a directory that will hold all the extension files in it:"}]},{"type":"element","tagName":"div","properties":{"className":["kg-card","kg-code-card","gatsby-highlight"],"dataLanguage":"bash"},"children":[{"type":"element","tagName":"pre","properties":{"className":["language-bash"]},"children":[{"type":"element","tagName":"code","properties":{"className":["language-bash"]},"children":[{"type":"element","tagName":"span","properties":{"className":["token","function"]},"children":[{"type":"text","value":"mkdir"}]},{"type":"text","value":" new-extension\n"},{"type":"element","tagName":"span","properties":{"className":["token","builtin","class-name"]},"children":[{"type":"text","value":"cd"}]},{"type":"text","value":" new-extension"}]}]}]},{"type":"element","tagName":"p","properties":{},"children":[{"type":"text","value":"Then, create a "},{"type":"element","tagName":"code","properties":{"className":["language-text"]},"children":[{"type":"text","value":"manifest.json"}]},{"type":"text","value":" file with the following content:"}]},{"type":"element","tagName":"div","properties":{"className":["kg-card","kg-code-card","gatsby-highlight"],"dataLanguage":"json"},"children":[{"type":"element","tagName":"pre","properties":{"className":["language-json"]},"children":[{"type":"element","tagName":"code","properties":{"className":["language-json"]},"children":[{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"{"}]},{"type":"text","value":"\n    "},{"type":"element","tagName":"span","properties":{"className":["token","property"]},"children":[{"type":"text","value":"\"name\""}]},{"type":"element","tagName":"span","properties":{"className":["token","operator"]},"children":[{"type":"text","value":":"}]},{"type":"text","value":" "},{"type":"element","tagName":"span","properties":{"className":["token","string"]},"children":[{"type":"text","value":"\"Commands\""}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":","}]},{"type":"text","value":"\n    "},{"type":"element","tagName":"span","properties":{"className":["token","property"]},"children":[{"type":"text","value":"\"version\""}]},{"type":"element","tagName":"span","properties":{"className":["token","operator"]},"children":[{"type":"text","value":":"}]},{"type":"text","value":" "},{"type":"element","tagName":"span","properties":{"className":["token","string"]},"children":[{"type":"text","value":"\"0.0.1\""}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":","}]},{"type":"text","value":"\n    "},{"type":"element","tagName":"span","properties":{"className":["token","property"]},"children":[{"type":"text","value":"\"description\""}]},{"type":"element","tagName":"span","properties":{"className":["token","operator"]},"children":[{"type":"text","value":":"}]},{"type":"text","value":" "},{"type":"element","tagName":"span","properties":{"className":["token","string"]},"children":[{"type":"text","value":"\"Assign Keyboard Commands\""}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":","}]},{"type":"text","value":"\n    "},{"type":"element","tagName":"span","properties":{"className":["token","property"]},"children":[{"type":"text","value":"\"manifest_version\""}]},{"type":"element","tagName":"span","properties":{"className":["token","operator"]},"children":[{"type":"text","value":":"}]},{"type":"text","value":" "},{"type":"element","tagName":"span","properties":{"className":["token","number"]},"children":[{"type":"text","value":"3"}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":","}]},{"type":"text","value":"\n    "},{"type":"element","tagName":"span","properties":{"className":["token","property"]},"children":[{"type":"text","value":"\"icons\""}]},{"type":"element","tagName":"span","properties":{"className":["token","operator"]},"children":[{"type":"text","value":":"}]},{"type":"text","value":" "},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"{"}]},{"type":"text","value":"\n        "},{"type":"element","tagName":"span","properties":{"className":["token","property"]},"children":[{"type":"text","value":"\"16\""}]},{"type":"element","tagName":"span","properties":{"className":["token","operator"]},"children":[{"type":"text","value":":"}]},{"type":"text","value":" "},{"type":"element","tagName":"span","properties":{"className":["token","string"]},"children":[{"type":"text","value":"\"/assets/icon-16.png\""}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":","}]},{"type":"text","value":"\n        "},{"type":"element","tagName":"span","properties":{"className":["token","property"]},"children":[{"type":"text","value":"\"32\""}]},{"type":"element","tagName":"span","properties":{"className":["token","operator"]},"children":[{"type":"text","value":":"}]},{"type":"text","value":" "},{"type":"element","tagName":"span","properties":{"className":["token","string"]},"children":[{"type":"text","value":"\"/assets/icon-32.png\""}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":","}]},{"type":"text","value":"\n        "},{"type":"element","tagName":"span","properties":{"className":["token","property"]},"children":[{"type":"text","value":"\"48\""}]},{"type":"element","tagName":"span","properties":{"className":["token","operator"]},"children":[{"type":"text","value":":"}]},{"type":"text","value":" "},{"type":"element","tagName":"span","properties":{"className":["token","string"]},"children":[{"type":"text","value":"\"/assets/icon-48.png\""}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":","}]},{"type":"text","value":"\n        "},{"type":"element","tagName":"span","properties":{"className":["token","property"]},"children":[{"type":"text","value":"\"128\""}]},{"type":"element","tagName":"span","properties":{"className":["token","operator"]},"children":[{"type":"text","value":":"}]},{"type":"text","value":" "},{"type":"element","tagName":"span","properties":{"className":["token","string"]},"children":[{"type":"text","value":"\"/assets/icon-128.png\""}]},{"type":"text","value":"\n    "},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"}"}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":","}]},{"type":"text","value":"\n"},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"}"}]}]}]}]},{"type":"element","tagName":"p","properties":{},"children":[{"type":"text","value":"If you're creating a "},{"type":"element","tagName":"strong","properties":{},"children":[{"type":"text","value":"Manifest V2"}]},{"type":"text","value":" extension, make sure to change the manifest version:"}]},{"type":"element","tagName":"div","properties":{"className":["kg-card","kg-code-card","gatsby-highlight"],"dataLanguage":"json"},"children":[{"type":"element","tagName":"pre","properties":{"className":["language-json"]},"children":[{"type":"element","tagName":"code","properties":{"className":["language-json"]},"children":[{"type":"element","tagName":"span","properties":{"className":["token","property"]},"children":[{"type":"text","value":"\"manifest_version\""}]},{"type":"element","tagName":"span","properties":{"className":["token","operator"]},"children":[{"type":"text","value":":"}]},{"type":"text","value":" "},{"type":"element","tagName":"span","properties":{"className":["token","number"]},"children":[{"type":"text","value":"2"}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":","}]}]}]}]},{"type":"element","tagName":"p","properties":{},"children":[{"type":"text","value":"Also, make sure to download the icons from the GitHub repository first, or add your own icons."}]},{"type":"element","tagName":"p","properties":{},"children":[{"type":"text","value":"Then, go to "},{"type":"element","tagName":"a","properties":{"href":"chrome://extensions/"},"children":[{"type":"text","value":"chrome://extensions/"}]},{"type":"text","value":" on your Chrome browser, enable Developer Mode if you haven't from the top right, click on \"Load unpacked\" and choose the folder that you created for your extension. Once done, the extension will be added to your Chrome browser."}]},{"type":"element","tagName":"hr","properties":{},"children":[]},{"type":"element","tagName":"h3","properties":{"id":"what-are-chrome-commands"},"children":[{"type":"text","value":"What Are Chrome Commands"}]},{"type":"element","tagName":"p","properties":{},"children":[{"type":"text","value":"Chrome commands allow an extension to register commands, or keyboard shortcuts, to provide ease of use for the users of that extension to perform certain actions through those shortcuts. An extension can have at most 4 commands."}]},{"type":"element","tagName":"p","properties":{},"children":[{"type":"text","value":"There are two kinds of commands. A standard command and an action command. A standard command runs in the background script or service worker and has no access to the current page or tab opened. Standard commands trigger the event "},{"type":"element","tagName":"code","properties":{"className":["language-text"]},"children":[{"type":"text","value":"chrome.commands.onCommand"}]},{"type":"text","value":", and you can listen to it in your background script or service worker."}]},{"type":"element","tagName":"p","properties":{},"children":[{"type":"text","value":"Action commands map the shortcuts to your extension's action (if "},{"type":"element","tagName":"strong","properties":{},"children":[{"type":"text","value":"MV3"}]},{"type":"text","value":") or browser action or page action (if "},{"type":"element","tagName":"strong","properties":{},"children":[{"type":"text","value":"MV2"}]},{"type":"text","value":"). Action commands can have access to the current tab and can execute a script on the current page. Action commands "},{"type":"element","tagName":"strong","properties":{},"children":[{"type":"text","value":"do not "}]},{"type":"text","value":"trigger the event "},{"type":"element","tagName":"code","properties":{"className":["language-text"]},"children":[{"type":"text","value":"chrome.commands.onCommand"}]},{"type":"text","value":". Instead, in "},{"type":"element","tagName":"strong","properties":{},"children":[{"type":"text","value":"MV3"}]},{"type":"text","value":" they trigger "},{"type":"element","tagName":"code","properties":{"className":["language-text"]},"children":[{"type":"text","value":"chrome.action.onClicked"}]},{"type":"text","value":" , and in "},{"type":"element","tagName":"strong","properties":{},"children":[{"type":"text","value":"MV2"}]},{"type":"text","value":" they trigger "},{"type":"element","tagName":"code","properties":{"className":["language-text"]},"children":[{"type":"text","value":"chrome.browserAction.onClicked"}]},{"type":"text","value":" or "},{"type":"element","tagName":"code","properties":{"className":["language-text"]},"children":[{"type":"text","value":"chrome.pageAction.onClicked"}]},{"type":"text","value":"."}]},{"type":"element","tagName":"p","properties":{},"children":[{"type":"text","value":"Commands are defined in an extension's "},{"type":"element","tagName":"code","properties":{"className":["language-text"]},"children":[{"type":"text","value":"manifest.json"}]},{"type":"text","value":" under the "},{"type":"element","tagName":"code","properties":{"className":["language-text"]},"children":[{"type":"text","value":"commands"}]},{"type":"text","value":" key. Standard commands are defined in the following format:"}]},{"type":"element","tagName":"div","properties":{"className":["kg-card","kg-code-card","gatsby-highlight"],"dataLanguage":"json"},"children":[{"type":"element","tagName":"pre","properties":{"className":["language-json"]},"children":[{"type":"element","tagName":"code","properties":{"className":["language-json"]},"children":[{"type":"element","tagName":"span","properties":{"className":["token","property"]},"children":[{"type":"text","value":"\"commands\""}]},{"type":"element","tagName":"span","properties":{"className":["token","operator"]},"children":[{"type":"text","value":":"}]},{"type":"text","value":" "},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"{"}]},{"type":"text","value":"\n\t"},{"type":"element","tagName":"span","properties":{"className":["token","property"]},"children":[{"type":"text","value":"\"firstCommand\""}]},{"type":"element","tagName":"span","properties":{"className":["token","operator"]},"children":[{"type":"text","value":":"}]},{"type":"text","value":" "},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"{"}]},{"type":"text","value":"\n    \t"},{"type":"element","tagName":"span","properties":{"className":["token","property"]},"children":[{"type":"text","value":"\"suggested_key\""}]},{"type":"element","tagName":"span","properties":{"className":["token","operator"]},"children":[{"type":"text","value":":"}]},{"type":"text","value":" "},{"type":"element","tagName":"span","properties":{"className":["token","string"]},"children":[{"type":"text","value":"\"CTRL+F\""}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":","}]},{"type":"text","value":"\n        "},{"type":"element","tagName":"span","properties":{"className":["token","property"]},"children":[{"type":"text","value":"\"description\""}]},{"type":"element","tagName":"span","properties":{"className":["token","operator"]},"children":[{"type":"text","value":":"}]},{"type":"text","value":" "},{"type":"element","tagName":"span","properties":{"className":["token","string"]},"children":[{"type":"text","value":"\"My first command\""}]},{"type":"text","value":"\n    "},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"}"}]},{"type":"text","value":"\n"},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"}"}]}]}]}]},{"type":"element","tagName":"p","properties":{},"children":[{"type":"text","value":"Where "},{"type":"element","tagName":"code","properties":{"className":["language-text"]},"children":[{"type":"text","value":"suggested_key"}]},{"type":"text","value":" can be an object or a string (we'll get to that in a bit) and "},{"type":"element","tagName":"code","properties":{"className":["language-text"]},"children":[{"type":"text","value":"description"}]},{"type":"text","value":" is required."}]},{"type":"element","tagName":"p","properties":{},"children":[{"type":"text","value":"Action commands are defined in the same way but under the "},{"type":"element","tagName":"code","properties":{"className":["language-text"]},"children":[{"type":"text","value":"_execute_action"}]},{"type":"text","value":" key in MV3, or "},{"type":"element","tagName":"code","properties":{"className":["language-text"]},"children":[{"type":"text","value":"_execute_browser_action"}]},{"type":"text","value":" or "},{"type":"element","tagName":"code","properties":{"className":["language-text"]},"children":[{"type":"text","value":"_execute_page_action"}]},{"type":"text","value":" keys in MV2:"}]},{"type":"element","tagName":"div","properties":{"className":["kg-card","kg-code-card","gatsby-highlight"],"dataLanguage":"json"},"children":[{"type":"element","tagName":"pre","properties":{"className":["language-json"]},"children":[{"type":"element","tagName":"code","properties":{"className":["language-json"]},"children":[{"type":"element","tagName":"span","properties":{"className":["token","property"]},"children":[{"type":"text","value":"\"commands\""}]},{"type":"element","tagName":"span","properties":{"className":["token","operator"]},"children":[{"type":"text","value":":"}]},{"type":"text","value":" "},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"{"}]},{"type":"text","value":"\n\t"},{"type":"element","tagName":"span","properties":{"className":["token","property"]},"children":[{"type":"text","value":"\"_execute_action\""}]},{"type":"element","tagName":"span","properties":{"className":["token","operator"]},"children":[{"type":"text","value":":"}]},{"type":"text","value":" "},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"{"}]},{"type":"text","value":"\n    \t"},{"type":"element","tagName":"span","properties":{"className":["token","property"]},"children":[{"type":"text","value":"\"suggested_key\""}]},{"type":"element","tagName":"span","properties":{"className":["token","operator"]},"children":[{"type":"text","value":":"}]},{"type":"text","value":" "},{"type":"element","tagName":"span","properties":{"className":["token","string"]},"children":[{"type":"text","value":"\"CTRL+F\""}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":","}]},{"type":"text","value":"\n        "},{"type":"element","tagName":"span","properties":{"className":["token","property"]},"children":[{"type":"text","value":"\"description\""}]},{"type":"element","tagName":"span","properties":{"className":["token","operator"]},"children":[{"type":"text","value":":"}]},{"type":"text","value":" "},{"type":"element","tagName":"span","properties":{"className":["token","string"]},"children":[{"type":"text","value":"\"My first command\""}]},{"type":"text","value":"\n    "},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"}"}]},{"type":"text","value":"\n"},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"}"}]}]}]}]},{"type":"element","tagName":"p","properties":{},"children":[{"type":"element","tagName":"code","properties":{"className":["language-text"]},"children":[{"type":"text","value":"description"}]},{"type":"text","value":" for action commands is optional."}]},{"type":"element","tagName":"h3","properties":{"id":"suggested-key"},"children":[{"type":"text","value":"Suggested Key"}]},{"type":"element","tagName":"p","properties":{},"children":[{"type":"text","value":"Some of the keys that are allowed to be used are: "},{"type":"element","tagName":"code","properties":{"className":["language-text"]},"children":[{"type":"text","value":"Ctrl"}]},{"type":"text","value":", "},{"type":"element","tagName":"code","properties":{"className":["language-text"]},"children":[{"type":"text","value":"Alt"}]},{"type":"text","value":", "},{"type":"element","tagName":"code","properties":{"className":["language-text"]},"children":[{"type":"text","value":"Shift"}]},{"type":"text","value":", "},{"type":"element","tagName":"code","properties":{"className":["language-text"]},"children":[{"type":"text","value":"0...9"}]},{"type":"text","value":", and "},{"type":"element","tagName":"code","properties":{"className":["language-text"]},"children":[{"type":"text","value":"A...Z"}]},{"type":"text","value":". For a full list of keys, you can see them in the "},{"type":"element","tagName":"a","properties":{"href":"https://developer.chrome.com/docs/extensions/reference/commands/#supported-keys"},"children":[{"type":"text","value":"chrome.commands reference"}]},{"type":"text","value":"."}]},{"type":"element","tagName":"p","properties":{},"children":[{"type":"element","tagName":"code","properties":{"className":["language-text"]},"children":[{"type":"text","value":"suggested_key"}]},{"type":"text","value":" can either be a string or an object. If a string, then the same shortcut will be applied for all Operating Systems. In this case, certain keys will be replaced with their equivalent on the Operating Systems. This means that "},{"type":"element","tagName":"code","properties":{"className":["language-text"]},"children":[{"type":"text","value":"Ctrl"}]},{"type":"text","value":" on Mac OS will be substituted by "},{"type":"element","tagName":"code","properties":{"className":["language-text"]},"children":[{"type":"text","value":"Command"}]},{"type":"text","value":"."}]},{"type":"element","tagName":"p","properties":{},"children":[{"type":"text","value":"To specify the shortcut for an operating system, you should pass an object where the key is the operating system's name. The allowed keys are "},{"type":"element","tagName":"code","properties":{"className":["language-text"]},"children":[{"type":"text","value":"default"}]},{"type":"text","value":" which applies the shortcut to all operating systems not listed in the object, "},{"type":"element","tagName":"code","properties":{"className":["language-text"]},"children":[{"type":"text","value":"chromeos"}]},{"type":"text","value":", "},{"type":"element","tagName":"code","properties":{"className":["language-text"]},"children":[{"type":"text","value":"linux"}]},{"type":"text","value":", "},{"type":"element","tagName":"code","properties":{"className":["language-text"]},"children":[{"type":"text","value":"mac"}]},{"type":"text","value":", and "},{"type":"element","tagName":"code","properties":{"className":["language-text"]},"children":[{"type":"text","value":"windows"}]},{"type":"text","value":"."}]},{"type":"element","tagName":"p","properties":{},"children":[{"type":"text","value":"If you want to use "},{"type":"element","tagName":"code","properties":{"className":["language-text"]},"children":[{"type":"text","value":"Ctrl"}]},{"type":"text","value":" on Mac instead of "},{"type":"element","tagName":"code","properties":{"className":["language-text"]},"children":[{"type":"text","value":"Command"}]},{"type":"text","value":", you can use "},{"type":"element","tagName":"code","properties":{"className":["language-text"]},"children":[{"type":"text","value":"MacCtrl"}]},{"type":"text","value":" for the value of "},{"type":"element","tagName":"code","properties":{"className":["language-text"]},"children":[{"type":"text","value":"mac"}]},{"type":"text","value":" in the object:"}]},{"type":"element","tagName":"div","properties":{"className":["kg-card","kg-code-card","gatsby-highlight"],"dataLanguage":"json"},"children":[{"type":"element","tagName":"pre","properties":{"className":["language-json"]},"children":[{"type":"element","tagName":"code","properties":{"className":["language-json"]},"children":[{"type":"element","tagName":"span","properties":{"className":["token","property"]},"children":[{"type":"text","value":"\"suggested_key\""}]},{"type":"element","tagName":"span","properties":{"className":["token","operator"]},"children":[{"type":"text","value":":"}]},{"type":"text","value":" "},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"{"}]},{"type":"text","value":"\n\t"},{"type":"element","tagName":"span","properties":{"className":["token","property"]},"children":[{"type":"text","value":"\"default\""}]},{"type":"element","tagName":"span","properties":{"className":["token","operator"]},"children":[{"type":"text","value":":"}]},{"type":"text","value":" "},{"type":"element","tagName":"span","properties":{"className":["token","string"]},"children":[{"type":"text","value":"\"Ctrl+F\""}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":","}]},{"type":"text","value":"\n    "},{"type":"element","tagName":"span","properties":{"className":["token","property"]},"children":[{"type":"text","value":"\"mac\""}]},{"type":"element","tagName":"span","properties":{"className":["token","operator"]},"children":[{"type":"text","value":":"}]},{"type":"text","value":" "},{"type":"element","tagName":"span","properties":{"className":["token","string"]},"children":[{"type":"text","value":"\"MacCtrl+F\""}]},{"type":"text","value":"\n"},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"}"}]}]}]}]},{"type":"element","tagName":"p","properties":{},"children":[{"type":"text","value":"This will make the shortcut "},{"type":"element","tagName":"code","properties":{"className":["language-text"]},"children":[{"type":"text","value":"Ctrl+F"}]},{"type":"text","value":" for all operating systems, but for Mac instead of the conventional "},{"type":"element","tagName":"code","properties":{"className":["language-text"]},"children":[{"type":"text","value":"Command"}]},{"type":"text","value":" substitute, it will use the "},{"type":"element","tagName":"code","properties":{"className":["language-text"]},"children":[{"type":"text","value":"Ctrl"}]},{"type":"text","value":" key on Mac."}]},{"type":"element","tagName":"p","properties":{},"children":[{"type":"text","value":"It should be noted that the value for the keyboard shortcuts is "},{"type":"element","tagName":"strong","properties":{},"children":[{"type":"text","value":"case-sensitive"}]},{"type":"text","value":". If you set the value to "},{"type":"element","tagName":"code","properties":{"className":["language-text"]},"children":[{"type":"text","value":"CTRL+F"}]},{"type":"text","value":" instead of "},{"type":"element","tagName":"code","properties":{"className":["language-text"]},"children":[{"type":"text","value":"Ctrl+F"}]},{"type":"text","value":", the shortcut will not be registered."}]},{"type":"element","tagName":"h3","properties":{"id":"global-shortcuts"},"children":[{"type":"text","value":"Global Shortcuts"}]},{"type":"element","tagName":"p","properties":{},"children":[{"type":"text","value":"By default, keyboard shortcuts are executed only when the Chrome browser is in focus. To allow shortcuts to work even when Chrome is in the background, you can make the command \"global\" by adding the "},{"type":"element","tagName":"code","properties":{"className":["language-text"]},"children":[{"type":"text","value":"global"}]},{"type":"text","value":" key in the definition:"}]},{"type":"element","tagName":"div","properties":{"className":["kg-card","kg-code-card","gatsby-highlight"],"dataLanguage":"json"},"children":[{"type":"element","tagName":"pre","properties":{"className":["language-json"]},"children":[{"type":"element","tagName":"code","properties":{"className":["language-json"]},"children":[{"type":"element","tagName":"span","properties":{"className":["token","property"]},"children":[{"type":"text","value":"\"commands\""}]},{"type":"element","tagName":"span","properties":{"className":["token","operator"]},"children":[{"type":"text","value":":"}]},{"type":"text","value":" "},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"{"}]},{"type":"text","value":"\n\t"},{"type":"element","tagName":"span","properties":{"className":["token","property"]},"children":[{"type":"text","value":"\"first\""}]},{"type":"element","tagName":"span","properties":{"className":["token","operator"]},"children":[{"type":"text","value":":"}]},{"type":"text","value":" "},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"{"}]},{"type":"text","value":"\n    \t"},{"type":"element","tagName":"span","properties":{"className":["token","property"]},"children":[{"type":"text","value":"\"suggested_key\""}]},{"type":"element","tagName":"span","properties":{"className":["token","operator"]},"children":[{"type":"text","value":":"}]},{"type":"text","value":" "},{"type":"element","tagName":"span","properties":{"className":["token","string"]},"children":[{"type":"text","value":"\"Ctrl+Shift+F\""}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":","}]},{"type":"text","value":"\n        "},{"type":"element","tagName":"span","properties":{"className":["token","property"]},"children":[{"type":"text","value":"\"description\""}]},{"type":"element","tagName":"span","properties":{"className":["token","operator"]},"children":[{"type":"text","value":":"}]},{"type":"text","value":" "},{"type":"element","tagName":"span","properties":{"className":["token","string"]},"children":[{"type":"text","value":"\"First command\""}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":","}]},{"type":"text","value":"\n        "},{"type":"element","tagName":"span","properties":{"className":["token","property"]},"children":[{"type":"text","value":"\"global\""}]},{"type":"element","tagName":"span","properties":{"className":["token","operator"]},"children":[{"type":"text","value":":"}]},{"type":"text","value":" "},{"type":"element","tagName":"span","properties":{"className":["token","boolean"]},"children":[{"type":"text","value":"true"}]},{"type":"text","value":"\n    "},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"}"}]},{"type":"text","value":"\n"},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"}"}]}]}]}]},{"type":"element","tagName":"p","properties":{},"children":[{"type":"text","value":"Global shortcuts are very limited in the amount of keys you can use in "},{"type":"element","tagName":"code","properties":{"className":["language-text"]},"children":[{"type":"text","value":"suggested_key"}]},{"type":"text","value":" to make sure your shortcut does not overlap with a system shortcut. For that reason, the format should be "},{"type":"element","tagName":"code","properties":{"className":["language-text"]},"children":[{"type":"text","value":"Ctrl+Shift+[0...9]"}]},{"type":"text","value":"."}]},{"type":"element","tagName":"p","properties":{},"children":[{"type":"text","value":"If the shortcut is already defined and you later set it to global, upon testing it I noticed that the shortcut will not be updated. To update it, you will need to remove and reinstall the extension."}]},{"type":"element","tagName":"hr","properties":{},"children":[]},{"type":"element","tagName":"h3","properties":{"id":"adding-an-action-command"},"children":[{"type":"text","value":"Adding an Action Command"}]},{"type":"element","tagName":"p","properties":{},"children":[{"type":"text","value":"We'll first add an action command. The action command will toggle \"Dark Mode\" on the current website. We'll do it in a very basic way, which is just changing the background color to black and text color to white, as this is not the purpose of this tutorial."}]},{"type":"element","tagName":"p","properties":{},"children":[{"type":"element","tagName":"strong","properties":{},"children":[{"type":"text","value":"Define The Command"}]}]},{"type":"element","tagName":"p","properties":{},"children":[{"type":"text","value":"Let's first define the action command. It will be triggered at the press of "},{"type":"element","tagName":"code","properties":{"className":["language-text"]},"children":[{"type":"text","value":"Ctrl+Shift+L"}]},{"type":"text","value":". "}]},{"type":"element","tagName":"p","properties":{},"children":[{"type":"element","tagName":"strong","properties":{},"children":[{"type":"text","value":"MV3"}]}]},{"type":"element","tagName":"p","properties":{},"children":[{"type":"text","value":"For "},{"type":"element","tagName":"strong","properties":{},"children":[{"type":"text","value":"MV3"}]},{"type":"text","value":", the definition will be:"}]},{"type":"element","tagName":"div","properties":{"className":["kg-card","kg-code-card","gatsby-highlight"],"dataLanguage":"json"},"children":[{"type":"element","tagName":"pre","properties":{"className":["language-json"]},"children":[{"type":"element","tagName":"code","properties":{"className":["language-json"]},"children":[{"type":"element","tagName":"span","properties":{"className":["token","property"]},"children":[{"type":"text","value":"\"commands\""}]},{"type":"element","tagName":"span","properties":{"className":["token","operator"]},"children":[{"type":"text","value":":"}]},{"type":"text","value":" "},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"{"}]},{"type":"text","value":"\n\t"},{"type":"element","tagName":"span","properties":{"className":["token","property"]},"children":[{"type":"text","value":"\"_execute_action\""}]},{"type":"element","tagName":"span","properties":{"className":["token","operator"]},"children":[{"type":"text","value":":"}]},{"type":"text","value":" "},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"{"}]},{"type":"text","value":"\n\t\t"},{"type":"element","tagName":"span","properties":{"className":["token","property"]},"children":[{"type":"text","value":"\"suggested_key\""}]},{"type":"element","tagName":"span","properties":{"className":["token","operator"]},"children":[{"type":"text","value":":"}]},{"type":"text","value":" "},{"type":"element","tagName":"span","properties":{"className":["token","string"]},"children":[{"type":"text","value":"\"Ctrl+Shift+L\""}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":","}]},{"type":"text","value":"\n\t\t"},{"type":"element","tagName":"span","properties":{"className":["token","property"]},"children":[{"type":"text","value":"\"description\""}]},{"type":"element","tagName":"span","properties":{"className":["token","operator"]},"children":[{"type":"text","value":":"}]},{"type":"text","value":" "},{"type":"element","tagName":"span","properties":{"className":["token","string"]},"children":[{"type":"text","value":"\"Toggle dark body\""}]},{"type":"text","value":"\n\t"},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"}"}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":","}]},{"type":"text","value":"\n"},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"}"}]}]}]}]},{"type":"element","tagName":"p","properties":{},"children":[{"type":"text","value":"As we are adding an Action command, we need to also add an "},{"type":"element","tagName":"code","properties":{"className":["language-text"]},"children":[{"type":"text","value":"action"}]},{"type":"text","value":" key"},{"type":"element","tagName":"strong","properties":{},"children":[{"type":"text","value":":"}]}]},{"type":"element","tagName":"div","properties":{"className":["kg-card","kg-code-card","gatsby-highlight"],"dataLanguage":"json"},"children":[{"type":"element","tagName":"pre","properties":{"className":["language-json"]},"children":[{"type":"element","tagName":"code","properties":{"className":["language-json"]},"children":[{"type":"element","tagName":"span","properties":{"className":["token","property"]},"children":[{"type":"text","value":"\"action\""}]},{"type":"element","tagName":"span","properties":{"className":["token","operator"]},"children":[{"type":"text","value":":"}]},{"type":"text","value":" "},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"{"}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"}"}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":","}]}]}]}]},{"type":"element","tagName":"p","properties":{},"children":[{"type":"element","tagName":"strong","properties":{},"children":[{"type":"text","value":"MV2"}]}]},{"type":"element","tagName":"p","properties":{},"children":[{"type":"text","value":"For "},{"type":"element","tagName":"strong","properties":{},"children":[{"type":"text","value":"MV2 "}]},{"type":"text","value":"we just need to change "},{"type":"element","tagName":"code","properties":{"className":["language-text"]},"children":[{"type":"text","value":"_execute_action"}]},{"type":"text","value":" to "},{"type":"element","tagName":"code","properties":{"className":["language-text"]},"children":[{"type":"text","value":"_execute_browser_action"}]},{"type":"text","value":":"}]},{"type":"element","tagName":"div","properties":{"className":["kg-card","kg-code-card","gatsby-highlight"],"dataLanguage":"json"},"children":[{"type":"element","tagName":"pre","properties":{"className":["language-json"]},"children":[{"type":"element","tagName":"code","properties":{"className":["language-json"]},"children":[{"type":"element","tagName":"span","properties":{"className":["token","property"]},"children":[{"type":"text","value":"\"commands\""}]},{"type":"element","tagName":"span","properties":{"className":["token","operator"]},"children":[{"type":"text","value":":"}]},{"type":"text","value":" "},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"{"}]},{"type":"text","value":"\n\t"},{"type":"element","tagName":"span","properties":{"className":["token","property"]},"children":[{"type":"text","value":"\"_execute_browser_action\""}]},{"type":"element","tagName":"span","properties":{"className":["token","operator"]},"children":[{"type":"text","value":":"}]},{"type":"text","value":" "},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"{"}]},{"type":"text","value":"\n            "},{"type":"element","tagName":"span","properties":{"className":["token","property"]},"children":[{"type":"text","value":"\"suggested_key\""}]},{"type":"element","tagName":"span","properties":{"className":["token","operator"]},"children":[{"type":"text","value":":"}]},{"type":"text","value":" "},{"type":"element","tagName":"span","properties":{"className":["token","string"]},"children":[{"type":"text","value":"\"Ctrl+Shift+L\""}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":","}]},{"type":"text","value":"\n            "},{"type":"element","tagName":"span","properties":{"className":["token","property"]},"children":[{"type":"text","value":"\"description\""}]},{"type":"element","tagName":"span","properties":{"className":["token","operator"]},"children":[{"type":"text","value":":"}]},{"type":"text","value":" "},{"type":"element","tagName":"span","properties":{"className":["token","string"]},"children":[{"type":"text","value":"\"Toggle dark body\""}]},{"type":"text","value":"\n\t"},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"}"}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":","}]},{"type":"text","value":"\n"},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"}"}]}]}]}]},{"type":"element","tagName":"p","properties":{},"children":[{"type":"text","value":"and add a "},{"type":"element","tagName":"code","properties":{"className":["language-text"]},"children":[{"type":"text","value":"browser_action"}]},{"type":"text","value":" key"},{"type":"element","tagName":"strong","properties":{},"children":[{"type":"text","value":":"}]}]},{"type":"element","tagName":"div","properties":{"className":["kg-card","kg-code-card","gatsby-highlight"],"dataLanguage":"json"},"children":[{"type":"element","tagName":"pre","properties":{"className":["language-json"]},"children":[{"type":"element","tagName":"code","properties":{"className":["language-json"]},"children":[{"type":"element","tagName":"span","properties":{"className":["token","property"]},"children":[{"type":"text","value":"\"browser_action\""}]},{"type":"element","tagName":"span","properties":{"className":["token","operator"]},"children":[{"type":"text","value":":"}]},{"type":"text","value":" "},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"{"}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"}"}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":","}]}]}]}]},{"type":"element","tagName":"p","properties":{},"children":[{"type":"text","value":"This will define our command that toggles dark mode at the press of "},{"type":"element","tagName":"code","properties":{"className":["language-text"]},"children":[{"type":"text","value":"Ctrl+Shift+L"}]},{"type":"text","value":". Make sure that the "},{"type":"element","tagName":"code","properties":{"className":["language-text"]},"children":[{"type":"text","value":"suggested_key"}]},{"type":"text","value":" value is correct as it is "},{"type":"element","tagName":"strong","properties":{},"children":[{"type":"text","value":"case-sensitive"}]},{"type":"text","value":". If it's incorrect, the shortcut will not be registered."}]},{"type":"element","tagName":"p","properties":{},"children":[{"type":"element","tagName":"strong","properties":{},"children":[{"type":"text","value":"Add Event Listeners"}]}]},{"type":"element","tagName":"p","properties":{},"children":[{"type":"text","value":"Next, we need to listen to when these keys are pressed. Inside the listener, we need to execute a Javascript code that will just toggle the background color and text color of the document's body."}]},{"type":"element","tagName":"p","properties":{},"children":[{"type":"element","tagName":"strong","properties":{},"children":[{"type":"text","value":"MV3"}]}]},{"type":"element","tagName":"p","properties":{},"children":[{"type":"text","value":"To do that, first, add a service worker:"}]},{"type":"element","tagName":"div","properties":{"className":["kg-card","kg-code-card","gatsby-highlight"],"dataLanguage":"json"},"children":[{"type":"element","tagName":"pre","properties":{"className":["language-json"]},"children":[{"type":"element","tagName":"code","properties":{"className":["language-json"]},"children":[{"type":"element","tagName":"span","properties":{"className":["token","property"]},"children":[{"type":"text","value":"\"background\""}]},{"type":"element","tagName":"span","properties":{"className":["token","operator"]},"children":[{"type":"text","value":":"}]},{"type":"text","value":" "},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"{"}]},{"type":"text","value":"\n   "},{"type":"element","tagName":"span","properties":{"className":["token","property"]},"children":[{"type":"text","value":"\"service_worker\""}]},{"type":"element","tagName":"span","properties":{"className":["token","operator"]},"children":[{"type":"text","value":":"}]},{"type":"text","value":" "},{"type":"element","tagName":"span","properties":{"className":["token","string"]},"children":[{"type":"text","value":"\"background.js\""}]},{"type":"text","value":"\n"},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"}"}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":","}]}]}]}]},{"type":"element","tagName":"p","properties":{},"children":[{"type":"text","value":"Then, in the service worker or background script, we'll add a listener to handle the press of the keys. In the service worker, we'll add a listener to "},{"type":"element","tagName":"code","properties":{"className":["language-text"]},"children":[{"type":"text","value":"chrome.action.onClicked"}]},{"type":"text","value":" event:"}]},{"type":"element","tagName":"div","properties":{"className":["kg-card","kg-code-card","gatsby-highlight"],"dataLanguage":"js"},"children":[{"type":"element","tagName":"pre","properties":{"className":["language-js"]},"children":[{"type":"element","tagName":"code","properties":{"className":["language-js"]},"children":[{"type":"text","value":"chrome"},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"."}]},{"type":"text","value":"action"},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"."}]},{"type":"text","value":"onClicked"},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"."}]},{"type":"element","tagName":"span","properties":{"className":["token","function"]},"children":[{"type":"text","value":"addListener"}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"("}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"("}]},{"type":"element","tagName":"span","properties":{"className":["token","parameter"]},"children":[{"type":"text","value":"tab"}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":")"}]},{"type":"text","value":" "},{"type":"element","tagName":"span","properties":{"className":["token","operator"]},"children":[{"type":"text","value":"=>"}]},{"type":"text","value":" "},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"{"}]},{"type":"text","value":"\n    "},{"type":"element","tagName":"span","properties":{"className":["token","comment"]},"children":[{"type":"text","value":"//TODO toggle dark mode in the tab"}]},{"type":"text","value":"\n"},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"}"}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":")"}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":";"}]}]}]}]},{"type":"element","tagName":"p","properties":{},"children":[{"type":"text","value":"Inside the listener, we'll use "},{"type":"element","tagName":"a","properties":{"href":"https://developer.chrome.com/docs/extensions/reference/scripting/#method-executeScript"},"children":[{"type":"text","value":"chrome.scripting.executeScript"}]},{"type":"element","tagName":"strong","properties":{},"children":[{"type":"text","value":". "}]},{"type":"element","tagName":"code","properties":{"className":["language-text"]},"children":[{"type":"text","value":"executeScript"}]},{"type":"text","value":" accepts an object of options including "},{"type":"element","tagName":"code","properties":{"className":["language-text"]},"children":[{"type":"text","value":"tabId"}]},{"type":"text","value":" for the id of the tab to execute the script on and "},{"type":"element","tagName":"code","properties":{"className":["language-text"]},"children":[{"type":"text","value":"function"}]},{"type":"text","value":" for the Javascript function to execute. Add in the service worker the following function:"}]},{"type":"element","tagName":"div","properties":{"className":["kg-card","kg-code-card","gatsby-highlight"],"dataLanguage":"js"},"children":[{"type":"element","tagName":"pre","properties":{"className":["language-js"]},"children":[{"type":"element","tagName":"code","properties":{"className":["language-js"]},"children":[{"type":"element","tagName":"span","properties":{"className":["token","keyword"]},"children":[{"type":"text","value":"function"}]},{"type":"text","value":" "},{"type":"element","tagName":"span","properties":{"className":["token","function"]},"children":[{"type":"text","value":"toggleDark"}]},{"type":"text","value":" "},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"("}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":")"}]},{"type":"text","value":" "},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"{"}]},{"type":"text","value":"\n    "},{"type":"element","tagName":"span","properties":{"className":["token","keyword"]},"children":[{"type":"text","value":"if"}]},{"type":"text","value":" "},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"("}]},{"type":"element","tagName":"span","properties":{"className":["token","operator"]},"children":[{"type":"text","value":"!"}]},{"type":"text","value":"document"},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"."}]},{"type":"text","value":"body"},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"."}]},{"type":"element","tagName":"span","properties":{"className":["token","function"]},"children":[{"type":"text","value":"getAttribute"}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"("}]},{"type":"element","tagName":"span","properties":{"className":["token","string"]},"children":[{"type":"text","value":"'data-ext-dark'"}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":")"}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":")"}]},{"type":"text","value":" "},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"{"}]},{"type":"text","value":"\n        document"},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"."}]},{"type":"text","value":"body"},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"."}]},{"type":"element","tagName":"span","properties":{"className":["token","function"]},"children":[{"type":"text","value":"setAttribute"}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"("}]},{"type":"element","tagName":"span","properties":{"className":["token","string"]},"children":[{"type":"text","value":"'data-ext-dark'"}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":","}]},{"type":"text","value":" "},{"type":"element","tagName":"span","properties":{"className":["token","boolean"]},"children":[{"type":"text","value":"true"}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":")"}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":";"}]},{"type":"text","value":"\n        document"},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"."}]},{"type":"text","value":"body"},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"."}]},{"type":"text","value":"style"},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"."}]},{"type":"text","value":"backgroundColor "},{"type":"element","tagName":"span","properties":{"className":["token","operator"]},"children":[{"type":"text","value":"="}]},{"type":"text","value":" "},{"type":"element","tagName":"span","properties":{"className":["token","string"]},"children":[{"type":"text","value":"'#000'"}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":";"}]},{"type":"text","value":"\n        document"},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"."}]},{"type":"text","value":"body"},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"."}]},{"type":"text","value":"style"},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"."}]},{"type":"text","value":"color "},{"type":"element","tagName":"span","properties":{"className":["token","operator"]},"children":[{"type":"text","value":"="}]},{"type":"text","value":" "},{"type":"element","tagName":"span","properties":{"className":["token","string"]},"children":[{"type":"text","value":"'#fff'"}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":";"}]},{"type":"text","value":"\n    "},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"}"}]},{"type":"text","value":" "},{"type":"element","tagName":"span","properties":{"className":["token","keyword"]},"children":[{"type":"text","value":"else"}]},{"type":"text","value":" "},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"{"}]},{"type":"text","value":"\n        document"},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"."}]},{"type":"text","value":"body"},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"."}]},{"type":"element","tagName":"span","properties":{"className":["token","function"]},"children":[{"type":"text","value":"setAttribute"}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"("}]},{"type":"element","tagName":"span","properties":{"className":["token","string"]},"children":[{"type":"text","value":"'data-ext-dark'"}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":","}]},{"type":"text","value":" "},{"type":"element","tagName":"span","properties":{"className":["token","boolean"]},"children":[{"type":"text","value":"false"}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":")"}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":";"}]},{"type":"text","value":"\n        document"},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"."}]},{"type":"text","value":"body"},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"."}]},{"type":"text","value":"style"},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"."}]},{"type":"text","value":"backgroundColor "},{"type":"element","tagName":"span","properties":{"className":["token","operator"]},"children":[{"type":"text","value":"="}]},{"type":"text","value":" "},{"type":"element","tagName":"span","properties":{"className":["token","string"]},"children":[{"type":"text","value":"'#fff'"}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":";"}]},{"type":"text","value":"\n        document"},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"."}]},{"type":"text","value":"body"},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"."}]},{"type":"text","value":"style"},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"."}]},{"type":"text","value":"color "},{"type":"element","tagName":"span","properties":{"className":["token","operator"]},"children":[{"type":"text","value":"="}]},{"type":"text","value":" "},{"type":"element","tagName":"span","properties":{"className":["token","string"]},"children":[{"type":"text","value":"'#000'"}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":";"}]},{"type":"text","value":"\n    "},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"}"}]},{"type":"text","value":"\n"},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"}"}]}]}]}]},{"type":"element","tagName":"p","properties":{},"children":[{"type":"text","value":"Then, inside the listener we created earlier, we'll use "},{"type":"element","tagName":"code","properties":{"className":["language-text"]},"children":[{"type":"text","value":"executeScript"}]},{"type":"text","value":" to execute the function on the current tab:"}]},{"type":"element","tagName":"div","properties":{"className":["kg-card","kg-code-card","gatsby-highlight"],"dataLanguage":"js"},"children":[{"type":"element","tagName":"pre","properties":{"className":["language-js"]},"children":[{"type":"element","tagName":"code","properties":{"className":["language-js"]},"children":[{"type":"text","value":"chrome"},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"."}]},{"type":"text","value":"scripting"},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"."}]},{"type":"element","tagName":"span","properties":{"className":["token","function"]},"children":[{"type":"text","value":"executeScript"}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"("}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"{"}]},{"type":"text","value":"\n    "},{"type":"element","tagName":"span","properties":{"className":["token","literal-property","property"]},"children":[{"type":"text","value":"target"}]},{"type":"element","tagName":"span","properties":{"className":["token","operator"]},"children":[{"type":"text","value":":"}]},{"type":"text","value":" "},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"{"}]},{"type":"element","tagName":"span","properties":{"className":["token","literal-property","property"]},"children":[{"type":"text","value":"tabId"}]},{"type":"element","tagName":"span","properties":{"className":["token","operator"]},"children":[{"type":"text","value":":"}]},{"type":"text","value":" tab"},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"."}]},{"type":"text","value":"id"},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"}"}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":","}]},{"type":"text","value":"\n    "},{"type":"element","tagName":"span","properties":{"className":["token","keyword"]},"children":[{"type":"text","value":"function"}]},{"type":"element","tagName":"span","properties":{"className":["token","operator"]},"children":[{"type":"text","value":":"}]},{"type":"text","value":" toggleDark\n"},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"}"}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":")"}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":";"}]}]}]}]},{"type":"element","tagName":"p","properties":{},"children":[{"type":"element","tagName":"strong","properties":{},"children":[{"type":"text","value":"MV2"}]}]},{"type":"element","tagName":"p","properties":{},"children":[{"type":"text","value":"If you're creating an "},{"type":"element","tagName":"strong","properties":{},"children":[{"type":"text","value":"MV2 "}]},{"type":"text","value":"extension, you need to add a background script instead:"}]},{"type":"element","tagName":"div","properties":{"className":["kg-card","kg-code-card","gatsby-highlight"],"dataLanguage":"json"},"children":[{"type":"element","tagName":"pre","properties":{"className":["language-json"]},"children":[{"type":"element","tagName":"code","properties":{"className":["language-json"]},"children":[{"type":"element","tagName":"span","properties":{"className":["token","property"]},"children":[{"type":"text","value":"\"background\""}]},{"type":"element","tagName":"span","properties":{"className":["token","operator"]},"children":[{"type":"text","value":":"}]},{"type":"text","value":" "},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"{"}]},{"type":"text","value":"\n   "},{"type":"element","tagName":"span","properties":{"className":["token","property"]},"children":[{"type":"text","value":"\"scripts\""}]},{"type":"element","tagName":"span","properties":{"className":["token","operator"]},"children":[{"type":"text","value":":"}]},{"type":"text","value":" "},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"["}]},{"type":"element","tagName":"span","properties":{"className":["token","string"]},"children":[{"type":"text","value":"\"background.js\""}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"]"}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":","}]},{"type":"text","value":"\n   "},{"type":"element","tagName":"span","properties":{"className":["token","property"]},"children":[{"type":"text","value":"\"persistent\""}]},{"type":"element","tagName":"span","properties":{"className":["token","operator"]},"children":[{"type":"text","value":":"}]},{"type":"text","value":" "},{"type":"element","tagName":"span","properties":{"className":["token","boolean"]},"children":[{"type":"text","value":"false"}]},{"type":"text","value":"\n "},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"}"}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":","}]}]}]}]},{"type":"element","tagName":"p","properties":{},"children":[{"type":"text","value":"In  the background script, we'll add a listener to "},{"type":"element","tagName":"code","properties":{"className":["language-text"]},"children":[{"type":"text","value":"chrome.browserAction.onClicked"}]},{"type":"text","value":" event:"}]},{"type":"element","tagName":"div","properties":{"className":["kg-card","kg-code-card","gatsby-highlight"],"dataLanguage":"js"},"children":[{"type":"element","tagName":"pre","properties":{"className":["language-js"]},"children":[{"type":"element","tagName":"code","properties":{"className":["language-js"]},"children":[{"type":"text","value":"chrome"},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"."}]},{"type":"text","value":"browserAction"},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"."}]},{"type":"text","value":"onClicked"},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"."}]},{"type":"element","tagName":"span","properties":{"className":["token","function"]},"children":[{"type":"text","value":"addListener"}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"("}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"("}]},{"type":"element","tagName":"span","properties":{"className":["token","parameter"]},"children":[{"type":"text","value":"tab"}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":")"}]},{"type":"text","value":" "},{"type":"element","tagName":"span","properties":{"className":["token","operator"]},"children":[{"type":"text","value":"=>"}]},{"type":"text","value":" "},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"{"}]},{"type":"text","value":"\n    "},{"type":"element","tagName":"span","properties":{"className":["token","comment"]},"children":[{"type":"text","value":"//TODO toggle dark mode in the tab"}]},{"type":"text","value":"\n"},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"}"}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":")"}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":";"}]}]}]}]},{"type":"element","tagName":"p","properties":{},"children":[{"type":"element","tagName":"code","properties":{"className":["language-text"]},"children":[{"type":"text","value":"chrome.scripting"}]},{"type":"text","value":" is not compatible with "},{"type":"element","tagName":"strong","properties":{},"children":[{"type":"text","value":"MV2"}]},{"type":"text","value":". Instead, we'll use "},{"type":"element","tagName":"a","properties":{"href":"https://developer.chrome.com/docs/extensions/reference/tabs/#method-executeScript"},"children":[{"type":"text","value":"chrome.tabs.executeScript"}]},{"type":"text","value":".  This method accepts 2 arguments, the first one the id of the tab to execute the script on, and the second is an object of options including the "},{"type":"element","tagName":"code","properties":{"className":["language-text"]},"children":[{"type":"text","value":"file"}]},{"type":"text","value":" option which allows you to specify a Javascript file to inject into the page and execute. So, create a new file called "},{"type":"element","tagName":"code","properties":{"className":["language-text"]},"children":[{"type":"text","value":"toggleDark.js"}]},{"type":"text","value":" in the root of the extension with the following content:"}]},{"type":"element","tagName":"div","properties":{"className":["kg-card","kg-code-card","gatsby-highlight"],"dataLanguage":"js"},"children":[{"type":"element","tagName":"pre","properties":{"className":["language-js"]},"children":[{"type":"element","tagName":"code","properties":{"className":["language-js"]},"children":[{"type":"element","tagName":"span","properties":{"className":["token","keyword"]},"children":[{"type":"text","value":"if"}]},{"type":"text","value":" "},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"("}]},{"type":"element","tagName":"span","properties":{"className":["token","operator"]},"children":[{"type":"text","value":"!"}]},{"type":"text","value":"document"},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"."}]},{"type":"text","value":"body"},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"."}]},{"type":"element","tagName":"span","properties":{"className":["token","function"]},"children":[{"type":"text","value":"getAttribute"}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"("}]},{"type":"element","tagName":"span","properties":{"className":["token","string"]},"children":[{"type":"text","value":"'data-ext-dark'"}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":")"}]},{"type":"text","value":" "},{"type":"element","tagName":"span","properties":{"className":["token","operator"]},"children":[{"type":"text","value":"||"}]},{"type":"text","value":" document"},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"."}]},{"type":"text","value":"body"},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"."}]},{"type":"element","tagName":"span","properties":{"className":["token","function"]},"children":[{"type":"text","value":"getAttribute"}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"("}]},{"type":"element","tagName":"span","properties":{"className":["token","string"]},"children":[{"type":"text","value":"'data-ext-dark'"}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":")"}]},{"type":"text","value":" "},{"type":"element","tagName":"span","properties":{"className":["token","operator"]},"children":[{"type":"text","value":"==="}]},{"type":"text","value":" "},{"type":"element","tagName":"span","properties":{"className":["token","string"]},"children":[{"type":"text","value":"'false'"}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":")"}]},{"type":"text","value":" "},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"{"}]},{"type":"text","value":"\n    document"},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"."}]},{"type":"text","value":"body"},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"."}]},{"type":"element","tagName":"span","properties":{"className":["token","function"]},"children":[{"type":"text","value":"setAttribute"}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"("}]},{"type":"element","tagName":"span","properties":{"className":["token","string"]},"children":[{"type":"text","value":"'data-ext-dark'"}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":","}]},{"type":"text","value":" "},{"type":"element","tagName":"span","properties":{"className":["token","boolean"]},"children":[{"type":"text","value":"true"}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":")"}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":";"}]},{"type":"text","value":"\n    document"},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"."}]},{"type":"text","value":"body"},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"."}]},{"type":"text","value":"style"},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"."}]},{"type":"text","value":"backgroundColor "},{"type":"element","tagName":"span","properties":{"className":["token","operator"]},"children":[{"type":"text","value":"="}]},{"type":"text","value":" "},{"type":"element","tagName":"span","properties":{"className":["token","string"]},"children":[{"type":"text","value":"'#000'"}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":";"}]},{"type":"text","value":"\n    document"},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"."}]},{"type":"text","value":"body"},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"."}]},{"type":"text","value":"style"},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"."}]},{"type":"text","value":"color "},{"type":"element","tagName":"span","properties":{"className":["token","operator"]},"children":[{"type":"text","value":"="}]},{"type":"text","value":" "},{"type":"element","tagName":"span","properties":{"className":["token","string"]},"children":[{"type":"text","value":"'#fff'"}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":";"}]},{"type":"text","value":"\n"},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"}"}]},{"type":"text","value":" "},{"type":"element","tagName":"span","properties":{"className":["token","keyword"]},"children":[{"type":"text","value":"else"}]},{"type":"text","value":" "},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"{"}]},{"type":"text","value":"\n    document"},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"."}]},{"type":"text","value":"body"},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"."}]},{"type":"element","tagName":"span","properties":{"className":["token","function"]},"children":[{"type":"text","value":"setAttribute"}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"("}]},{"type":"element","tagName":"span","properties":{"className":["token","string"]},"children":[{"type":"text","value":"'data-ext-dark'"}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":","}]},{"type":"text","value":" "},{"type":"element","tagName":"span","properties":{"className":["token","boolean"]},"children":[{"type":"text","value":"false"}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":")"}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":";"}]},{"type":"text","value":"\n    document"},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"."}]},{"type":"text","value":"body"},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"."}]},{"type":"text","value":"style"},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"."}]},{"type":"text","value":"backgroundColor "},{"type":"element","tagName":"span","properties":{"className":["token","operator"]},"children":[{"type":"text","value":"="}]},{"type":"text","value":" "},{"type":"element","tagName":"span","properties":{"className":["token","string"]},"children":[{"type":"text","value":"'#fff'"}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":";"}]},{"type":"text","value":"\n    document"},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"."}]},{"type":"text","value":"body"},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"."}]},{"type":"text","value":"style"},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"."}]},{"type":"text","value":"color "},{"type":"element","tagName":"span","properties":{"className":["token","operator"]},"children":[{"type":"text","value":"="}]},{"type":"text","value":" "},{"type":"element","tagName":"span","properties":{"className":["token","string"]},"children":[{"type":"text","value":"'#000'"}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":";"}]},{"type":"text","value":"\n"},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"}"}]}]}]}]},{"type":"element","tagName":"p","properties":{},"children":[{"type":"text","value":"And in the listener we created earlier, use "},{"type":"element","tagName":"code","properties":{"className":["language-text"]},"children":[{"type":"text","value":"chrome.tabs.executeScript"}]},{"type":"text","value":" to run the code in "},{"type":"element","tagName":"code","properties":{"className":["language-text"]},"children":[{"type":"text","value":"toggleDark.js"}]},{"type":"text","value":":"}]},{"type":"element","tagName":"div","properties":{"className":["kg-card","kg-code-card","gatsby-highlight"],"dataLanguage":"js"},"children":[{"type":"element","tagName":"pre","properties":{"className":["language-js"]},"children":[{"type":"element","tagName":"code","properties":{"className":["language-js"]},"children":[{"type":"text","value":"chrome"},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"."}]},{"type":"text","value":"browserAction"},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"."}]},{"type":"text","value":"onClicked"},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"."}]},{"type":"element","tagName":"span","properties":{"className":["token","function"]},"children":[{"type":"text","value":"addListener"}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"("}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"("}]},{"type":"element","tagName":"span","properties":{"className":["token","parameter"]},"children":[{"type":"text","value":"tab"}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":")"}]},{"type":"text","value":" "},{"type":"element","tagName":"span","properties":{"className":["token","operator"]},"children":[{"type":"text","value":"=>"}]},{"type":"text","value":" "},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"{"}]},{"type":"text","value":"\n    chrome"},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"."}]},{"type":"text","value":"tabs"},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"."}]},{"type":"element","tagName":"span","properties":{"className":["token","function"]},"children":[{"type":"text","value":"executeScript"}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"("}]},{"type":"text","value":"tab"},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"."}]},{"type":"text","value":"id"},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":","}]},{"type":"text","value":" "},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"{"}]},{"type":"text","value":"\n        "},{"type":"element","tagName":"span","properties":{"className":["token","literal-property","property"]},"children":[{"type":"text","value":"file"}]},{"type":"element","tagName":"span","properties":{"className":["token","operator"]},"children":[{"type":"text","value":":"}]},{"type":"text","value":" "},{"type":"element","tagName":"span","properties":{"className":["token","string"]},"children":[{"type":"text","value":"'toggleDark.js'"}]},{"type":"text","value":"\n    "},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"}"}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":")"}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":";"}]},{"type":"text","value":"\n"},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"}"}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":")"}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":";"}]}]}]}]},{"type":"element","tagName":"p","properties":{},"children":[{"type":"element","tagName":"strong","properties":{},"children":[{"type":"text","value":"Adding Permissions"}]}]},{"type":"element","tagName":"p","properties":{},"children":[{"type":"text","value":"Now we're ready to handle when the shortcut is pressed. There's one last thing we need to do which is add permissions. Permissions are defined in "},{"type":"element","tagName":"code","properties":{"className":["language-text"]},"children":[{"type":"text","value":"manifest.json"}]},{"type":"text","value":" and include a set of permissions that the user has to allow for the extension to run."}]},{"type":"element","tagName":"p","properties":{},"children":[{"type":"element","tagName":"strong","properties":{},"children":[{"type":"text","value":"MV3"}]}]},{"type":"element","tagName":"p","properties":{},"children":[{"type":"text","value":"If you've been following along with the "},{"type":"element","tagName":"strong","properties":{},"children":[{"type":"text","value":"MV3 "}]},{"type":"text","value":"instructions, then you'll need two permissions: "},{"type":"element","tagName":"code","properties":{"className":["language-text"]},"children":[{"type":"text","value":"activeTab"}]},{"type":"text","value":" to access the current tab and "},{"type":"element","tagName":"code","properties":{"className":["language-text"]},"children":[{"type":"text","value":"scripting"}]},{"type":"text","value":" to use the "},{"type":"element","tagName":"code","properties":{"className":["language-text"]},"children":[{"type":"text","value":"chrome.scripting"}]},{"type":"text","value":" API. So, add the following in "},{"type":"element","tagName":"code","properties":{"className":["language-text"]},"children":[{"type":"text","value":"manifest.json"}]},{"type":"text","value":":"}]},{"type":"element","tagName":"div","properties":{"className":["kg-card","kg-code-card","gatsby-highlight"],"dataLanguage":"json"},"children":[{"type":"element","tagName":"pre","properties":{"className":["language-json"]},"children":[{"type":"element","tagName":"code","properties":{"className":["language-json"]},"children":[{"type":"element","tagName":"span","properties":{"className":["token","property"]},"children":[{"type":"text","value":"\"permissions\""}]},{"type":"element","tagName":"span","properties":{"className":["token","operator"]},"children":[{"type":"text","value":":"}]},{"type":"text","value":" "},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"["}]},{"type":"element","tagName":"span","properties":{"className":["token","string"]},"children":[{"type":"text","value":"\"activeTab\""}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":","}]},{"type":"text","value":" "},{"type":"element","tagName":"span","properties":{"className":["token","string"]},"children":[{"type":"text","value":"\"scripting\""}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"]"}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":","}]}]}]}]},{"type":"element","tagName":"p","properties":{},"children":[{"type":"element","tagName":"strong","properties":{},"children":[{"type":"text","value":"MV2"}]}]},{"type":"element","tagName":"p","properties":{},"children":[{"type":"text","value":"As for "},{"type":"element","tagName":"strong","properties":{},"children":[{"type":"text","value":"MV2, "}]},{"type":"text","value":"we'll need the "},{"type":"element","tagName":"code","properties":{"className":["language-text"]},"children":[{"type":"text","value":"activeTab"}]},{"type":"text","value":" permission to access the current tab and "},{"type":"element","tagName":"code","properties":{"className":["language-text"]},"children":[{"type":"text","value":"tabs"}]},{"type":"text","value":" to use the "},{"type":"element","tagName":"code","properties":{"className":["language-text"]},"children":[{"type":"text","value":"chrome.tabs"}]},{"type":"text","value":" API. So, add the following in "},{"type":"element","tagName":"code","properties":{"className":["language-text"]},"children":[{"type":"text","value":"manifest.json"}]},{"type":"text","value":":"}]},{"type":"element","tagName":"div","properties":{"className":["kg-card","kg-code-card","gatsby-highlight"],"dataLanguage":"json"},"children":[{"type":"element","tagName":"pre","properties":{"className":["language-json"]},"children":[{"type":"element","tagName":"code","properties":{"className":["language-json"]},"children":[{"type":"element","tagName":"span","properties":{"className":["token","property"]},"children":[{"type":"text","value":"\"permissions\""}]},{"type":"element","tagName":"span","properties":{"className":["token","operator"]},"children":[{"type":"text","value":":"}]},{"type":"text","value":" "},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"["}]},{"type":"element","tagName":"span","properties":{"className":["token","string"]},"children":[{"type":"text","value":"\"activeTab\""}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":","}]},{"type":"text","value":" "},{"type":"element","tagName":"span","properties":{"className":["token","string"]},"children":[{"type":"text","value":"\"tabs\""}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"]"}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":","}]}]}]}]},{"type":"element","tagName":"p","properties":{},"children":[{"type":"element","tagName":"strong","properties":{},"children":[{"type":"text","value":"Testing"}]}]},{"type":"element","tagName":"p","properties":{},"children":[{"type":"text","value":"Let's test our code. First, go to "},{"type":"element","tagName":"code","properties":{"className":["language-text"]},"children":[{"type":"text","value":"chrome://extensions"}]},{"type":"text","value":" and refresh the extension. Then, in an open tab (if you're creating an MV2 extension you'll need to refresh the page), press "},{"type":"element","tagName":"code","properties":{"className":["language-text"]},"children":[{"type":"text","value":"Ctrl+Shift+L"}]},{"type":"text","value":", and the document's body background color will change to black and the text to white. Remember that we're taking a simple approach to dark mode, so obviously it will not actually look good. If you keep pressing the same shortcut, it will toggle between light and dark mode."}]},{"type":"element","tagName":"figure","properties":{"className":["kg-card","kg-image-card"]},"children":[{"type":"element","tagName":"img","properties":{"src":"https://res-1.cloudinary.com/hbqmf3mbz/image/upload/q_auto/v1/ghost-blog-images/Screen-Shot-2021-07-08-at-6.54.33-PM.png","className":["kg-image"],"alt":"","loading":"lazy"},"children":[]}]},{"type":"element","tagName":"p","properties":{},"children":[{"type":"text","value":"If it doesn't work, make sure that "},{"type":"element","tagName":"code","properties":{"className":["language-text"]},"children":[{"type":"text","value":"Ctrl+Shift+P"}]},{"type":"text","value":" in your "},{"type":"element","tagName":"code","properties":{"className":["language-text"]},"children":[{"type":"text","value":"manifest.json"}]},{"type":"text","value":" is written correctly, as this is case-sensitive. Also, it should be noted that if other extensions that were installed previously use this shortcut, they'll have precedence to use the shortcut.  Similarly, if the shortcut is used by the browser or your operating system, then it will not be triggered. So, if the command does not work for you, try changing the shortcut in the manifest, then refresh the extension and try again."}]},{"type":"element","tagName":"hr","properties":{},"children":[]},{"type":"element","tagName":"h3","properties":{"id":"adding-a-standard-command"},"children":[{"type":"text","value":"Adding a Standard Command"}]},{"type":"element","tagName":"p","properties":{},"children":[{"type":"text","value":"Next, we'll add a standard command. The standard command we'll create will use the shortcut "},{"type":"element","tagName":"code","properties":{"className":["language-text"]},"children":[{"type":"text","value":"Ctrl+Shift+P"}]},{"type":"text","value":" to print to the console of the service worker or background script \"Hello there!\". It will run as long as Chrome is in focus, regardless of what page or tab is open."}]},{"type":"element","tagName":"p","properties":{},"children":[{"type":"text","value":"This section is the same for both "},{"type":"element","tagName":"strong","properties":{},"children":[{"type":"text","value":"MV3 "}]},{"type":"text","value":"and "},{"type":"element","tagName":"strong","properties":{},"children":[{"type":"text","value":"MV2"}]},{"type":"text","value":", so you can follow along regardless of what version your extension is. "}]},{"type":"element","tagName":"p","properties":{},"children":[{"type":"text","value":"In "},{"type":"element","tagName":"code","properties":{"className":["language-text"]},"children":[{"type":"text","value":"manifest.json"}]},{"type":"text","value":", add another command under the "},{"type":"element","tagName":"code","properties":{"className":["language-text"]},"children":[{"type":"text","value":"commands"}]},{"type":"text","value":" key:"}]},{"type":"element","tagName":"div","properties":{"className":["kg-card","kg-code-card","gatsby-highlight"],"dataLanguage":"json"},"children":[{"type":"element","tagName":"pre","properties":{"className":["language-json"]},"children":[{"type":"element","tagName":"code","properties":{"className":["language-json"]},"children":[{"type":"element","tagName":"span","properties":{"className":["token","property"]},"children":[{"type":"text","value":"\"commands\""}]},{"type":"element","tagName":"span","properties":{"className":["token","operator"]},"children":[{"type":"text","value":":"}]},{"type":"text","value":" "},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"{"}]},{"type":"text","value":"\n\t"},{"type":"element","tagName":"span","properties":{"className":["token","comment"]},"children":[{"type":"text","value":"//..."}]},{"type":"text","value":"\n    "},{"type":"element","tagName":"span","properties":{"className":["token","property"]},"children":[{"type":"text","value":"\"hello\""}]},{"type":"element","tagName":"span","properties":{"className":["token","operator"]},"children":[{"type":"text","value":":"}]},{"type":"text","value":" "},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"{"}]},{"type":"text","value":"\n       "},{"type":"element","tagName":"span","properties":{"className":["token","property"]},"children":[{"type":"text","value":"\"suggested_key\""}]},{"type":"element","tagName":"span","properties":{"className":["token","operator"]},"children":[{"type":"text","value":":"}]},{"type":"text","value":" "},{"type":"element","tagName":"span","properties":{"className":["token","string"]},"children":[{"type":"text","value":"\"Ctrl+Shift+P\""}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":","}]},{"type":"text","value":"\n       "},{"type":"element","tagName":"span","properties":{"className":["token","property"]},"children":[{"type":"text","value":"\"description\""}]},{"type":"element","tagName":"span","properties":{"className":["token","operator"]},"children":[{"type":"text","value":":"}]},{"type":"text","value":" "},{"type":"element","tagName":"span","properties":{"className":["token","string"]},"children":[{"type":"text","value":"\"Say Hello\""}]},{"type":"text","value":"\n    "},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"}"}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":","}]},{"type":"text","value":"\n"},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"}"}]}]}]}]},{"type":"element","tagName":"p","properties":{},"children":[{"type":"text","value":"Then, in the service worker or background script, add a listener to the "},{"type":"element","tagName":"code","properties":{"className":["language-text"]},"children":[{"type":"text","value":"chrome.commands.onCommand"}]},{"type":"text","value":" event:"}]},{"type":"element","tagName":"div","properties":{"className":["kg-card","kg-code-card","gatsby-highlight"],"dataLanguage":"js"},"children":[{"type":"element","tagName":"pre","properties":{"className":["language-js"]},"children":[{"type":"element","tagName":"code","properties":{"className":["language-js"]},"children":[{"type":"text","value":"chrome"},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"."}]},{"type":"text","value":"commands"},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"."}]},{"type":"text","value":"onCommand"},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"."}]},{"type":"element","tagName":"span","properties":{"className":["token","function"]},"children":[{"type":"text","value":"addListener"}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"("}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"("}]},{"type":"element","tagName":"span","properties":{"className":["token","parameter"]},"children":[{"type":"text","value":"command"}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":")"}]},{"type":"text","value":" "},{"type":"element","tagName":"span","properties":{"className":["token","operator"]},"children":[{"type":"text","value":"=>"}]},{"type":"text","value":" "},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"{"}]},{"type":"text","value":"\n    "},{"type":"element","tagName":"span","properties":{"className":["token","comment"]},"children":[{"type":"text","value":"//TODO handle event"}]},{"type":"text","value":"\n"},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"}"}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":")"}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":";"}]}]}]}]},{"type":"element","tagName":"p","properties":{},"children":[{"type":"text","value":"In the listener, we'll first check which command is triggered. This is necessary once you have more than one standard command, as they all trigger this same event."}]},{"type":"element","tagName":"p","properties":{},"children":[{"type":"text","value":"The "},{"type":"element","tagName":"code","properties":{"className":["language-text"]},"children":[{"type":"text","value":"command"}]},{"type":"text","value":" argument passed is the name of the command. So, if the user presses "},{"type":"element","tagName":"code","properties":{"className":["language-text"]},"children":[{"type":"text","value":"Ctrl+Shift+P"}]},{"type":"text","value":", the value for "},{"type":"element","tagName":"code","properties":{"className":["language-text"]},"children":[{"type":"text","value":"command"}]},{"type":"text","value":" will be "},{"type":"element","tagName":"code","properties":{"className":["language-text"]},"children":[{"type":"text","value":"hello"}]},{"type":"text","value":"."}]},{"type":"element","tagName":"p","properties":{},"children":[{"type":"text","value":"If the "},{"type":"element","tagName":"code","properties":{"className":["language-text"]},"children":[{"type":"text","value":"command"}]},{"type":"text","value":" is "},{"type":"element","tagName":"code","properties":{"className":["language-text"]},"children":[{"type":"text","value":"hello"}]},{"type":"text","value":", we'll log to the console \"Hello there!\":"}]},{"type":"element","tagName":"div","properties":{"className":["kg-card","kg-code-card","gatsby-highlight"],"dataLanguage":"js"},"children":[{"type":"element","tagName":"pre","properties":{"className":["language-js"]},"children":[{"type":"element","tagName":"code","properties":{"className":["language-js"]},"children":[{"type":"element","tagName":"span","properties":{"className":["token","keyword"]},"children":[{"type":"text","value":"if"}]},{"type":"text","value":" "},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"("}]},{"type":"text","value":"command "},{"type":"element","tagName":"span","properties":{"className":["token","operator"]},"children":[{"type":"text","value":"==="}]},{"type":"text","value":" "},{"type":"element","tagName":"span","properties":{"className":["token","string"]},"children":[{"type":"text","value":"'hello'"}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":")"}]},{"type":"text","value":" "},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"{"}]},{"type":"text","value":"\n\tconsole"},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"."}]},{"type":"element","tagName":"span","properties":{"className":["token","function"]},"children":[{"type":"text","value":"log"}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"("}]},{"type":"element","tagName":"span","properties":{"className":["token","string"]},"children":[{"type":"text","value":"\"Hello there!\""}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":")"}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":";"}]},{"type":"text","value":"\n"},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"}"}]}]}]}]},{"type":"element","tagName":"p","properties":{},"children":[{"type":"text","value":"That's it. Go to "},{"type":"element","tagName":"a","properties":{"href":"chrome://extensions"},"children":[{"type":"text","value":"chrome://extensions"}]},{"type":"text","value":" and refresh the extension. Then, for "},{"type":"element","tagName":"strong","properties":{},"children":[{"type":"text","value":"MV3 "}]},{"type":"text","value":"extensions click on \"Inspect views service worker\""}]},{"type":"element","tagName":"figure","properties":{"className":["kg-card","kg-image-card"]},"children":[{"type":"element","tagName":"img","properties":{"src":"https://res-1.cloudinary.com/hbqmf3mbz/image/upload/q_auto/v1/ghost-blog-images/Screen-Shot-2021-07-08-at-7.10.25-PM.png","className":["kg-image"],"alt":"","loading":"lazy"},"children":[]}]},{"type":"element","tagName":"p","properties":{},"children":[{"type":"text","value":"For "},{"type":"element","tagName":"strong","properties":{},"children":[{"type":"text","value":"MV2 "}]},{"type":"text","value":"extensions click on \"Inspect views background page\""}]},{"type":"element","tagName":"figure","properties":{"className":["kg-card","kg-image-card"]},"children":[{"type":"element","tagName":"img","properties":{"src":"https://res-5.cloudinary.com/hbqmf3mbz/image/upload/q_auto/v1/ghost-blog-images/Screen-Shot-2021-07-08-at-7.10.31-PM.png","className":["kg-image"],"alt":"","loading":"lazy"},"children":[]}]},{"type":"element","tagName":"p","properties":{},"children":[{"type":"text","value":"This will open the devtools for the service worker or background script. Click on the Console tab. Now, try clicking the shortcut "},{"type":"element","tagName":"code","properties":{"className":["language-text"]},"children":[{"type":"text","value":"Ctrl+Shift+P"}]},{"type":"text","value":" and you'll see \"Hello there!\" logged in the console."}]},{"type":"element","tagName":"figure","properties":{"className":["kg-card","kg-image-card"]},"children":[{"type":"element","tagName":"img","properties":{"src":"https://res-3.cloudinary.com/hbqmf3mbz/image/upload/q_auto/v1/ghost-blog-images/Screen-Shot-2021-07-08-at-7.13.14-PM.png","className":["kg-image"],"alt":"","loading":"lazy"},"children":[]}]},{"type":"element","tagName":"p","properties":{},"children":[{"type":"text","value":"If it doesn't work, make sure that "},{"type":"element","tagName":"code","properties":{"className":["language-text"]},"children":[{"type":"text","value":"Ctrl+Shift+P"}]},{"type":"text","value":" in your "},{"type":"element","tagName":"code","properties":{"className":["language-text"]},"children":[{"type":"text","value":"manifest.json"}]},{"type":"text","value":" is written correctly, as this is case-sensitive. Also, it should be noted that if other extensions that were installed previously use this shortcut, they'll have precedence to use the shortcut.  Similarly, if the shortcut is used by the browser or your operating system, then it will not be triggered. So, if the command does not work for you, try changing the shortcut in the manifest, then refresh the extension and try again."}]},{"type":"element","tagName":"hr","properties":{},"children":[]},{"type":"element","tagName":"h3","properties":{"id":"making-a-command-global"},"children":[{"type":"text","value":"Making a Command Global"}]},{"type":"element","tagName":"p","properties":{},"children":[{"type":"text","value":"In this section, we'll create a global standard command that when the user presses "},{"type":"element","tagName":"code","properties":{"className":["language-text"]},"children":[{"type":"text","value":"Ctrl+Shift+8"}]},{"type":"text","value":", it will log to the console of the service worker or background script the current date."}]},{"type":"element","tagName":"p","properties":{},"children":[{"type":"text","value":"This section is the same for both "},{"type":"element","tagName":"strong","properties":{},"children":[{"type":"text","value":"MV3 "}]},{"type":"text","value":"and "},{"type":"element","tagName":"strong","properties":{},"children":[{"type":"text","value":"MV2"}]},{"type":"text","value":", so you can follow along regardless of what version your extension is."}]},{"type":"element","tagName":"p","properties":{},"children":[{"type":"text","value":"To make a command global, we just need to add the key "},{"type":"element","tagName":"code","properties":{"className":["language-text"]},"children":[{"type":"text","value":"global"}]},{"type":"text","value":" to its definition in "},{"type":"element","tagName":"code","properties":{"className":["language-text"]},"children":[{"type":"text","value":"manifest.json"}]},{"type":"text","value":" and set it to true:"}]},{"type":"element","tagName":"div","properties":{"className":["kg-card","kg-code-card","gatsby-highlight"],"dataLanguage":"json"},"children":[{"type":"element","tagName":"pre","properties":{"className":["language-json"]},"children":[{"type":"element","tagName":"code","properties":{"className":["language-json"]},"children":[{"type":"element","tagName":"span","properties":{"className":["token","property"]},"children":[{"type":"text","value":"\"date\""}]},{"type":"element","tagName":"span","properties":{"className":["token","operator"]},"children":[{"type":"text","value":":"}]},{"type":"text","value":" "},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"{"}]},{"type":"text","value":"\n\t"},{"type":"element","tagName":"span","properties":{"className":["token","property"]},"children":[{"type":"text","value":"\"suggested_key\""}]},{"type":"element","tagName":"span","properties":{"className":["token","operator"]},"children":[{"type":"text","value":":"}]},{"type":"text","value":" "},{"type":"element","tagName":"span","properties":{"className":["token","string"]},"children":[{"type":"text","value":"\"Ctrl+Shift+8\""}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":","}]},{"type":"text","value":"\n\t"},{"type":"element","tagName":"span","properties":{"className":["token","property"]},"children":[{"type":"text","value":"\"description\""}]},{"type":"element","tagName":"span","properties":{"className":["token","operator"]},"children":[{"type":"text","value":":"}]},{"type":"text","value":" "},{"type":"element","tagName":"span","properties":{"className":["token","string"]},"children":[{"type":"text","value":"\"Show Date\""}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":","}]},{"type":"text","value":"\n    "},{"type":"element","tagName":"span","properties":{"className":["token","property"]},"children":[{"type":"text","value":"\"global\""}]},{"type":"element","tagName":"span","properties":{"className":["token","operator"]},"children":[{"type":"text","value":":"}]},{"type":"text","value":" "},{"type":"element","tagName":"span","properties":{"className":["token","boolean"]},"children":[{"type":"text","value":"true"}]},{"type":"text","value":"\n"},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"}"}]}]}]}]},{"type":"element","tagName":"p","properties":{},"children":[{"type":"text","value":"Then, in your service worker or background script, add an else block to the previous if statement that checked if the command name was \"hello\". In the else block, we'll log to the console the current date:"}]},{"type":"element","tagName":"div","properties":{"className":["kg-card","kg-code-card","gatsby-highlight"],"dataLanguage":"js"},"children":[{"type":"element","tagName":"pre","properties":{"className":["language-js"]},"children":[{"type":"element","tagName":"code","properties":{"className":["language-js"]},"children":[{"type":"text","value":"chrome"},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"."}]},{"type":"text","value":"commands"},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"."}]},{"type":"text","value":"onCommand"},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"."}]},{"type":"element","tagName":"span","properties":{"className":["token","function"]},"children":[{"type":"text","value":"addListener"}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"("}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"("}]},{"type":"element","tagName":"span","properties":{"className":["token","parameter"]},"children":[{"type":"text","value":"command"}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":")"}]},{"type":"text","value":" "},{"type":"element","tagName":"span","properties":{"className":["token","operator"]},"children":[{"type":"text","value":"=>"}]},{"type":"text","value":" "},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"{"}]},{"type":"text","value":"\n    "},{"type":"element","tagName":"span","properties":{"className":["token","keyword"]},"children":[{"type":"text","value":"if"}]},{"type":"text","value":" "},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"("}]},{"type":"text","value":"command "},{"type":"element","tagName":"span","properties":{"className":["token","operator"]},"children":[{"type":"text","value":"==="}]},{"type":"text","value":" "},{"type":"element","tagName":"span","properties":{"className":["token","string"]},"children":[{"type":"text","value":"'hello'"}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":")"}]},{"type":"text","value":" "},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"{"}]},{"type":"text","value":"\n        console"},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"."}]},{"type":"element","tagName":"span","properties":{"className":["token","function"]},"children":[{"type":"text","value":"log"}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"("}]},{"type":"element","tagName":"span","properties":{"className":["token","string"]},"children":[{"type":"text","value":"\"Hello there!\""}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":")"}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":";"}]},{"type":"text","value":"\n    "},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"}"}]},{"type":"text","value":" "},{"type":"element","tagName":"span","properties":{"className":["token","keyword"]},"children":[{"type":"text","value":"else"}]},{"type":"text","value":" "},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"{"}]},{"type":"text","value":"\n        console"},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"."}]},{"type":"element","tagName":"span","properties":{"className":["token","function"]},"children":[{"type":"text","value":"log"}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"("}]},{"type":"element","tagName":"span","properties":{"className":["token","string"]},"children":[{"type":"text","value":"\"Date: \""}]},{"type":"text","value":" "},{"type":"element","tagName":"span","properties":{"className":["token","operator"]},"children":[{"type":"text","value":"+"}]},{"type":"text","value":" "},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"("}]},{"type":"element","tagName":"span","properties":{"className":["token","keyword"]},"children":[{"type":"text","value":"new"}]},{"type":"text","value":" "},{"type":"element","tagName":"span","properties":{"className":["token","class-name"]},"children":[{"type":"text","value":"Date"}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":")"}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"."}]},{"type":"element","tagName":"span","properties":{"className":["token","function"]},"children":[{"type":"text","value":"toDateString"}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"("}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":")"}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":")"}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":";"}]},{"type":"text","value":"\n    "},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"}"}]},{"type":"text","value":"\n"},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"}"}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":")"}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":";"}]}]}]}]},{"type":"element","tagName":"p","properties":{},"children":[{"type":"text","value":"Our new command is ready. During development, I noticed that the global command, whether it's a newly added command or an older command that we added the "},{"type":"element","tagName":"code","properties":{"className":["language-text"]},"children":[{"type":"text","value":"global"}]},{"type":"text","value":" key to it, later on, the shortcut was not added or updated unless the extension was removed and re-loaded. So, make sure to do that."}]},{"type":"element","tagName":"p","properties":{},"children":[{"type":"text","value":"Then, open the console for the service worker or background script as mentioned in the previous section. If you now change to any window that's not your Chrome browser and press "},{"type":"element","tagName":"code","properties":{"className":["language-text"]},"children":[{"type":"text","value":"Ctrl+Shift+8"}]},{"type":"text","value":", then go back to the console for the service worker or background script, you'll see that the date is logged."}]},{"type":"element","tagName":"figure","properties":{"className":["kg-card","kg-image-card"]},"children":[{"type":"element","tagName":"img","properties":{"src":"https://res-5.cloudinary.com/hbqmf3mbz/image/upload/q_auto/v1/ghost-blog-images/Screen-Shot-2021-07-08-at-7.19.23-PM.png","className":["kg-image"],"alt":"","loading":"lazy"},"children":[]}]},{"type":"element","tagName":"p","properties":{},"children":[{"type":"text","value":"Again, if the command does not work, make sure that the command in the manifest is correct as it is "},{"type":"element","tagName":"strong","properties":{},"children":[{"type":"text","value":"case-sensitive"}]},{"type":"text","value":". Also, if this command is used by your system for another shortcut, you'll need to change it to something else."}]},{"type":"element","tagName":"hr","properties":{},"children":[]},{"type":"element","tagName":"h3","properties":{"id":"conclusion-next-steps"},"children":[{"type":"text","value":"Conclusion: Next Steps"}]},{"type":"element","tagName":"p","properties":{},"children":[{"type":"text","value":"As you have noticed while going through this tutorial, if an extension that was added prior to yours uses the same shortcut, your shortcuts will not be registered, so they will not work. To avoid that, you can check if your shortcuts are all added on installing the extension, and if any are missing you can notify the user that they can set the shortcuts in their Chrome browser on "},{"type":"element","tagName":"a","properties":{"href":"chrome://extensions/shortcuts"},"children":[{"type":"text","value":"chrome://extensions/shortcuts"}]},{"type":"text","value":". For more information and an example on how to do that, check "},{"type":"element","tagName":"a","properties":{"href":"https://developer.chrome.com/docs/extensions/reference/commands/#verify-commands-registered"},"children":[{"type":"text","value":"the example on chrome.command reference"}]},{"type":"text","value":"."}]}],"data":{"quirksMode":false}},"tableOfContents":[{"id":"creating-the-extension","heading":"Creating The Extension"},{"id":"what-are-chrome-commands","heading":"What Are Chrome Commands"},{"id":"suggested-key","heading":"Suggested Key"},{"id":"global-shortcuts","heading":"Global Shortcuts"},{"id":"adding-an-action-command","heading":"Adding an Action Command"},{"id":"adding-a-standard-command","heading":"Adding a Standard Command"},{"id":"making-a-command-global","heading":"Making a Command Global"},{"id":"conclusion-next-steps","heading":"Conclusion: Next Steps"}]},"featureImageSharp":{"base":"clay-banks-PXaQXThG1FY-unsplash--2---1-.jpg","publicURL":"/static/bf2190a2ebcbc0919c959c33d0cc75a3/clay-banks-PXaQXThG1FY-unsplash--2---1-.jpg","imageMeta":{"width":1920,"height":1287},"childImageSharp":{"fluid":{"base64":"data:image/jpeg;base64,/9j/2wBDABALDA4MChAODQ4SERATGCgaGBYWGDEjJR0oOjM9PDkzODdASFxOQERXRTc4UG1RV19iZ2hnPk1xeXBkeFxlZ2P/2wBDARESEhgVGC8aGi9jQjhCY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2P/wgARCAANABQDASIAAhEBAxEB/8QAGAAAAwEBAAAAAAAAAAAAAAAAAAECAwT/xAAVAQEBAAAAAAAAAAAAAAAAAAAAAf/aAAwDAQACEAMQAAAB7ZhRiIP/xAAYEAEBAQEBAAAAAAAAAAAAAAAAARECMf/aAAgBAQABBQLVx16qv//EABQRAQAAAAAAAAAAAAAAAAAAABD/2gAIAQMBAT8BP//EABQRAQAAAAAAAAAAAAAAAAAAABD/2gAIAQIBAT8BP//EABQQAQAAAAAAAAAAAAAAAAAAACD/2gAIAQEABj8CX//EABsQAAICAwEAAAAAAAAAAAAAAAABUWERMYGR/9oACAEBAAE/IcIGgUCVs69Nj//aAAwDAQACAAMAAAAQ/wDf/8QAFhEAAwAAAAAAAAAAAAAAAAAAARAR/9oACAEDAQE/EIV//8QAFhEAAwAAAAAAAAAAAAAAAAAAARAR/9oACAECAQE/EKF//8QAGxAAAwEAAwEAAAAAAAAAAAAAAAERIVFxkaH/2gAIAQEAAT8QUVU9JsT0dLKai2/QjkJe6+2f/9k=","aspectRatio":1.4957264957264957,"src":"/static/bf2190a2ebcbc0919c959c33d0cc75a3/ea4ab/clay-banks-PXaQXThG1FY-unsplash--2---1-.jpg","srcSet":"/static/bf2190a2ebcbc0919c959c33d0cc75a3/477ba/clay-banks-PXaQXThG1FY-unsplash--2---1-.jpg 175w,\n/static/bf2190a2ebcbc0919c959c33d0cc75a3/06776/clay-banks-PXaQXThG1FY-unsplash--2---1-.jpg 350w,\n/static/bf2190a2ebcbc0919c959c33d0cc75a3/ea4ab/clay-banks-PXaQXThG1FY-unsplash--2---1-.jpg 700w,\n/static/bf2190a2ebcbc0919c959c33d0cc75a3/3055e/clay-banks-PXaQXThG1FY-unsplash--2---1-.jpg 1050w,\n/static/bf2190a2ebcbc0919c959c33d0cc75a3/eff08/clay-banks-PXaQXThG1FY-unsplash--2---1-.jpg 1400w,\n/static/bf2190a2ebcbc0919c959c33d0cc75a3/74fd5/clay-banks-PXaQXThG1FY-unsplash--2---1-.jpg 1920w","srcWebp":"/static/bf2190a2ebcbc0919c959c33d0cc75a3/89afa/clay-banks-PXaQXThG1FY-unsplash--2---1-.webp","srcSetWebp":"/static/bf2190a2ebcbc0919c959c33d0cc75a3/9fca7/clay-banks-PXaQXThG1FY-unsplash--2---1-.webp 175w,\n/static/bf2190a2ebcbc0919c959c33d0cc75a3/37a4e/clay-banks-PXaQXThG1FY-unsplash--2---1-.webp 350w,\n/static/bf2190a2ebcbc0919c959c33d0cc75a3/89afa/clay-banks-PXaQXThG1FY-unsplash--2---1-.webp 700w,\n/static/bf2190a2ebcbc0919c959c33d0cc75a3/78e7a/clay-banks-PXaQXThG1FY-unsplash--2---1-.webp 1050w,\n/static/bf2190a2ebcbc0919c959c33d0cc75a3/03d34/clay-banks-PXaQXThG1FY-unsplash--2---1-.webp 1400w,\n/static/bf2190a2ebcbc0919c959c33d0cc75a3/6833b/clay-banks-PXaQXThG1FY-unsplash--2---1-.webp 1920w","sizes":"(max-width: 700px) 100vw, 700px"}}}}},{"node":{"id":"Ghost__Post__6127ba1b3ed159214d382eab","title":"How to Take Screenshots in Chrome Extension","slug":"how-to-take-screenshots-in-chrome-extension","featured":false,"feature_image":"https://res-2.cloudinary.com/hbqmf3mbz/image/upload/q_auto/v1/ghost-blog-images/photo-1509966756634-9c23dd6e6815.jpg","excerpt":"In this tutorial, we'll cover how to take a screenshot in a Chrome extension and save it on the user's machine.","custom_excerpt":"In this tutorial, we'll cover how to take a screenshot in a Chrome extension and save it on the user's machine.","visibility":"public","created_at_pretty":"5 Jun 2021","published_at_pretty":"7 Jun 2021","updated_at_pretty":"26 Aug 2021","created_at":"2021-06-05T15:37:06.000+00:00","published_at":"2021-06-07T15:28:48.000+00:00","updated_at":"2021-08-26T17:38:01.000+00:00","meta_title":null,"meta_description":null,"og_description":null,"og_image":null,"og_title":null,"twitter_description":null,"twitter_image":null,"twitter_title":null,"authors":[{"slug":"shahed","url":"https://backend.shahednasser.com/author/shahed/","name":"Shahed Nasser","bio":null,"cover_image":null,"profile_image":"https://backend.shahednasser.com/content/images/2022/03/IMG_0591.jpg","location":null,"website":null,"twitter":null,"facebook":null,"meta_title":null,"meta_description":null,"coverImageSharp":null,"profileImageSharp":null}],"primary_author":{"slug":"shahed","url":"https://backend.shahednasser.com/author/shahed/","name":"Shahed Nasser","bio":null,"cover_image":null,"profile_image":"https://backend.shahednasser.com/content/images/2022/03/IMG_0591.jpg","location":null,"website":null,"twitter":null,"facebook":null,"meta_title":null,"meta_description":null,"coverImageSharp":null,"profileImageSharp":{"base":"IMG_0591.jpg","publicURL":"/static/ceb49c3c631485453e71e00d7f84b069/IMG_0591.jpg","imageMeta":{"width":1182,"height":1179},"childImageSharp":{"fluid":{"base64":"data:image/jpeg;base64,/9j/2wBDABALDA4MChAODQ4SERATGCgaGBYWGDEjJR0oOjM9PDkzODdASFxOQERXRTc4UG1RV19iZ2hnPk1xeXBkeFxlZ2P/2wBDARESEhgVGC8aGi9jQjhCY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2P/wgARCAAUABQDASIAAhEBAxEB/8QAGAABAQEBAQAAAAAAAAAAAAAAAAMEAQL/xAAWAQEBAQAAAAAAAAAAAAAAAAADBAL/2gAMAwEAAhADEAAAAdXiFM6i0CohUWXoKn//xAAcEAACAgIDAAAAAAAAAAAAAAACAwESBBEhM0H/2gAIAQEAAQUCWySE3WEr7SzbXjAj4iKty+sOQ//EABYRAQEBAAAAAAAAAAAAAAAAAAERIP/aAAgBAwEBPwEhj//EABQRAQAAAAAAAAAAAAAAAAAAACD/2gAIAQIBAT8BH//EAB4QAAIBBAMBAAAAAAAAAAAAAAABIRESMUECECJx/9oACAEBAAY/ApVGWvOjzgtUwLlTZA0sdL4f/8QAHBAAAwACAwEAAAAAAAAAAAAAAAERITFBkbHB/9oACAEBAAE/IahkCy+N2GwZpjQiJHJCspUFY0QrSi+HqiW2rgf/2gAMAwEAAgADAAAAEPw3/wD/xAAYEQEBAAMAAAAAAAAAAAAAAAAAARExQf/aAAgBAwEBPxCtjDqP/8QAFxEBAQEBAAAAAAAAAAAAAAAAAQAxEf/aAAgBAgEBPxBFus6Tt//EAB8QAQEAAgIBBQAAAAAAAAAAAAERACExQWFRcYGR0f/aAAgBAQABPxAuaBPPzkO1wyX7F4wkwXanfZrFQgeqE9JgS14vVOvrERIJomVBKwt2jebAeP0yVa8h1n//2Q==","aspectRatio":1,"src":"/static/ceb49c3c631485453e71e00d7f84b069/31709/IMG_0591.jpg","srcSet":"/static/ceb49c3c631485453e71e00d7f84b069/f340b/IMG_0591.jpg 28w,\n/static/ceb49c3c631485453e71e00d7f84b069/22d64/IMG_0591.jpg 55w,\n/static/ceb49c3c631485453e71e00d7f84b069/31709/IMG_0591.jpg 110w,\n/static/ceb49c3c631485453e71e00d7f84b069/aa249/IMG_0591.jpg 165w,\n/static/ceb49c3c631485453e71e00d7f84b069/0dc33/IMG_0591.jpg 220w,\n/static/ceb49c3c631485453e71e00d7f84b069/d8257/IMG_0591.jpg 1182w","srcWebp":"/static/ceb49c3c631485453e71e00d7f84b069/8678c/IMG_0591.webp","srcSetWebp":"/static/ceb49c3c631485453e71e00d7f84b069/59cda/IMG_0591.webp 28w,\n/static/ceb49c3c631485453e71e00d7f84b069/7da75/IMG_0591.webp 55w,\n/static/ceb49c3c631485453e71e00d7f84b069/8678c/IMG_0591.webp 110w,\n/static/ceb49c3c631485453e71e00d7f84b069/f282e/IMG_0591.webp 165w,\n/static/ceb49c3c631485453e71e00d7f84b069/a7b21/IMG_0591.webp 220w,\n/static/ceb49c3c631485453e71e00d7f84b069/63099/IMG_0591.webp 1182w","sizes":"(max-width: 110px) 100vw, 110px"}}}},"primary_tag":{"slug":"browser-extensions","url":"https://backend.shahednasser.com/tag/browser-extensions/","name":"Browser Extensions","visibility":"public","feature_image":"https://backend.shahednasser.com/content/images/2022/01/photo-1616499370260-485b3e5ed653-2.jpeg","description":"Learn more about Browser Extensions through tutorials, articles, and tips.","meta_title":null,"meta_description":null,"featureImageSharp":{"base":"photo-1616499370260-485b3e5ed653-2.jpeg","publicURL":"/static/17d3062927434b0d9d22f3e45ece68b8/photo-1616499370260-485b3e5ed653-2.jpeg","imageMeta":{"width":2000,"height":1334},"childImageSharp":{"fluid":{"base64":"data:image/jpeg;base64,/9j/2wBDABALDA4MChAODQ4SERATGCgaGBYWGDEjJR0oOjM9PDkzODdASFxOQERXRTc4UG1RV19iZ2hnPk1xeXBkeFxlZ2P/2wBDARESEhgVGC8aGi9jQjhCY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2P/wgARCAANABQDASIAAhEBAxEB/8QAGAAAAgMAAAAAAAAAAAAAAAAAAAMCBAX/xAAVAQEBAAAAAAAAAAAAAAAAAAAAAv/aAAwDAQACEAMQAAABQ+hsRUBAf//EABoQAQADAAMAAAAAAAAAAAAAAAIAAQMEEyL/2gAIAQEAAQUCKpA3nefSo/JALzvlaXP/xAAVEQEBAAAAAAAAAAAAAAAAAAAQEf/aAAgBAwEBPwGH/8QAFREBAQAAAAAAAAAAAAAAAAAAERD/2gAIAQIBAT8BSf/EAB4QAAEDBAMAAAAAAAAAAAAAAAEAAiEDEBEiMVFh/9oACAEBAAY/Ai1x9CG2/VmEcoVCJwogL//EABoQAQEBAQADAAAAAAAAAAAAAAERACFBcYH/2gAIAQEAAT8h6pD2YW6ifbqrwMDk3W6VFUxyFZ4ib//aAAwDAQACAAMAAAAQLB//xAAXEQEAAwAAAAAAAAAAAAAAAAAAAREh/9oACAEDAQE/EJs1/8QAFREBAQAAAAAAAAAAAAAAAAAAABH/2gAIAQIBAT8QFf/EABwQAQEAAwEAAwAAAAAAAAAAAAERACExQVFhgf/aAAgBAQABPxCsM1ex6ZRBIol9InPbigkXRbrDW2E9042KoTWHL8fWeOKEM/Xuf//Z","aspectRatio":1.5028901734104045,"src":"/static/17d3062927434b0d9d22f3e45ece68b8/d5c54/photo-1616499370260-485b3e5ed653-2.jpg","srcSet":"/static/17d3062927434b0d9d22f3e45ece68b8/65d8c/photo-1616499370260-485b3e5ed653-2.jpg 260w,\n/static/17d3062927434b0d9d22f3e45ece68b8/c5f21/photo-1616499370260-485b3e5ed653-2.jpg 520w,\n/static/17d3062927434b0d9d22f3e45ece68b8/d5c54/photo-1616499370260-485b3e5ed653-2.jpg 1040w,\n/static/17d3062927434b0d9d22f3e45ece68b8/81a53/photo-1616499370260-485b3e5ed653-2.jpg 1560w,\n/static/17d3062927434b0d9d22f3e45ece68b8/4e5f3/photo-1616499370260-485b3e5ed653-2.jpg 2000w","srcWebp":"/static/17d3062927434b0d9d22f3e45ece68b8/e4875/photo-1616499370260-485b3e5ed653-2.webp","srcSetWebp":"/static/17d3062927434b0d9d22f3e45ece68b8/dc8f3/photo-1616499370260-485b3e5ed653-2.webp 260w,\n/static/17d3062927434b0d9d22f3e45ece68b8/2db4b/photo-1616499370260-485b3e5ed653-2.webp 520w,\n/static/17d3062927434b0d9d22f3e45ece68b8/e4875/photo-1616499370260-485b3e5ed653-2.webp 1040w,\n/static/17d3062927434b0d9d22f3e45ece68b8/f5845/photo-1616499370260-485b3e5ed653-2.webp 1560w,\n/static/17d3062927434b0d9d22f3e45ece68b8/49d6b/photo-1616499370260-485b3e5ed653-2.webp 2000w","sizes":"(max-width: 1040px) 100vw, 1040px"}}}},"tags":[{"slug":"browser-extensions","url":"https://backend.shahednasser.com/tag/browser-extensions/","name":"Browser Extensions","visibility":"public","feature_image":"https://backend.shahednasser.com/content/images/2022/01/photo-1616499370260-485b3e5ed653-2.jpeg","description":"Learn more about Browser Extensions through tutorials, articles, and tips.","meta_title":null,"meta_description":null,"featureImageSharp":null},{"slug":"js","url":"https://backend.shahednasser.com/tag/js/","name":"Javascript","visibility":"public","feature_image":"https://backend.shahednasser.com/content/images/2022/01/photo-1592609931095-54a2168ae893-2.jpeg","description":"Learn more about Javascript through tutorials, articles, and tips.","meta_title":null,"meta_description":"Learn more about Javascript through tutorials, articles and tips.","featureImageSharp":null}],"plaintext":"In this tutorial, we'll cover how to take a screenshot in a Chrome extension and\nsave it on the user's machine. This tutorial requires some beginner skills in\nJavascript.\n\nWe'll create an extension that allows the user to take a screenshot just by\nclicking the icon of the toolbar. The user can choose to take a screenshot of\nthe entire screen, just a window, or the current tab.\n\nNote that this extension will be using Manifest V3. I'll provide some hints\nabout the differences between V3 and V2 throughout the tutorial, but if you want\nto know more about the differences between the two versions you can check out \nthis tutorial\n[https://blog.shahednasser.com/chrome-extension-tutorial-migrating-to-manifest-v3-from-v2/]\n.\n\nYou can find the code for this tutorial on this GitHub Repository\n[https://github.com/shahednasser/chrome-screenshot-tutorial].\n\n\n--------------------------------------------------------------------------------\n\nCreating The Extension\nWe will not get into details on how to create a Chrome Extension, as it is not\nthe purpose. If you need to learn more details about it you can check out this\ntutorial\n[https://blog.shahednasser.com/chrome-extension-tutorial-replace-images-in-any-website-with-pikachu/]\n.\n\nCreate manifest.json in the root of your extension directory with the following\ncontent:\n\n{\n    \"name\": \"Screenshots\",\n    \"version\": \"0.0.1\",\n    \"description\": \"Take screenshots\",\n    \"manifest_version\": 3,\n    \"action\": {\n        \"default_title\": \"Take a Screenshot\"\n    },\n    \"icons\": {\n        \"16\": \"/assets/icon-16.png\",\n        \"32\": \"/assets/icon-32.png\",\n        \"48\": \"/assets/icon-48.png\",\n        \"128\": \"/assets/icon-128.png\"\n    }\n}\n\nThe icons we are using for this extension are by BZZRICON Studio\n[https://iconscout.com/contributors/bzzricon] on Iconscout\n[https://iconscout.com/].\n\nFor Manifest V2, make sure the manifest_version is set to 2:\n\n\"manifest_version\": 2\n\nand make sure to replace action with browser_action:\n\n\"browser_action\": {\n\t\"default_title\": \"Take a Screenshot\"\n}\n\nThen, create a zip of, go to chrome://extensions, enable Developer Mode from the\ntop right if it isn't enabled, click \"Load Unpacked\" from the buttons on the\nleft, and choose the directory of the extension. Our extension will be added\nsuccessfully.\n\nAdd Service Worker (or Background Script)\nTo detect when a user clicks on the extension's icon, we need to attach an event\nlistener to chrome.action.onClicked. To do that, we need to add a service worker\n(or background script for V2).\n\nTo add a service worker, add the following in manifest.json:\n\n\"background\": {\n\t\"service_worker\": \"background.js\"\n},\n\nOr the following for V2:\n\n\"background\": {\n\t\"scripts\": [\"background.js\"],\n\t\"persistent\": false\n},\n\nNext, create background.js in the root of the extension with the following\ncontent:\n\nchrome.action.onClicked.addListener(function (tab) {\n    \n})\n\nfor V2 it should be the following:\n\nchrome.browserAction.onClicked.addListener(function (tab) {\n    \n})\n\nNote that if you don't have the action key in manifest.json, you will not be\nable to add a listener to onClicked. \n\nNext, we'll start the \"take screenshot\" process. To do that, we'll use the \nDesktop Capture API\n[https://developer.chrome.com/docs/extensions/reference/desktopCapture/]. In\nparticular, we'll use the method chrome.desktopCapture.chooseDesktopMedia which\ntakes 3 parameters: The first is an array of strings of capture sources, which\ncan be \"screen\", \"window\", \"tab\", and \"audio\". The second parameter is the\ntarget tab which is optional, however, in some cases if the target tab is not\npassed Chrome crashes. The third parameter is a callback that returns the stream\nid which we'll use later to get a screenshot.\n\nadd the following inside the listener:\n\nchrome.desktopCapture.chooseDesktopMedia([\n        \"screen\",\n        \"window\",\n        \"tab\"\n    ], tab, (streamId) => {\n        //check whether the user canceled the request or not\n        if (streamId && streamId.length) {\n            \n        }\n    })\n\nNotice that we're passing in the first parameter \"screen\", \"window\", and \"tab\"\nas the allowed source types. The second parameter is the tab parameter passed to\nthe listener, and the third is the callback function. We're checking if streamId \nis not empty since it will be empty if the user cancels the request.\n\nBefore we can use this though, we need to add some permissions in the \nmanifest.json. Permissions allow the user to understand what the extension is\ndoing and agree to it before it is installed in their browser.\n\nAdd the following to manifest.json:\n\n\"permissions\": [\n    \"desktopCapture\",\n    \"tabs\"\n],\n\nThe reason we also need the tabs permission is because if we don't have the\npermission, the tab object passed to the onClicked event listener will not have\nthe url parameter which is required for chooseDesktopMedia when passing that tab\nas a parameter.\n\nSo, if you reload the extension now and press the icon, you'll see that it will\nask you what screen do you want to record and that's it. Next, we need to use\nthe streamId to get the screenshot.\n\nAdd Content Script\nTo obtain the stream from the streamId, we need to use getUserMedia\n[https://developer.mozilla.org/en-US/docs/Web/API/MediaDevices/getUserMedia].\nHowever, this is not available in the service worker. So, we need to create a\ncontent script that receives a message from the service worker with the stream\nid, then gets the screenshot from the stream.\n\nTo add a content script, add the following to manifest.json:\n\n\"content_scripts\": [\n    {\n    \t\"matches\": [\"<all_urls>\"],\n    \t\"js\": [\"content_script.js\"]\n    }\n]\n\nThen, create content_script.js in the root of the extension with the following\ncontent:\n\nchrome.runtime.onMessage.addListener((message, sender, senderResponse) => {\n    if (message.name === 'stream' && message.streamId) {\n        \n    }\n});\n\nThis code listens for the \"onMessage\" event and checks if the message received\nhas a name property that's equal to stream and has a streamId property, then\nwe'll obtain the stream and take a screenshot of it.\n\nInside the if, we'll use getUserMedia which returns a Promise that resolves to a \nMediaStream [https://developer.mozilla.org/en-US/docs/Web/API/MediaStream]:\n\nlet track, canvas\nnavigator.mediaDevices.getUserMedia({\n    video: {\n        mandatory: {\n            chromeMediaSource: 'desktop',\n            chromeMediaSourceId: message.streamId\n        },\n    }\n}).then((stream) => {\n    \n})\n\nNotice that the parameter we passed to getUserMedia takes an object of options.\nWe're passing the chromeMediaSource which equals to desktop, and \nchromeMediaSourceId which equals to the stream Id we received.\n\nNext, inside the callback function for the resolved promise, we'll get the \nMediaStreamTrack\n[https://developer.mozilla.org/en-US/docs/Web/API/MediaStreamTrack] and then\ncapture a screenshot from it using the ImageCapture\n[https://developer.mozilla.org/en-US/docs/Web/API/ImageCapture] API:\n\ntrack = stream.getVideoTracks()[0]\nconst imageCapture = new ImageCapture(track)\nreturn imageCapture.grabFrame()\n\nIn the end, we're returning the value of imageCapture.grabFrame which returns a\nPromise that resolves to an ImageBitmap. Notice that we didn't use the takePhoto \nmethod of the ImageCapture API. The reason behind that is that there are known\ncases of a DOMException thrown using it and this is a workaround\n[https://github.com/GoogleChromeLabs/imagecapture-polyfill/issues/15] for it.\n\nNext, we'll attach another then method to handle the returned Promise from \nimageCapture.grabFrame. The callback function will stop the stream, create a\ncanvas and draw the ImageBitmap in it, then get the Data Url of the canvas:\n\n.then((bitmap) => {\n    track.stop();\n    canvas = document.createElement('canvas');\n    canvas.width = bitmap.width;\n    canvas.height = bitmap.height;\n    let context = canvas.getContext('2d');\n    context.drawImage(bitmap, 0, 0, bitmap.width, bitmap.height);\n    return canvas.toDataURL();\n})\n\nNotice that it's important to set the width and height of the canvas to be equal\nto that of the bitmap. If we don't, the canvas height and width will default to \n200px and if the width or height of the bitmap is larger than that then the\nscreenshot will be cropped.\n\nIn the end, we're returning canvas.toDataUrl. We'll attach a last then method\nthat takes the URL returned as a parameter. This URL will be used to download\nthe image on the user's device:\n\n.then((url) => {\n    //TODO download the image from the URL\n}).catch((err) => {\n    alert(\"Could not take screenshot\")\n    senderResponse({success: false, message: err})\n})\n\nNotice that we also added catch to catch any errors. As you can see in the catch \ncallback, we're calling the function senderResponse. This function is the one\nwe'll pass from the service worker or background script to the content script\nwhen sending the message.\n\nAt the end of the if block we'll add the following:\n\nreturn true;\n\nIn a onMessage event listener if the listener returns true that means that we'll\nlater return a response to the sender using the callback function they passed\nwhen sending the message.\n\nDownload Screenshot\nTo download the screenshot, we'll use the Downloads\n[https://developer.chrome.com/docs/extensions/reference/downloads] API. It\nprovides a lot of methods to manage downloads like search, open, remove, and\nmore.\n\nBefore we can use any of the methods, we need to add the downloads permission to\nthe permissions array in manifest.json:\n\n\"permissions\": [\n    \"desktopCapture\",\n    \"tabs\",\n    \"downloads\"\n],\n\nNow, we can use the methods of the Downloads API. We'll use the method \nchrome.downloads.download which takes an array of options as the first parameter\nand a callback function as the second.\n\nHowever, this method cannot be called from the content script. We need to call\nit from the service worker/background script. So, when we get to the TODO part\nin our code earlier, we need to send a message to the service worker with the\nURL we want to download. \n\nTo send a message in an extension, we use the chrome.runtime.sendMessage which\ntakes as a first parameter the message to send (which can be of any type), and\nan optional callback function as the second parameter, which is the function\nthat the receiver of the message should call to deliver the response.\n\nAdd the following code in place of the TODO comment:\n\n.then((url) => {\n    chrome.runtime.sendMessage({name: 'download', url}, (response) => {\n        if (response.success) {\n            alert(\"Screenshot saved\");\n        } else {\n            alert(\"Could not save screenshot\")\n        }\n        canvas.remove()\n        senderResponse({success: true})\n    })\n})\n\nNotice that we're sending the message {name: 'download', url} to the receiver.\nAs the message is sent to every listener in the extension, it's good to include\na message property in the message you're sending to be able to handle different\nmessages. We're also sending the URL to download the image from.\n\nLet's go back to our service worker now. First, let's send a message to the\ncontent script from chooseDesktopMedia callback function we earlier did:\n\n//check whether the user canceled the request or not\nif (streamId && streamId.length) {\n    setTimeout(() => {\n        chrome.tabs.sendMessage(tab.id, {name: \"stream\", streamId}, (response) => console.log(response))\n    }, 200)\n}\n\nNotice that to send a message to the content script we're using \nchrome.tabs.sendMessage. The difference between this one and \nchrome.runtime.sendMessage is that the former one sends the message to content\nscripts in a specific tab, whereas the first one sends the message to all\nscripts in the extension that listen to the onMessage handler.\n\nNext, we'll add a listener to the onMessage event to receive the download \nmessage and download the file to the user's machine:\n\nchrome.runtime.onMessage.addListener((message, sender, senderResponse) => {\n    if (message.name === 'download' && message.url) {\n        chrome.downloads.download({\n            filename: 'screenshot.png',\n            url: message.url\n        }, (downloadId) => {\n            senderResponse({success: true})\n        })\n\n        return true;\n    }\n})\n\nFirst, we're checking if the name property of the message is equal to download \nto make sure that the message received is the correct one. Then, we're\ndownloading the file using chrome.downloads.download, passing it the options\nobject that has two options here: filename which is the name of the file to\ndownload, and url which is the URL to download. In the callback of the downloads \nmethod we're calling the callback function passed by the sender.\n\nOur extension is now ready. Go to chrome://extensions again and reload the\nextension. Then, go to any page, click the icon of the extension. You'll be\nprompted to choose either the entire screen, a window, or a tab. Once you\nchoose, a screenshot will be taken and saved on your machine.\n\n\n--------------------------------------------------------------------------------\n\nConclusion\nIn this tutorial, we learned how to a screenshot and a few of the concepts of a\nchrome extension shortly. If you want to learn more about Chrome extensions,\nmake sure to check out the rest of my tutorials about browser extensions\n[https://blog.shahednasser.com/tag/browser-extensions].","html":"<p>In this tutorial, we'll cover how to take a screenshot in a Chrome extension and save it on the user's machine. This tutorial requires some beginner skills in Javascript.</p><p>We'll create an extension that allows the user to take a screenshot just by clicking the icon of the toolbar. The user can choose to take a screenshot of the entire screen, just a window, or the current tab.</p><p>Note that this extension will be using Manifest V3. I'll provide some hints about the differences between V3 and V2 throughout the tutorial, but if you want to know more about the differences between the two versions you can check out <a href=\"https://blog.shahednasser.com/chrome-extension-tutorial-migrating-to-manifest-v3-from-v2/\">this tutorial</a>.</p><p>You can find the code for this tutorial on <a href=\"https://github.com/shahednasser/chrome-screenshot-tutorial\">this GitHub Repository</a>.</p><hr><h2 id=\"creating-the-extension\">Creating The Extension</h2><p>We will not get into details on how to create a Chrome Extension, as it is not the purpose. If you need to learn more details about it you can check out <a href=\"https://blog.shahednasser.com/chrome-extension-tutorial-replace-images-in-any-website-with-pikachu/\">this tutorial</a>.</p><p>Create <code>manifest.json</code> in the root of your extension directory with the following content:</p><pre><code class=\"language-json\">{\n    \"name\": \"Screenshots\",\n    \"version\": \"0.0.1\",\n    \"description\": \"Take screenshots\",\n    \"manifest_version\": 3,\n    \"action\": {\n        \"default_title\": \"Take a Screenshot\"\n    },\n    \"icons\": {\n        \"16\": \"/assets/icon-16.png\",\n        \"32\": \"/assets/icon-32.png\",\n        \"48\": \"/assets/icon-48.png\",\n        \"128\": \"/assets/icon-128.png\"\n    }\n}</code></pre><p>The icons we are using for this extension are by <a href=\"https://iconscout.com/contributors/bzzricon\">BZZRICON Studio</a> on <a href=\"https://iconscout.com/\">Iconscout</a>.</p><p>For Manifest V2, make sure the <code>manifest_version</code> is set to 2:</p><pre><code class=\"language-json\">\"manifest_version\": 2</code></pre><p>and make sure to replace <code>action</code> with <code>browser_action</code>:</p><pre><code class=\"language-json\">\"browser_action\": {\n\t\"default_title\": \"Take a Screenshot\"\n}</code></pre><p>Then, create a zip of, go to <a href=\"chrome://extensions\">chrome://extensions</a>, enable Developer Mode from the top right if it isn't enabled, click \"Load Unpacked\" from the buttons on the left, and choose the directory of the extension. Our extension will be added successfully.</p><h2 id=\"add-service-worker-or-background-script-\">Add Service Worker (or Background Script)</h2><p>To detect when a user clicks on the extension's icon, we need to attach an event listener to <code>chrome.action.onClicked</code>. To do that, we need to add a service worker (or background script for V2).</p><p>To add a service worker, add the following in <code>manifest.json</code>:</p><pre><code class=\"language-json\">\"background\": {\n\t\"service_worker\": \"background.js\"\n},</code></pre><p>Or the following for V2:</p><pre><code class=\"language-json\">\"background\": {\n\t\"scripts\": [\"background.js\"],\n\t\"persistent\": false\n},</code></pre><p>Next, create <code>background.js</code> in the root of the extension with the following content:</p><pre><code class=\"language-js\">chrome.action.onClicked.addListener(function (tab) {\n    \n})</code></pre><p>for V2 it should be the following:</p><pre><code class=\"language-js\">chrome.browserAction.onClicked.addListener(function (tab) {\n    \n})</code></pre><p>Note that if you don't have the <code>action</code> key in <code>manifest.json</code>, you will not be able to add a listener to <code>onClicked</code>. </p><p>Next, we'll start the \"take screenshot\" process. To do that, we'll use the <a href=\"https://developer.chrome.com/docs/extensions/reference/desktopCapture/\">Desktop Capture API</a>. In particular, we'll use the method <code>chrome.desktopCapture.chooseDesktopMedia</code> which takes 3 parameters: The first is an array of strings of capture sources, which can be \"screen\", \"window\", \"tab\", and \"audio\". The second parameter is the target tab which is optional, however, in some cases if the target tab is not passed Chrome crashes. The third parameter is a callback that returns the stream id which we'll use later to get a screenshot.</p><p>add the following inside the listener:</p><pre><code class=\"language-js\">chrome.desktopCapture.chooseDesktopMedia([\n        \"screen\",\n        \"window\",\n        \"tab\"\n    ], tab, (streamId) =&gt; {\n        //check whether the user canceled the request or not\n        if (streamId &amp;&amp; streamId.length) {\n            \n        }\n    })</code></pre><p>Notice that we're passing in the first parameter \"screen\", \"window\", and \"tab\" as the allowed source types. The second parameter is the <code>tab</code> parameter passed to the listener, and the third is the callback function. We're checking if <code>streamId</code> is not empty since it will be <code>empty</code> if the user cancels the request.</p><p>Before we can use this though, we need to add some permissions in the <code>manifest.json</code>. Permissions allow the user to understand what the extension is doing and agree to it before it is installed in their browser.</p><p>Add the following to <code>manifest.json</code>:</p><pre><code class=\"language-json\">\"permissions\": [\n    \"desktopCapture\",\n    \"tabs\"\n],</code></pre><p>The reason we also need the <code>tabs</code> permission is because if we don't have the permission, the <code>tab</code> object passed to the <code>onClicked</code> event listener will not have the <code>url</code> parameter which is required for <code>chooseDesktopMedia</code> when passing that tab as a parameter.</p><p>So, if you reload the extension now and press the icon, you'll see that it will ask you what screen do you want to record and that's it. Next, we need to use the <code>streamId</code> to get the screenshot.</p><h2 id=\"add-content-script\">Add Content Script</h2><p>To obtain the stream from the <code>streamId</code>, we need to use <a href=\"https://developer.mozilla.org/en-US/docs/Web/API/MediaDevices/getUserMedia\">getUserMedia</a>. However, this is not available in the service worker. So, we need to create a content script that receives a message from the service worker with the stream id, then gets the screenshot from the stream.</p><p>To add a content script, add the following to <code>manifest.json</code>:</p><pre><code class=\"language-json\">\"content_scripts\": [\n    {\n    \t\"matches\": [\"&lt;all_urls&gt;\"],\n    \t\"js\": [\"content_script.js\"]\n    }\n]</code></pre><p>Then, create <code>content_script.js</code> in the root of the extension with the following content:</p><pre><code class=\"language-js\">chrome.runtime.onMessage.addListener((message, sender, senderResponse) =&gt; {\n    if (message.name === 'stream' &amp;&amp; message.streamId) {\n        \n    }\n});</code></pre><p>This code listens for the \"onMessage\" event and checks if the <code>message</code> received has a <code>name</code> property that's equal to <code>stream</code> and has a <code>streamId</code> property, then we'll obtain the stream and take a screenshot of it.</p><p>Inside the if, we'll use <code>getUserMedia</code> which returns a Promise that resolves to a <a href=\"https://developer.mozilla.org/en-US/docs/Web/API/MediaStream\">MediaStream</a>:</p><pre><code class=\"language-js\">let track, canvas\nnavigator.mediaDevices.getUserMedia({\n    video: {\n        mandatory: {\n            chromeMediaSource: 'desktop',\n            chromeMediaSourceId: message.streamId\n        },\n    }\n}).then((stream) =&gt; {\n    \n})</code></pre><p>Notice that the parameter we passed to <code>getUserMedia</code> takes an object of options. We're passing the <code>chromeMediaSource</code> which equals to <code>desktop</code>, and <code>chromeMediaSourceId</code> which equals to the stream Id we received.</p><p>Next, inside the callback function for the resolved promise, we'll get the <a href=\"https://developer.mozilla.org/en-US/docs/Web/API/MediaStreamTrack\">MediaStreamTrack</a> and then capture a screenshot from it using the <a href=\"https://developer.mozilla.org/en-US/docs/Web/API/ImageCapture\">ImageCapture</a> API:</p><pre><code class=\"language-js\">track = stream.getVideoTracks()[0]\nconst imageCapture = new ImageCapture(track)\nreturn imageCapture.grabFrame()</code></pre><p>In the end, we're returning the value of <code>imageCapture.grabFrame</code> which returns a Promise that resolves to an ImageBitmap. Notice that we didn't use the <code>takePhoto</code> method of the <code>ImageCapture</code> API. The reason behind that is that there are known cases of a DOMException thrown using it and this is a <a href=\"https://github.com/GoogleChromeLabs/imagecapture-polyfill/issues/15\">workaround</a> for it.</p><p>Next, we'll attach another <code>then</code> method to handle the returned Promise from <code>imageCapture.grabFrame</code>. The callback function will stop the stream, create a canvas and draw the ImageBitmap in it, then get the Data Url of the canvas:</p><pre><code class=\"language-js\">.then((bitmap) =&gt; {\n    track.stop();\n    canvas = document.createElement('canvas');\n    canvas.width = bitmap.width;\n    canvas.height = bitmap.height;\n    let context = canvas.getContext('2d');\n    context.drawImage(bitmap, 0, 0, bitmap.width, bitmap.height);\n    return canvas.toDataURL();\n})</code></pre><p>Notice that it's important to set the width and height of the canvas to be equal to that of the <code>bitmap</code>. If we don't, the canvas height and width will default to <code>200px</code> and if the width or height of the bitmap is larger than that then the screenshot will be cropped.</p><p>In the end, we're returning <code>canvas.toDataUrl</code>. We'll attach a last <code>then</code> method that takes the URL returned as a parameter. This URL will be used to download the image on the user's device:</p><pre><code class=\"language-js\">.then((url) =&gt; {\n    //TODO download the image from the URL\n}).catch((err) =&gt; {\n    alert(\"Could not take screenshot\")\n    senderResponse({success: false, message: err})\n})</code></pre><p>Notice that we also added <code>catch</code> to catch any errors. As you can see in the <code>catch</code> callback, we're calling the function <code>senderResponse</code>. This function is the one we'll pass from the service worker or background script to the content script when sending the message.</p><p>At the end of the <code>if</code> block we'll add the following:</p><pre><code class=\"language-js\">return true;</code></pre><p>In a <code>onMessage</code> event listener if the listener returns true that means that we'll later return a response to the sender using the callback function they passed when sending the message.</p><h2 id=\"download-screenshot\">Download Screenshot</h2><p>To download the screenshot, we'll use the <a href=\"https://developer.chrome.com/docs/extensions/reference/downloads\">Downloads</a> API. It provides a lot of methods to manage downloads like search, open, remove, and more.</p><p>Before we can use any of the methods, we need to add the <code>downloads</code> permission to the <code>permissions</code> array in <code>manifest.json</code>:</p><pre><code class=\"language-json\">\"permissions\": [\n    \"desktopCapture\",\n    \"tabs\",\n    \"downloads\"\n],</code></pre><p>Now, we can use the methods of the Downloads API. We'll use the method <code>chrome.downloads.download</code> which takes an array of options as the first parameter and a callback function as the second.</p><p>However, this method cannot be called from the content script. We need to call it from the service worker/background script. So, when we get to the <code>TODO</code> part in our code earlier, we need to send a message to the service worker with the URL we want to download. </p><p>To send a message in an extension, we use the <code>chrome.runtime.sendMessage</code> which takes as a first parameter the message to send (which can be of any type), and an optional callback function as the second parameter, which is the function that the receiver of the message should call to deliver the response.</p><p>Add the following code in place of the <code>TODO</code> comment:</p><pre><code class=\"language-js\">.then((url) =&gt; {\n    chrome.runtime.sendMessage({name: 'download', url}, (response) =&gt; {\n        if (response.success) {\n            alert(\"Screenshot saved\");\n        } else {\n            alert(\"Could not save screenshot\")\n        }\n        canvas.remove()\n        senderResponse({success: true})\n    })\n})</code></pre><p>Notice that we're sending the message <code>{name: 'download', url}</code> to the receiver. As the message is sent to every listener in the extension, it's good to include a message property in the message you're sending to be able to handle different messages. We're also sending the URL to download the image from.</p><p>Let's go back to our service worker now. First, let's send a message to the content script from <code>chooseDesktopMedia</code> callback function we earlier did:</p><pre><code class=\"language-js\">//check whether the user canceled the request or not\nif (streamId &amp;&amp; streamId.length) {\n    setTimeout(() =&gt; {\n        chrome.tabs.sendMessage(tab.id, {name: \"stream\", streamId}, (response) =&gt; console.log(response))\n    }, 200)\n}</code></pre><p>Notice that to send a message to the content script we're using <code>chrome.tabs.sendMessage</code>. The difference between this one and <code>chrome.runtime.sendMessage</code> is that the former one sends the message to content scripts in a specific tab, whereas the first one sends the message to all scripts in the extension that listen to the <code>onMessage</code> handler.</p><p>Next, we'll add a listener to the <code>onMessage</code> event to receive the <code>download</code> message and download the file to the user's machine:</p><pre><code class=\"language-js\">chrome.runtime.onMessage.addListener((message, sender, senderResponse) =&gt; {\n    if (message.name === 'download' &amp;&amp; message.url) {\n        chrome.downloads.download({\n            filename: 'screenshot.png',\n            url: message.url\n        }, (downloadId) =&gt; {\n            senderResponse({success: true})\n        })\n\n        return true;\n    }\n})</code></pre><p>First, we're checking if the <code>name</code> property of the message is equal to <code>download</code> to make sure that the message received is the correct one. Then, we're downloading the file using <code>chrome.downloads.download</code>, passing it the options object that has two options here: <code>filename</code> which is the name of the file to download, and <code>url</code> which is the URL to download. In the callback of the <code>downloads</code> method we're calling the callback function passed by the sender.</p><p>Our extension is now ready. Go to <a href=\"chrome://extensions\">chrome://extensions</a> again and reload the extension. Then, go to any page, click the icon of the extension. You'll be prompted to choose either the entire screen, a window, or a tab. Once you choose, a screenshot will be taken and saved on your machine.</p><hr><h2 id=\"conclusion\">Conclusion</h2><p>In this tutorial, we learned how to a screenshot and a few of the concepts of a chrome extension shortly. If you want to learn more about Chrome extensions, make sure to check out the rest of my tutorials about <a href=\"https://blog.shahednasser.com/tag/browser-extensions\">browser extensions</a>.</p>","url":"https://backend.shahednasser.com/how-to-take-screenshots-in-chrome-extension/","canonical_url":null,"uuid":"f406e09d-a9cc-48a6-94b2-5ab3b4495ccd","codeinjection_foot":null,"codeinjection_head":null,"codeinjection_styles":null,"comment_id":"60bb9a2273b377001e61e2e6","reading_time":7,"send_email_when_published":null,"email_subject":null,"childHtmlRehype":{"html":"<p>In this tutorial, we'll cover how to take a screenshot in a Chrome extension and save it on the user's machine. This tutorial requires some beginner skills in Javascript.</p><p>We'll create an extension that allows the user to take a screenshot just by clicking the icon of the toolbar. The user can choose to take a screenshot of the entire screen, just a window, or the current tab.</p><p>Note that this extension will be using Manifest V3. I'll provide some hints about the differences between V3 and V2 throughout the tutorial, but if you want to know more about the differences between the two versions you can check out <a href=\"https://blog.shahednasser.com/chrome-extension-tutorial-migrating-to-manifest-v3-from-v2/\">this tutorial</a>.</p><p>You can find the code for this tutorial on <a href=\"https://github.com/shahednasser/chrome-screenshot-tutorial\">this GitHub Repository</a>.</p><hr><h2 id=\"creating-the-extension\">Creating The Extension</h2><p>We will not get into details on how to create a Chrome Extension, as it is not the purpose. If you need to learn more details about it you can check out <a href=\"https://blog.shahednasser.com/chrome-extension-tutorial-replace-images-in-any-website-with-pikachu/\">this tutorial</a>.</p><p>Create <code class=\"language-text\">manifest.json</code> in the root of your extension directory with the following content:</p><div class=\"kg-card kg-code-card gatsby-highlight\" data-language=\"json\"><pre class=\"language-json\"><code class=\"language-json\"><span class=\"token punctuation\">{</span>\n    <span class=\"token property\">\"name\"</span><span class=\"token operator\">:</span> <span class=\"token string\">\"Screenshots\"</span><span class=\"token punctuation\">,</span>\n    <span class=\"token property\">\"version\"</span><span class=\"token operator\">:</span> <span class=\"token string\">\"0.0.1\"</span><span class=\"token punctuation\">,</span>\n    <span class=\"token property\">\"description\"</span><span class=\"token operator\">:</span> <span class=\"token string\">\"Take screenshots\"</span><span class=\"token punctuation\">,</span>\n    <span class=\"token property\">\"manifest_version\"</span><span class=\"token operator\">:</span> <span class=\"token number\">3</span><span class=\"token punctuation\">,</span>\n    <span class=\"token property\">\"action\"</span><span class=\"token operator\">:</span> <span class=\"token punctuation\">{</span>\n        <span class=\"token property\">\"default_title\"</span><span class=\"token operator\">:</span> <span class=\"token string\">\"Take a Screenshot\"</span>\n    <span class=\"token punctuation\">}</span><span class=\"token punctuation\">,</span>\n    <span class=\"token property\">\"icons\"</span><span class=\"token operator\">:</span> <span class=\"token punctuation\">{</span>\n        <span class=\"token property\">\"16\"</span><span class=\"token operator\">:</span> <span class=\"token string\">\"/assets/icon-16.png\"</span><span class=\"token punctuation\">,</span>\n        <span class=\"token property\">\"32\"</span><span class=\"token operator\">:</span> <span class=\"token string\">\"/assets/icon-32.png\"</span><span class=\"token punctuation\">,</span>\n        <span class=\"token property\">\"48\"</span><span class=\"token operator\">:</span> <span class=\"token string\">\"/assets/icon-48.png\"</span><span class=\"token punctuation\">,</span>\n        <span class=\"token property\">\"128\"</span><span class=\"token operator\">:</span> <span class=\"token string\">\"/assets/icon-128.png\"</span>\n    <span class=\"token punctuation\">}</span>\n<span class=\"token punctuation\">}</span></code></pre></div><p>The icons we are using for this extension are by <a href=\"https://iconscout.com/contributors/bzzricon\">BZZRICON Studio</a> on <a href=\"https://iconscout.com/\">Iconscout</a>.</p><p>For Manifest V2, make sure the <code class=\"language-text\">manifest_version</code> is set to 2:</p><div class=\"kg-card kg-code-card gatsby-highlight\" data-language=\"json\"><pre class=\"language-json\"><code class=\"language-json\"><span class=\"token property\">\"manifest_version\"</span><span class=\"token operator\">:</span> <span class=\"token number\">2</span></code></pre></div><p>and make sure to replace <code class=\"language-text\">action</code> with <code class=\"language-text\">browser_action</code>:</p><div class=\"kg-card kg-code-card gatsby-highlight\" data-language=\"json\"><pre class=\"language-json\"><code class=\"language-json\"><span class=\"token property\">\"browser_action\"</span><span class=\"token operator\">:</span> <span class=\"token punctuation\">{</span>\n\t<span class=\"token property\">\"default_title\"</span><span class=\"token operator\">:</span> <span class=\"token string\">\"Take a Screenshot\"</span>\n<span class=\"token punctuation\">}</span></code></pre></div><p>Then, create a zip of, go to <a href=\"chrome://extensions\">chrome://extensions</a>, enable Developer Mode from the top right if it isn't enabled, click \"Load Unpacked\" from the buttons on the left, and choose the directory of the extension. Our extension will be added successfully.</p><h2 id=\"add-service-worker-or-background-script-\">Add Service Worker (or Background Script)</h2><p>To detect when a user clicks on the extension's icon, we need to attach an event listener to <code class=\"language-text\">chrome.action.onClicked</code>. To do that, we need to add a service worker (or background script for V2).</p><p>To add a service worker, add the following in <code class=\"language-text\">manifest.json</code>:</p><div class=\"kg-card kg-code-card gatsby-highlight\" data-language=\"json\"><pre class=\"language-json\"><code class=\"language-json\"><span class=\"token property\">\"background\"</span><span class=\"token operator\">:</span> <span class=\"token punctuation\">{</span>\n\t<span class=\"token property\">\"service_worker\"</span><span class=\"token operator\">:</span> <span class=\"token string\">\"background.js\"</span>\n<span class=\"token punctuation\">}</span><span class=\"token punctuation\">,</span></code></pre></div><p>Or the following for V2:</p><div class=\"kg-card kg-code-card gatsby-highlight\" data-language=\"json\"><pre class=\"language-json\"><code class=\"language-json\"><span class=\"token property\">\"background\"</span><span class=\"token operator\">:</span> <span class=\"token punctuation\">{</span>\n\t<span class=\"token property\">\"scripts\"</span><span class=\"token operator\">:</span> <span class=\"token punctuation\">[</span><span class=\"token string\">\"background.js\"</span><span class=\"token punctuation\">]</span><span class=\"token punctuation\">,</span>\n\t<span class=\"token property\">\"persistent\"</span><span class=\"token operator\">:</span> <span class=\"token boolean\">false</span>\n<span class=\"token punctuation\">}</span><span class=\"token punctuation\">,</span></code></pre></div><p>Next, create <code class=\"language-text\">background.js</code> in the root of the extension with the following content:</p><div class=\"kg-card kg-code-card gatsby-highlight\" data-language=\"js\"><pre class=\"language-js\"><code class=\"language-js\">chrome<span class=\"token punctuation\">.</span>action<span class=\"token punctuation\">.</span>onClicked<span class=\"token punctuation\">.</span><span class=\"token function\">addListener</span><span class=\"token punctuation\">(</span><span class=\"token keyword\">function</span> <span class=\"token punctuation\">(</span><span class=\"token parameter\">tab</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n    \n<span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span></code></pre></div><p>for V2 it should be the following:</p><div class=\"kg-card kg-code-card gatsby-highlight\" data-language=\"js\"><pre class=\"language-js\"><code class=\"language-js\">chrome<span class=\"token punctuation\">.</span>browserAction<span class=\"token punctuation\">.</span>onClicked<span class=\"token punctuation\">.</span><span class=\"token function\">addListener</span><span class=\"token punctuation\">(</span><span class=\"token keyword\">function</span> <span class=\"token punctuation\">(</span><span class=\"token parameter\">tab</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n    \n<span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span></code></pre></div><p>Note that if you don't have the <code class=\"language-text\">action</code> key in <code class=\"language-text\">manifest.json</code>, you will not be able to add a listener to <code class=\"language-text\">onClicked</code>. </p><p>Next, we'll start the \"take screenshot\" process. To do that, we'll use the <a href=\"https://developer.chrome.com/docs/extensions/reference/desktopCapture/\">Desktop Capture API</a>. In particular, we'll use the method <code class=\"language-text\">chrome.desktopCapture.chooseDesktopMedia</code> which takes 3 parameters: The first is an array of strings of capture sources, which can be \"screen\", \"window\", \"tab\", and \"audio\". The second parameter is the target tab which is optional, however, in some cases if the target tab is not passed Chrome crashes. The third parameter is a callback that returns the stream id which we'll use later to get a screenshot.</p><p>add the following inside the listener:</p><div class=\"kg-card kg-code-card gatsby-highlight\" data-language=\"js\"><pre class=\"language-js\"><code class=\"language-js\">chrome<span class=\"token punctuation\">.</span>desktopCapture<span class=\"token punctuation\">.</span><span class=\"token function\">chooseDesktopMedia</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">[</span>\n        <span class=\"token string\">\"screen\"</span><span class=\"token punctuation\">,</span>\n        <span class=\"token string\">\"window\"</span><span class=\"token punctuation\">,</span>\n        <span class=\"token string\">\"tab\"</span>\n    <span class=\"token punctuation\">]</span><span class=\"token punctuation\">,</span> tab<span class=\"token punctuation\">,</span> <span class=\"token punctuation\">(</span><span class=\"token parameter\">streamId</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">=></span> <span class=\"token punctuation\">{</span>\n        <span class=\"token comment\">//check whether the user canceled the request or not</span>\n        <span class=\"token keyword\">if</span> <span class=\"token punctuation\">(</span>streamId <span class=\"token operator\">&#x26;&#x26;</span> streamId<span class=\"token punctuation\">.</span>length<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n            \n        <span class=\"token punctuation\">}</span>\n    <span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span></code></pre></div><p>Notice that we're passing in the first parameter \"screen\", \"window\", and \"tab\" as the allowed source types. The second parameter is the <code class=\"language-text\">tab</code> parameter passed to the listener, and the third is the callback function. We're checking if <code class=\"language-text\">streamId</code> is not empty since it will be <code class=\"language-text\">empty</code> if the user cancels the request.</p><p>Before we can use this though, we need to add some permissions in the <code class=\"language-text\">manifest.json</code>. Permissions allow the user to understand what the extension is doing and agree to it before it is installed in their browser.</p><p>Add the following to <code class=\"language-text\">manifest.json</code>:</p><div class=\"kg-card kg-code-card gatsby-highlight\" data-language=\"json\"><pre class=\"language-json\"><code class=\"language-json\"><span class=\"token property\">\"permissions\"</span><span class=\"token operator\">:</span> <span class=\"token punctuation\">[</span>\n    <span class=\"token string\">\"desktopCapture\"</span><span class=\"token punctuation\">,</span>\n    <span class=\"token string\">\"tabs\"</span>\n<span class=\"token punctuation\">]</span><span class=\"token punctuation\">,</span></code></pre></div><p>The reason we also need the <code class=\"language-text\">tabs</code> permission is because if we don't have the permission, the <code class=\"language-text\">tab</code> object passed to the <code class=\"language-text\">onClicked</code> event listener will not have the <code class=\"language-text\">url</code> parameter which is required for <code class=\"language-text\">chooseDesktopMedia</code> when passing that tab as a parameter.</p><p>So, if you reload the extension now and press the icon, you'll see that it will ask you what screen do you want to record and that's it. Next, we need to use the <code class=\"language-text\">streamId</code> to get the screenshot.</p><h2 id=\"add-content-script\">Add Content Script</h2><p>To obtain the stream from the <code class=\"language-text\">streamId</code>, we need to use <a href=\"https://developer.mozilla.org/en-US/docs/Web/API/MediaDevices/getUserMedia\">getUserMedia</a>. However, this is not available in the service worker. So, we need to create a content script that receives a message from the service worker with the stream id, then gets the screenshot from the stream.</p><p>To add a content script, add the following to <code class=\"language-text\">manifest.json</code>:</p><div class=\"kg-card kg-code-card gatsby-highlight\" data-language=\"json\"><pre class=\"language-json\"><code class=\"language-json\"><span class=\"token property\">\"content_scripts\"</span><span class=\"token operator\">:</span> <span class=\"token punctuation\">[</span>\n    <span class=\"token punctuation\">{</span>\n    \t<span class=\"token property\">\"matches\"</span><span class=\"token operator\">:</span> <span class=\"token punctuation\">[</span><span class=\"token string\">\"&#x3C;all_urls>\"</span><span class=\"token punctuation\">]</span><span class=\"token punctuation\">,</span>\n    \t<span class=\"token property\">\"js\"</span><span class=\"token operator\">:</span> <span class=\"token punctuation\">[</span><span class=\"token string\">\"content_script.js\"</span><span class=\"token punctuation\">]</span>\n    <span class=\"token punctuation\">}</span>\n<span class=\"token punctuation\">]</span></code></pre></div><p>Then, create <code class=\"language-text\">content_script.js</code> in the root of the extension with the following content:</p><div class=\"kg-card kg-code-card gatsby-highlight\" data-language=\"js\"><pre class=\"language-js\"><code class=\"language-js\">chrome<span class=\"token punctuation\">.</span>runtime<span class=\"token punctuation\">.</span>onMessage<span class=\"token punctuation\">.</span><span class=\"token function\">addListener</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">(</span><span class=\"token parameter\">message<span class=\"token punctuation\">,</span> sender<span class=\"token punctuation\">,</span> senderResponse</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">=></span> <span class=\"token punctuation\">{</span>\n    <span class=\"token keyword\">if</span> <span class=\"token punctuation\">(</span>message<span class=\"token punctuation\">.</span>name <span class=\"token operator\">===</span> <span class=\"token string\">'stream'</span> <span class=\"token operator\">&#x26;&#x26;</span> message<span class=\"token punctuation\">.</span>streamId<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n        \n    <span class=\"token punctuation\">}</span>\n<span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span></code></pre></div><p>This code listens for the \"onMessage\" event and checks if the <code class=\"language-text\">message</code> received has a <code class=\"language-text\">name</code> property that's equal to <code class=\"language-text\">stream</code> and has a <code class=\"language-text\">streamId</code> property, then we'll obtain the stream and take a screenshot of it.</p><p>Inside the if, we'll use <code class=\"language-text\">getUserMedia</code> which returns a Promise that resolves to a <a href=\"https://developer.mozilla.org/en-US/docs/Web/API/MediaStream\">MediaStream</a>:</p><div class=\"kg-card kg-code-card gatsby-highlight\" data-language=\"js\"><pre class=\"language-js\"><code class=\"language-js\"><span class=\"token keyword\">let</span> track<span class=\"token punctuation\">,</span> canvas\nnavigator<span class=\"token punctuation\">.</span>mediaDevices<span class=\"token punctuation\">.</span><span class=\"token function\">getUserMedia</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">{</span>\n    <span class=\"token literal-property property\">video</span><span class=\"token operator\">:</span> <span class=\"token punctuation\">{</span>\n        <span class=\"token literal-property property\">mandatory</span><span class=\"token operator\">:</span> <span class=\"token punctuation\">{</span>\n            <span class=\"token literal-property property\">chromeMediaSource</span><span class=\"token operator\">:</span> <span class=\"token string\">'desktop'</span><span class=\"token punctuation\">,</span>\n            <span class=\"token literal-property property\">chromeMediaSourceId</span><span class=\"token operator\">:</span> message<span class=\"token punctuation\">.</span>streamId\n        <span class=\"token punctuation\">}</span><span class=\"token punctuation\">,</span>\n    <span class=\"token punctuation\">}</span>\n<span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">.</span><span class=\"token function\">then</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">(</span><span class=\"token parameter\">stream</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">=></span> <span class=\"token punctuation\">{</span>\n    \n<span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span></code></pre></div><p>Notice that the parameter we passed to <code class=\"language-text\">getUserMedia</code> takes an object of options. We're passing the <code class=\"language-text\">chromeMediaSource</code> which equals to <code class=\"language-text\">desktop</code>, and <code class=\"language-text\">chromeMediaSourceId</code> which equals to the stream Id we received.</p><p>Next, inside the callback function for the resolved promise, we'll get the <a href=\"https://developer.mozilla.org/en-US/docs/Web/API/MediaStreamTrack\">MediaStreamTrack</a> and then capture a screenshot from it using the <a href=\"https://developer.mozilla.org/en-US/docs/Web/API/ImageCapture\">ImageCapture</a> API:</p><div class=\"kg-card kg-code-card gatsby-highlight\" data-language=\"js\"><pre class=\"language-js\"><code class=\"language-js\">track <span class=\"token operator\">=</span> stream<span class=\"token punctuation\">.</span><span class=\"token function\">getVideoTracks</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">[</span><span class=\"token number\">0</span><span class=\"token punctuation\">]</span>\n<span class=\"token keyword\">const</span> imageCapture <span class=\"token operator\">=</span> <span class=\"token keyword\">new</span> <span class=\"token class-name\">ImageCapture</span><span class=\"token punctuation\">(</span>track<span class=\"token punctuation\">)</span>\n<span class=\"token keyword\">return</span> imageCapture<span class=\"token punctuation\">.</span><span class=\"token function\">grabFrame</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span></code></pre></div><p>In the end, we're returning the value of <code class=\"language-text\">imageCapture.grabFrame</code> which returns a Promise that resolves to an ImageBitmap. Notice that we didn't use the <code class=\"language-text\">takePhoto</code> method of the <code class=\"language-text\">ImageCapture</code> API. The reason behind that is that there are known cases of a DOMException thrown using it and this is a <a href=\"https://github.com/GoogleChromeLabs/imagecapture-polyfill/issues/15\">workaround</a> for it.</p><p>Next, we'll attach another <code class=\"language-text\">then</code> method to handle the returned Promise from <code class=\"language-text\">imageCapture.grabFrame</code>. The callback function will stop the stream, create a canvas and draw the ImageBitmap in it, then get the Data Url of the canvas:</p><div class=\"kg-card kg-code-card gatsby-highlight\" data-language=\"js\"><pre class=\"language-js\"><code class=\"language-js\"><span class=\"token punctuation\">.</span><span class=\"token function\">then</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">(</span><span class=\"token parameter\">bitmap</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">=></span> <span class=\"token punctuation\">{</span>\n    track<span class=\"token punctuation\">.</span><span class=\"token function\">stop</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n    canvas <span class=\"token operator\">=</span> document<span class=\"token punctuation\">.</span><span class=\"token function\">createElement</span><span class=\"token punctuation\">(</span><span class=\"token string\">'canvas'</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n    canvas<span class=\"token punctuation\">.</span>width <span class=\"token operator\">=</span> bitmap<span class=\"token punctuation\">.</span>width<span class=\"token punctuation\">;</span>\n    canvas<span class=\"token punctuation\">.</span>height <span class=\"token operator\">=</span> bitmap<span class=\"token punctuation\">.</span>height<span class=\"token punctuation\">;</span>\n    <span class=\"token keyword\">let</span> context <span class=\"token operator\">=</span> canvas<span class=\"token punctuation\">.</span><span class=\"token function\">getContext</span><span class=\"token punctuation\">(</span><span class=\"token string\">'2d'</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n    context<span class=\"token punctuation\">.</span><span class=\"token function\">drawImage</span><span class=\"token punctuation\">(</span>bitmap<span class=\"token punctuation\">,</span> <span class=\"token number\">0</span><span class=\"token punctuation\">,</span> <span class=\"token number\">0</span><span class=\"token punctuation\">,</span> bitmap<span class=\"token punctuation\">.</span>width<span class=\"token punctuation\">,</span> bitmap<span class=\"token punctuation\">.</span>height<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n    <span class=\"token keyword\">return</span> canvas<span class=\"token punctuation\">.</span><span class=\"token function\">toDataURL</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n<span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span></code></pre></div><p>Notice that it's important to set the width and height of the canvas to be equal to that of the <code class=\"language-text\">bitmap</code>. If we don't, the canvas height and width will default to <code class=\"language-text\">200px</code> and if the width or height of the bitmap is larger than that then the screenshot will be cropped.</p><p>In the end, we're returning <code class=\"language-text\">canvas.toDataUrl</code>. We'll attach a last <code class=\"language-text\">then</code> method that takes the URL returned as a parameter. This URL will be used to download the image on the user's device:</p><div class=\"kg-card kg-code-card gatsby-highlight\" data-language=\"js\"><pre class=\"language-js\"><code class=\"language-js\"><span class=\"token punctuation\">.</span><span class=\"token function\">then</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">(</span><span class=\"token parameter\">url</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">=></span> <span class=\"token punctuation\">{</span>\n    <span class=\"token comment\">//TODO download the image from the URL</span>\n<span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">.</span><span class=\"token function\">catch</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">(</span><span class=\"token parameter\">err</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">=></span> <span class=\"token punctuation\">{</span>\n    <span class=\"token function\">alert</span><span class=\"token punctuation\">(</span><span class=\"token string\">\"Could not take screenshot\"</span><span class=\"token punctuation\">)</span>\n    <span class=\"token function\">senderResponse</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">{</span><span class=\"token literal-property property\">success</span><span class=\"token operator\">:</span> <span class=\"token boolean\">false</span><span class=\"token punctuation\">,</span> <span class=\"token literal-property property\">message</span><span class=\"token operator\">:</span> err<span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span>\n<span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span></code></pre></div><p>Notice that we also added <code class=\"language-text\">catch</code> to catch any errors. As you can see in the <code class=\"language-text\">catch</code> callback, we're calling the function <code class=\"language-text\">senderResponse</code>. This function is the one we'll pass from the service worker or background script to the content script when sending the message.</p><p>At the end of the <code class=\"language-text\">if</code> block we'll add the following:</p><div class=\"kg-card kg-code-card gatsby-highlight\" data-language=\"js\"><pre class=\"language-js\"><code class=\"language-js\"><span class=\"token keyword\">return</span> <span class=\"token boolean\">true</span><span class=\"token punctuation\">;</span></code></pre></div><p>In a <code class=\"language-text\">onMessage</code> event listener if the listener returns true that means that we'll later return a response to the sender using the callback function they passed when sending the message.</p><h2 id=\"download-screenshot\">Download Screenshot</h2><p>To download the screenshot, we'll use the <a href=\"https://developer.chrome.com/docs/extensions/reference/downloads\">Downloads</a> API. It provides a lot of methods to manage downloads like search, open, remove, and more.</p><p>Before we can use any of the methods, we need to add the <code class=\"language-text\">downloads</code> permission to the <code class=\"language-text\">permissions</code> array in <code class=\"language-text\">manifest.json</code>:</p><div class=\"kg-card kg-code-card gatsby-highlight\" data-language=\"json\"><pre class=\"language-json\"><code class=\"language-json\"><span class=\"token property\">\"permissions\"</span><span class=\"token operator\">:</span> <span class=\"token punctuation\">[</span>\n    <span class=\"token string\">\"desktopCapture\"</span><span class=\"token punctuation\">,</span>\n    <span class=\"token string\">\"tabs\"</span><span class=\"token punctuation\">,</span>\n    <span class=\"token string\">\"downloads\"</span>\n<span class=\"token punctuation\">]</span><span class=\"token punctuation\">,</span></code></pre></div><p>Now, we can use the methods of the Downloads API. We'll use the method <code class=\"language-text\">chrome.downloads.download</code> which takes an array of options as the first parameter and a callback function as the second.</p><p>However, this method cannot be called from the content script. We need to call it from the service worker/background script. So, when we get to the <code class=\"language-text\">TODO</code> part in our code earlier, we need to send a message to the service worker with the URL we want to download. </p><p>To send a message in an extension, we use the <code class=\"language-text\">chrome.runtime.sendMessage</code> which takes as a first parameter the message to send (which can be of any type), and an optional callback function as the second parameter, which is the function that the receiver of the message should call to deliver the response.</p><p>Add the following code in place of the <code class=\"language-text\">TODO</code> comment:</p><div class=\"kg-card kg-code-card gatsby-highlight\" data-language=\"js\"><pre class=\"language-js\"><code class=\"language-js\"><span class=\"token punctuation\">.</span><span class=\"token function\">then</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">(</span><span class=\"token parameter\">url</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">=></span> <span class=\"token punctuation\">{</span>\n    chrome<span class=\"token punctuation\">.</span>runtime<span class=\"token punctuation\">.</span><span class=\"token function\">sendMessage</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">{</span><span class=\"token literal-property property\">name</span><span class=\"token operator\">:</span> <span class=\"token string\">'download'</span><span class=\"token punctuation\">,</span> url<span class=\"token punctuation\">}</span><span class=\"token punctuation\">,</span> <span class=\"token punctuation\">(</span><span class=\"token parameter\">response</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">=></span> <span class=\"token punctuation\">{</span>\n        <span class=\"token keyword\">if</span> <span class=\"token punctuation\">(</span>response<span class=\"token punctuation\">.</span>success<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n            <span class=\"token function\">alert</span><span class=\"token punctuation\">(</span><span class=\"token string\">\"Screenshot saved\"</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n        <span class=\"token punctuation\">}</span> <span class=\"token keyword\">else</span> <span class=\"token punctuation\">{</span>\n            <span class=\"token function\">alert</span><span class=\"token punctuation\">(</span><span class=\"token string\">\"Could not save screenshot\"</span><span class=\"token punctuation\">)</span>\n        <span class=\"token punctuation\">}</span>\n        canvas<span class=\"token punctuation\">.</span><span class=\"token function\">remove</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span>\n        <span class=\"token function\">senderResponse</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">{</span><span class=\"token literal-property property\">success</span><span class=\"token operator\">:</span> <span class=\"token boolean\">true</span><span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span>\n    <span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span>\n<span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span></code></pre></div><p>Notice that we're sending the message <code class=\"language-text\">{name: 'download', url}</code> to the receiver. As the message is sent to every listener in the extension, it's good to include a message property in the message you're sending to be able to handle different messages. We're also sending the URL to download the image from.</p><p>Let's go back to our service worker now. First, let's send a message to the content script from <code class=\"language-text\">chooseDesktopMedia</code> callback function we earlier did:</p><div class=\"kg-card kg-code-card gatsby-highlight\" data-language=\"js\"><pre class=\"language-js\"><code class=\"language-js\"><span class=\"token comment\">//check whether the user canceled the request or not</span>\n<span class=\"token keyword\">if</span> <span class=\"token punctuation\">(</span>streamId <span class=\"token operator\">&#x26;&#x26;</span> streamId<span class=\"token punctuation\">.</span>length<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token function\">setTimeout</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">=></span> <span class=\"token punctuation\">{</span>\n        chrome<span class=\"token punctuation\">.</span>tabs<span class=\"token punctuation\">.</span><span class=\"token function\">sendMessage</span><span class=\"token punctuation\">(</span>tab<span class=\"token punctuation\">.</span>id<span class=\"token punctuation\">,</span> <span class=\"token punctuation\">{</span><span class=\"token literal-property property\">name</span><span class=\"token operator\">:</span> <span class=\"token string\">\"stream\"</span><span class=\"token punctuation\">,</span> streamId<span class=\"token punctuation\">}</span><span class=\"token punctuation\">,</span> <span class=\"token punctuation\">(</span><span class=\"token parameter\">response</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">=></span> console<span class=\"token punctuation\">.</span><span class=\"token function\">log</span><span class=\"token punctuation\">(</span>response<span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span>\n    <span class=\"token punctuation\">}</span><span class=\"token punctuation\">,</span> <span class=\"token number\">200</span><span class=\"token punctuation\">)</span>\n<span class=\"token punctuation\">}</span></code></pre></div><p>Notice that to send a message to the content script we're using <code class=\"language-text\">chrome.tabs.sendMessage</code>. The difference between this one and <code class=\"language-text\">chrome.runtime.sendMessage</code> is that the former one sends the message to content scripts in a specific tab, whereas the first one sends the message to all scripts in the extension that listen to the <code class=\"language-text\">onMessage</code> handler.</p><p>Next, we'll add a listener to the <code class=\"language-text\">onMessage</code> event to receive the <code class=\"language-text\">download</code> message and download the file to the user's machine:</p><div class=\"kg-card kg-code-card gatsby-highlight\" data-language=\"js\"><pre class=\"language-js\"><code class=\"language-js\">chrome<span class=\"token punctuation\">.</span>runtime<span class=\"token punctuation\">.</span>onMessage<span class=\"token punctuation\">.</span><span class=\"token function\">addListener</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">(</span><span class=\"token parameter\">message<span class=\"token punctuation\">,</span> sender<span class=\"token punctuation\">,</span> senderResponse</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">=></span> <span class=\"token punctuation\">{</span>\n    <span class=\"token keyword\">if</span> <span class=\"token punctuation\">(</span>message<span class=\"token punctuation\">.</span>name <span class=\"token operator\">===</span> <span class=\"token string\">'download'</span> <span class=\"token operator\">&#x26;&#x26;</span> message<span class=\"token punctuation\">.</span>url<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n        chrome<span class=\"token punctuation\">.</span>downloads<span class=\"token punctuation\">.</span><span class=\"token function\">download</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">{</span>\n            <span class=\"token literal-property property\">filename</span><span class=\"token operator\">:</span> <span class=\"token string\">'screenshot.png'</span><span class=\"token punctuation\">,</span>\n            <span class=\"token literal-property property\">url</span><span class=\"token operator\">:</span> message<span class=\"token punctuation\">.</span>url\n        <span class=\"token punctuation\">}</span><span class=\"token punctuation\">,</span> <span class=\"token punctuation\">(</span><span class=\"token parameter\">downloadId</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">=></span> <span class=\"token punctuation\">{</span>\n            <span class=\"token function\">senderResponse</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">{</span><span class=\"token literal-property property\">success</span><span class=\"token operator\">:</span> <span class=\"token boolean\">true</span><span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span>\n        <span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span>\n\n        <span class=\"token keyword\">return</span> <span class=\"token boolean\">true</span><span class=\"token punctuation\">;</span>\n    <span class=\"token punctuation\">}</span>\n<span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span></code></pre></div><p>First, we're checking if the <code class=\"language-text\">name</code> property of the message is equal to <code class=\"language-text\">download</code> to make sure that the message received is the correct one. Then, we're downloading the file using <code class=\"language-text\">chrome.downloads.download</code>, passing it the options object that has two options here: <code class=\"language-text\">filename</code> which is the name of the file to download, and <code class=\"language-text\">url</code> which is the URL to download. In the callback of the <code class=\"language-text\">downloads</code> method we're calling the callback function passed by the sender.</p><p>Our extension is now ready. Go to <a href=\"chrome://extensions\">chrome://extensions</a> again and reload the extension. Then, go to any page, click the icon of the extension. You'll be prompted to choose either the entire screen, a window, or a tab. Once you choose, a screenshot will be taken and saved on your machine.</p><hr><h2 id=\"conclusion\">Conclusion</h2><p>In this tutorial, we learned how to a screenshot and a few of the concepts of a chrome extension shortly. If you want to learn more about Chrome extensions, make sure to check out the rest of my tutorials about <a href=\"https://blog.shahednasser.com/tag/browser-extensions\">browser extensions</a>.</p>","htmlAst":{"type":"root","children":[{"type":"element","tagName":"p","properties":{},"children":[{"type":"text","value":"In this tutorial, we'll cover how to take a screenshot in a Chrome extension and save it on the user's machine. This tutorial requires some beginner skills in Javascript."}]},{"type":"element","tagName":"p","properties":{},"children":[{"type":"text","value":"We'll create an extension that allows the user to take a screenshot just by clicking the icon of the toolbar. The user can choose to take a screenshot of the entire screen, just a window, or the current tab."}]},{"type":"element","tagName":"p","properties":{},"children":[{"type":"text","value":"Note that this extension will be using Manifest V3. I'll provide some hints about the differences between V3 and V2 throughout the tutorial, but if you want to know more about the differences between the two versions you can check out "},{"type":"element","tagName":"a","properties":{"href":"https://blog.shahednasser.com/chrome-extension-tutorial-migrating-to-manifest-v3-from-v2/"},"children":[{"type":"text","value":"this tutorial"}]},{"type":"text","value":"."}]},{"type":"element","tagName":"p","properties":{},"children":[{"type":"text","value":"You can find the code for this tutorial on "},{"type":"element","tagName":"a","properties":{"href":"https://github.com/shahednasser/chrome-screenshot-tutorial"},"children":[{"type":"text","value":"this GitHub Repository"}]},{"type":"text","value":"."}]},{"type":"element","tagName":"hr","properties":{},"children":[]},{"type":"element","tagName":"h2","properties":{"id":"creating-the-extension"},"children":[{"type":"text","value":"Creating The Extension"}]},{"type":"element","tagName":"p","properties":{},"children":[{"type":"text","value":"We will not get into details on how to create a Chrome Extension, as it is not the purpose. If you need to learn more details about it you can check out "},{"type":"element","tagName":"a","properties":{"href":"https://blog.shahednasser.com/chrome-extension-tutorial-replace-images-in-any-website-with-pikachu/"},"children":[{"type":"text","value":"this tutorial"}]},{"type":"text","value":"."}]},{"type":"element","tagName":"p","properties":{},"children":[{"type":"text","value":"Create "},{"type":"element","tagName":"code","properties":{"className":["language-text"]},"children":[{"type":"text","value":"manifest.json"}]},{"type":"text","value":" in the root of your extension directory with the following content:"}]},{"type":"element","tagName":"div","properties":{"className":["kg-card","kg-code-card","gatsby-highlight"],"dataLanguage":"json"},"children":[{"type":"element","tagName":"pre","properties":{"className":["language-json"]},"children":[{"type":"element","tagName":"code","properties":{"className":["language-json"]},"children":[{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"{"}]},{"type":"text","value":"\n    "},{"type":"element","tagName":"span","properties":{"className":["token","property"]},"children":[{"type":"text","value":"\"name\""}]},{"type":"element","tagName":"span","properties":{"className":["token","operator"]},"children":[{"type":"text","value":":"}]},{"type":"text","value":" "},{"type":"element","tagName":"span","properties":{"className":["token","string"]},"children":[{"type":"text","value":"\"Screenshots\""}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":","}]},{"type":"text","value":"\n    "},{"type":"element","tagName":"span","properties":{"className":["token","property"]},"children":[{"type":"text","value":"\"version\""}]},{"type":"element","tagName":"span","properties":{"className":["token","operator"]},"children":[{"type":"text","value":":"}]},{"type":"text","value":" "},{"type":"element","tagName":"span","properties":{"className":["token","string"]},"children":[{"type":"text","value":"\"0.0.1\""}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":","}]},{"type":"text","value":"\n    "},{"type":"element","tagName":"span","properties":{"className":["token","property"]},"children":[{"type":"text","value":"\"description\""}]},{"type":"element","tagName":"span","properties":{"className":["token","operator"]},"children":[{"type":"text","value":":"}]},{"type":"text","value":" "},{"type":"element","tagName":"span","properties":{"className":["token","string"]},"children":[{"type":"text","value":"\"Take screenshots\""}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":","}]},{"type":"text","value":"\n    "},{"type":"element","tagName":"span","properties":{"className":["token","property"]},"children":[{"type":"text","value":"\"manifest_version\""}]},{"type":"element","tagName":"span","properties":{"className":["token","operator"]},"children":[{"type":"text","value":":"}]},{"type":"text","value":" "},{"type":"element","tagName":"span","properties":{"className":["token","number"]},"children":[{"type":"text","value":"3"}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":","}]},{"type":"text","value":"\n    "},{"type":"element","tagName":"span","properties":{"className":["token","property"]},"children":[{"type":"text","value":"\"action\""}]},{"type":"element","tagName":"span","properties":{"className":["token","operator"]},"children":[{"type":"text","value":":"}]},{"type":"text","value":" "},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"{"}]},{"type":"text","value":"\n        "},{"type":"element","tagName":"span","properties":{"className":["token","property"]},"children":[{"type":"text","value":"\"default_title\""}]},{"type":"element","tagName":"span","properties":{"className":["token","operator"]},"children":[{"type":"text","value":":"}]},{"type":"text","value":" "},{"type":"element","tagName":"span","properties":{"className":["token","string"]},"children":[{"type":"text","value":"\"Take a Screenshot\""}]},{"type":"text","value":"\n    "},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"}"}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":","}]},{"type":"text","value":"\n    "},{"type":"element","tagName":"span","properties":{"className":["token","property"]},"children":[{"type":"text","value":"\"icons\""}]},{"type":"element","tagName":"span","properties":{"className":["token","operator"]},"children":[{"type":"text","value":":"}]},{"type":"text","value":" "},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"{"}]},{"type":"text","value":"\n        "},{"type":"element","tagName":"span","properties":{"className":["token","property"]},"children":[{"type":"text","value":"\"16\""}]},{"type":"element","tagName":"span","properties":{"className":["token","operator"]},"children":[{"type":"text","value":":"}]},{"type":"text","value":" "},{"type":"element","tagName":"span","properties":{"className":["token","string"]},"children":[{"type":"text","value":"\"/assets/icon-16.png\""}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":","}]},{"type":"text","value":"\n        "},{"type":"element","tagName":"span","properties":{"className":["token","property"]},"children":[{"type":"text","value":"\"32\""}]},{"type":"element","tagName":"span","properties":{"className":["token","operator"]},"children":[{"type":"text","value":":"}]},{"type":"text","value":" "},{"type":"element","tagName":"span","properties":{"className":["token","string"]},"children":[{"type":"text","value":"\"/assets/icon-32.png\""}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":","}]},{"type":"text","value":"\n        "},{"type":"element","tagName":"span","properties":{"className":["token","property"]},"children":[{"type":"text","value":"\"48\""}]},{"type":"element","tagName":"span","properties":{"className":["token","operator"]},"children":[{"type":"text","value":":"}]},{"type":"text","value":" "},{"type":"element","tagName":"span","properties":{"className":["token","string"]},"children":[{"type":"text","value":"\"/assets/icon-48.png\""}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":","}]},{"type":"text","value":"\n        "},{"type":"element","tagName":"span","properties":{"className":["token","property"]},"children":[{"type":"text","value":"\"128\""}]},{"type":"element","tagName":"span","properties":{"className":["token","operator"]},"children":[{"type":"text","value":":"}]},{"type":"text","value":" "},{"type":"element","tagName":"span","properties":{"className":["token","string"]},"children":[{"type":"text","value":"\"/assets/icon-128.png\""}]},{"type":"text","value":"\n    "},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"}"}]},{"type":"text","value":"\n"},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"}"}]}]}]}]},{"type":"element","tagName":"p","properties":{},"children":[{"type":"text","value":"The icons we are using for this extension are by "},{"type":"element","tagName":"a","properties":{"href":"https://iconscout.com/contributors/bzzricon"},"children":[{"type":"text","value":"BZZRICON Studio"}]},{"type":"text","value":" on "},{"type":"element","tagName":"a","properties":{"href":"https://iconscout.com/"},"children":[{"type":"text","value":"Iconscout"}]},{"type":"text","value":"."}]},{"type":"element","tagName":"p","properties":{},"children":[{"type":"text","value":"For Manifest V2, make sure the "},{"type":"element","tagName":"code","properties":{"className":["language-text"]},"children":[{"type":"text","value":"manifest_version"}]},{"type":"text","value":" is set to 2:"}]},{"type":"element","tagName":"div","properties":{"className":["kg-card","kg-code-card","gatsby-highlight"],"dataLanguage":"json"},"children":[{"type":"element","tagName":"pre","properties":{"className":["language-json"]},"children":[{"type":"element","tagName":"code","properties":{"className":["language-json"]},"children":[{"type":"element","tagName":"span","properties":{"className":["token","property"]},"children":[{"type":"text","value":"\"manifest_version\""}]},{"type":"element","tagName":"span","properties":{"className":["token","operator"]},"children":[{"type":"text","value":":"}]},{"type":"text","value":" "},{"type":"element","tagName":"span","properties":{"className":["token","number"]},"children":[{"type":"text","value":"2"}]}]}]}]},{"type":"element","tagName":"p","properties":{},"children":[{"type":"text","value":"and make sure to replace "},{"type":"element","tagName":"code","properties":{"className":["language-text"]},"children":[{"type":"text","value":"action"}]},{"type":"text","value":" with "},{"type":"element","tagName":"code","properties":{"className":["language-text"]},"children":[{"type":"text","value":"browser_action"}]},{"type":"text","value":":"}]},{"type":"element","tagName":"div","properties":{"className":["kg-card","kg-code-card","gatsby-highlight"],"dataLanguage":"json"},"children":[{"type":"element","tagName":"pre","properties":{"className":["language-json"]},"children":[{"type":"element","tagName":"code","properties":{"className":["language-json"]},"children":[{"type":"element","tagName":"span","properties":{"className":["token","property"]},"children":[{"type":"text","value":"\"browser_action\""}]},{"type":"element","tagName":"span","properties":{"className":["token","operator"]},"children":[{"type":"text","value":":"}]},{"type":"text","value":" "},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"{"}]},{"type":"text","value":"\n\t"},{"type":"element","tagName":"span","properties":{"className":["token","property"]},"children":[{"type":"text","value":"\"default_title\""}]},{"type":"element","tagName":"span","properties":{"className":["token","operator"]},"children":[{"type":"text","value":":"}]},{"type":"text","value":" "},{"type":"element","tagName":"span","properties":{"className":["token","string"]},"children":[{"type":"text","value":"\"Take a Screenshot\""}]},{"type":"text","value":"\n"},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"}"}]}]}]}]},{"type":"element","tagName":"p","properties":{},"children":[{"type":"text","value":"Then, create a zip of, go to "},{"type":"element","tagName":"a","properties":{"href":"chrome://extensions"},"children":[{"type":"text","value":"chrome://extensions"}]},{"type":"text","value":", enable Developer Mode from the top right if it isn't enabled, click \"Load Unpacked\" from the buttons on the left, and choose the directory of the extension. Our extension will be added successfully."}]},{"type":"element","tagName":"h2","properties":{"id":"add-service-worker-or-background-script-"},"children":[{"type":"text","value":"Add Service Worker (or Background Script)"}]},{"type":"element","tagName":"p","properties":{},"children":[{"type":"text","value":"To detect when a user clicks on the extension's icon, we need to attach an event listener to "},{"type":"element","tagName":"code","properties":{"className":["language-text"]},"children":[{"type":"text","value":"chrome.action.onClicked"}]},{"type":"text","value":". To do that, we need to add a service worker (or background script for V2)."}]},{"type":"element","tagName":"p","properties":{},"children":[{"type":"text","value":"To add a service worker, add the following in "},{"type":"element","tagName":"code","properties":{"className":["language-text"]},"children":[{"type":"text","value":"manifest.json"}]},{"type":"text","value":":"}]},{"type":"element","tagName":"div","properties":{"className":["kg-card","kg-code-card","gatsby-highlight"],"dataLanguage":"json"},"children":[{"type":"element","tagName":"pre","properties":{"className":["language-json"]},"children":[{"type":"element","tagName":"code","properties":{"className":["language-json"]},"children":[{"type":"element","tagName":"span","properties":{"className":["token","property"]},"children":[{"type":"text","value":"\"background\""}]},{"type":"element","tagName":"span","properties":{"className":["token","operator"]},"children":[{"type":"text","value":":"}]},{"type":"text","value":" "},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"{"}]},{"type":"text","value":"\n\t"},{"type":"element","tagName":"span","properties":{"className":["token","property"]},"children":[{"type":"text","value":"\"service_worker\""}]},{"type":"element","tagName":"span","properties":{"className":["token","operator"]},"children":[{"type":"text","value":":"}]},{"type":"text","value":" "},{"type":"element","tagName":"span","properties":{"className":["token","string"]},"children":[{"type":"text","value":"\"background.js\""}]},{"type":"text","value":"\n"},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"}"}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":","}]}]}]}]},{"type":"element","tagName":"p","properties":{},"children":[{"type":"text","value":"Or the following for V2:"}]},{"type":"element","tagName":"div","properties":{"className":["kg-card","kg-code-card","gatsby-highlight"],"dataLanguage":"json"},"children":[{"type":"element","tagName":"pre","properties":{"className":["language-json"]},"children":[{"type":"element","tagName":"code","properties":{"className":["language-json"]},"children":[{"type":"element","tagName":"span","properties":{"className":["token","property"]},"children":[{"type":"text","value":"\"background\""}]},{"type":"element","tagName":"span","properties":{"className":["token","operator"]},"children":[{"type":"text","value":":"}]},{"type":"text","value":" "},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"{"}]},{"type":"text","value":"\n\t"},{"type":"element","tagName":"span","properties":{"className":["token","property"]},"children":[{"type":"text","value":"\"scripts\""}]},{"type":"element","tagName":"span","properties":{"className":["token","operator"]},"children":[{"type":"text","value":":"}]},{"type":"text","value":" "},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"["}]},{"type":"element","tagName":"span","properties":{"className":["token","string"]},"children":[{"type":"text","value":"\"background.js\""}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"]"}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":","}]},{"type":"text","value":"\n\t"},{"type":"element","tagName":"span","properties":{"className":["token","property"]},"children":[{"type":"text","value":"\"persistent\""}]},{"type":"element","tagName":"span","properties":{"className":["token","operator"]},"children":[{"type":"text","value":":"}]},{"type":"text","value":" "},{"type":"element","tagName":"span","properties":{"className":["token","boolean"]},"children":[{"type":"text","value":"false"}]},{"type":"text","value":"\n"},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"}"}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":","}]}]}]}]},{"type":"element","tagName":"p","properties":{},"children":[{"type":"text","value":"Next, create "},{"type":"element","tagName":"code","properties":{"className":["language-text"]},"children":[{"type":"text","value":"background.js"}]},{"type":"text","value":" in the root of the extension with the following content:"}]},{"type":"element","tagName":"div","properties":{"className":["kg-card","kg-code-card","gatsby-highlight"],"dataLanguage":"js"},"children":[{"type":"element","tagName":"pre","properties":{"className":["language-js"]},"children":[{"type":"element","tagName":"code","properties":{"className":["language-js"]},"children":[{"type":"text","value":"chrome"},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"."}]},{"type":"text","value":"action"},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"."}]},{"type":"text","value":"onClicked"},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"."}]},{"type":"element","tagName":"span","properties":{"className":["token","function"]},"children":[{"type":"text","value":"addListener"}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"("}]},{"type":"element","tagName":"span","properties":{"className":["token","keyword"]},"children":[{"type":"text","value":"function"}]},{"type":"text","value":" "},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"("}]},{"type":"element","tagName":"span","properties":{"className":["token","parameter"]},"children":[{"type":"text","value":"tab"}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":")"}]},{"type":"text","value":" "},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"{"}]},{"type":"text","value":"\n    \n"},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"}"}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":")"}]}]}]}]},{"type":"element","tagName":"p","properties":{},"children":[{"type":"text","value":"for V2 it should be the following:"}]},{"type":"element","tagName":"div","properties":{"className":["kg-card","kg-code-card","gatsby-highlight"],"dataLanguage":"js"},"children":[{"type":"element","tagName":"pre","properties":{"className":["language-js"]},"children":[{"type":"element","tagName":"code","properties":{"className":["language-js"]},"children":[{"type":"text","value":"chrome"},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"."}]},{"type":"text","value":"browserAction"},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"."}]},{"type":"text","value":"onClicked"},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"."}]},{"type":"element","tagName":"span","properties":{"className":["token","function"]},"children":[{"type":"text","value":"addListener"}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"("}]},{"type":"element","tagName":"span","properties":{"className":["token","keyword"]},"children":[{"type":"text","value":"function"}]},{"type":"text","value":" "},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"("}]},{"type":"element","tagName":"span","properties":{"className":["token","parameter"]},"children":[{"type":"text","value":"tab"}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":")"}]},{"type":"text","value":" "},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"{"}]},{"type":"text","value":"\n    \n"},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"}"}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":")"}]}]}]}]},{"type":"element","tagName":"p","properties":{},"children":[{"type":"text","value":"Note that if you don't have the "},{"type":"element","tagName":"code","properties":{"className":["language-text"]},"children":[{"type":"text","value":"action"}]},{"type":"text","value":" key in "},{"type":"element","tagName":"code","properties":{"className":["language-text"]},"children":[{"type":"text","value":"manifest.json"}]},{"type":"text","value":", you will not be able to add a listener to "},{"type":"element","tagName":"code","properties":{"className":["language-text"]},"children":[{"type":"text","value":"onClicked"}]},{"type":"text","value":". "}]},{"type":"element","tagName":"p","properties":{},"children":[{"type":"text","value":"Next, we'll start the \"take screenshot\" process. To do that, we'll use the "},{"type":"element","tagName":"a","properties":{"href":"https://developer.chrome.com/docs/extensions/reference/desktopCapture/"},"children":[{"type":"text","value":"Desktop Capture API"}]},{"type":"text","value":". In particular, we'll use the method "},{"type":"element","tagName":"code","properties":{"className":["language-text"]},"children":[{"type":"text","value":"chrome.desktopCapture.chooseDesktopMedia"}]},{"type":"text","value":" which takes 3 parameters: The first is an array of strings of capture sources, which can be \"screen\", \"window\", \"tab\", and \"audio\". The second parameter is the target tab which is optional, however, in some cases if the target tab is not passed Chrome crashes. The third parameter is a callback that returns the stream id which we'll use later to get a screenshot."}]},{"type":"element","tagName":"p","properties":{},"children":[{"type":"text","value":"add the following inside the listener:"}]},{"type":"element","tagName":"div","properties":{"className":["kg-card","kg-code-card","gatsby-highlight"],"dataLanguage":"js"},"children":[{"type":"element","tagName":"pre","properties":{"className":["language-js"]},"children":[{"type":"element","tagName":"code","properties":{"className":["language-js"]},"children":[{"type":"text","value":"chrome"},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"."}]},{"type":"text","value":"desktopCapture"},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"."}]},{"type":"element","tagName":"span","properties":{"className":["token","function"]},"children":[{"type":"text","value":"chooseDesktopMedia"}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"("}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"["}]},{"type":"text","value":"\n        "},{"type":"element","tagName":"span","properties":{"className":["token","string"]},"children":[{"type":"text","value":"\"screen\""}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":","}]},{"type":"text","value":"\n        "},{"type":"element","tagName":"span","properties":{"className":["token","string"]},"children":[{"type":"text","value":"\"window\""}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":","}]},{"type":"text","value":"\n        "},{"type":"element","tagName":"span","properties":{"className":["token","string"]},"children":[{"type":"text","value":"\"tab\""}]},{"type":"text","value":"\n    "},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"]"}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":","}]},{"type":"text","value":" tab"},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":","}]},{"type":"text","value":" "},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"("}]},{"type":"element","tagName":"span","properties":{"className":["token","parameter"]},"children":[{"type":"text","value":"streamId"}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":")"}]},{"type":"text","value":" "},{"type":"element","tagName":"span","properties":{"className":["token","operator"]},"children":[{"type":"text","value":"=>"}]},{"type":"text","value":" "},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"{"}]},{"type":"text","value":"\n        "},{"type":"element","tagName":"span","properties":{"className":["token","comment"]},"children":[{"type":"text","value":"//check whether the user canceled the request or not"}]},{"type":"text","value":"\n        "},{"type":"element","tagName":"span","properties":{"className":["token","keyword"]},"children":[{"type":"text","value":"if"}]},{"type":"text","value":" "},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"("}]},{"type":"text","value":"streamId "},{"type":"element","tagName":"span","properties":{"className":["token","operator"]},"children":[{"type":"text","value":"&&"}]},{"type":"text","value":" streamId"},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"."}]},{"type":"text","value":"length"},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":")"}]},{"type":"text","value":" "},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"{"}]},{"type":"text","value":"\n            \n        "},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"}"}]},{"type":"text","value":"\n    "},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"}"}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":")"}]}]}]}]},{"type":"element","tagName":"p","properties":{},"children":[{"type":"text","value":"Notice that we're passing in the first parameter \"screen\", \"window\", and \"tab\" as the allowed source types. The second parameter is the "},{"type":"element","tagName":"code","properties":{"className":["language-text"]},"children":[{"type":"text","value":"tab"}]},{"type":"text","value":" parameter passed to the listener, and the third is the callback function. We're checking if "},{"type":"element","tagName":"code","properties":{"className":["language-text"]},"children":[{"type":"text","value":"streamId"}]},{"type":"text","value":" is not empty since it will be "},{"type":"element","tagName":"code","properties":{"className":["language-text"]},"children":[{"type":"text","value":"empty"}]},{"type":"text","value":" if the user cancels the request."}]},{"type":"element","tagName":"p","properties":{},"children":[{"type":"text","value":"Before we can use this though, we need to add some permissions in the "},{"type":"element","tagName":"code","properties":{"className":["language-text"]},"children":[{"type":"text","value":"manifest.json"}]},{"type":"text","value":". Permissions allow the user to understand what the extension is doing and agree to it before it is installed in their browser."}]},{"type":"element","tagName":"p","properties":{},"children":[{"type":"text","value":"Add the following to "},{"type":"element","tagName":"code","properties":{"className":["language-text"]},"children":[{"type":"text","value":"manifest.json"}]},{"type":"text","value":":"}]},{"type":"element","tagName":"div","properties":{"className":["kg-card","kg-code-card","gatsby-highlight"],"dataLanguage":"json"},"children":[{"type":"element","tagName":"pre","properties":{"className":["language-json"]},"children":[{"type":"element","tagName":"code","properties":{"className":["language-json"]},"children":[{"type":"element","tagName":"span","properties":{"className":["token","property"]},"children":[{"type":"text","value":"\"permissions\""}]},{"type":"element","tagName":"span","properties":{"className":["token","operator"]},"children":[{"type":"text","value":":"}]},{"type":"text","value":" "},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"["}]},{"type":"text","value":"\n    "},{"type":"element","tagName":"span","properties":{"className":["token","string"]},"children":[{"type":"text","value":"\"desktopCapture\""}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":","}]},{"type":"text","value":"\n    "},{"type":"element","tagName":"span","properties":{"className":["token","string"]},"children":[{"type":"text","value":"\"tabs\""}]},{"type":"text","value":"\n"},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"]"}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":","}]}]}]}]},{"type":"element","tagName":"p","properties":{},"children":[{"type":"text","value":"The reason we also need the "},{"type":"element","tagName":"code","properties":{"className":["language-text"]},"children":[{"type":"text","value":"tabs"}]},{"type":"text","value":" permission is because if we don't have the permission, the "},{"type":"element","tagName":"code","properties":{"className":["language-text"]},"children":[{"type":"text","value":"tab"}]},{"type":"text","value":" object passed to the "},{"type":"element","tagName":"code","properties":{"className":["language-text"]},"children":[{"type":"text","value":"onClicked"}]},{"type":"text","value":" event listener will not have the "},{"type":"element","tagName":"code","properties":{"className":["language-text"]},"children":[{"type":"text","value":"url"}]},{"type":"text","value":" parameter which is required for "},{"type":"element","tagName":"code","properties":{"className":["language-text"]},"children":[{"type":"text","value":"chooseDesktopMedia"}]},{"type":"text","value":" when passing that tab as a parameter."}]},{"type":"element","tagName":"p","properties":{},"children":[{"type":"text","value":"So, if you reload the extension now and press the icon, you'll see that it will ask you what screen do you want to record and that's it. Next, we need to use the "},{"type":"element","tagName":"code","properties":{"className":["language-text"]},"children":[{"type":"text","value":"streamId"}]},{"type":"text","value":" to get the screenshot."}]},{"type":"element","tagName":"h2","properties":{"id":"add-content-script"},"children":[{"type":"text","value":"Add Content Script"}]},{"type":"element","tagName":"p","properties":{},"children":[{"type":"text","value":"To obtain the stream from the "},{"type":"element","tagName":"code","properties":{"className":["language-text"]},"children":[{"type":"text","value":"streamId"}]},{"type":"text","value":", we need to use "},{"type":"element","tagName":"a","properties":{"href":"https://developer.mozilla.org/en-US/docs/Web/API/MediaDevices/getUserMedia"},"children":[{"type":"text","value":"getUserMedia"}]},{"type":"text","value":". However, this is not available in the service worker. So, we need to create a content script that receives a message from the service worker with the stream id, then gets the screenshot from the stream."}]},{"type":"element","tagName":"p","properties":{},"children":[{"type":"text","value":"To add a content script, add the following to "},{"type":"element","tagName":"code","properties":{"className":["language-text"]},"children":[{"type":"text","value":"manifest.json"}]},{"type":"text","value":":"}]},{"type":"element","tagName":"div","properties":{"className":["kg-card","kg-code-card","gatsby-highlight"],"dataLanguage":"json"},"children":[{"type":"element","tagName":"pre","properties":{"className":["language-json"]},"children":[{"type":"element","tagName":"code","properties":{"className":["language-json"]},"children":[{"type":"element","tagName":"span","properties":{"className":["token","property"]},"children":[{"type":"text","value":"\"content_scripts\""}]},{"type":"element","tagName":"span","properties":{"className":["token","operator"]},"children":[{"type":"text","value":":"}]},{"type":"text","value":" "},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"["}]},{"type":"text","value":"\n    "},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"{"}]},{"type":"text","value":"\n    \t"},{"type":"element","tagName":"span","properties":{"className":["token","property"]},"children":[{"type":"text","value":"\"matches\""}]},{"type":"element","tagName":"span","properties":{"className":["token","operator"]},"children":[{"type":"text","value":":"}]},{"type":"text","value":" "},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"["}]},{"type":"element","tagName":"span","properties":{"className":["token","string"]},"children":[{"type":"text","value":"\"<all_urls>\""}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"]"}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":","}]},{"type":"text","value":"\n    \t"},{"type":"element","tagName":"span","properties":{"className":["token","property"]},"children":[{"type":"text","value":"\"js\""}]},{"type":"element","tagName":"span","properties":{"className":["token","operator"]},"children":[{"type":"text","value":":"}]},{"type":"text","value":" "},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"["}]},{"type":"element","tagName":"span","properties":{"className":["token","string"]},"children":[{"type":"text","value":"\"content_script.js\""}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"]"}]},{"type":"text","value":"\n    "},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"}"}]},{"type":"text","value":"\n"},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"]"}]}]}]}]},{"type":"element","tagName":"p","properties":{},"children":[{"type":"text","value":"Then, create "},{"type":"element","tagName":"code","properties":{"className":["language-text"]},"children":[{"type":"text","value":"content_script.js"}]},{"type":"text","value":" in the root of the extension with the following content:"}]},{"type":"element","tagName":"div","properties":{"className":["kg-card","kg-code-card","gatsby-highlight"],"dataLanguage":"js"},"children":[{"type":"element","tagName":"pre","properties":{"className":["language-js"]},"children":[{"type":"element","tagName":"code","properties":{"className":["language-js"]},"children":[{"type":"text","value":"chrome"},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"."}]},{"type":"text","value":"runtime"},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"."}]},{"type":"text","value":"onMessage"},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"."}]},{"type":"element","tagName":"span","properties":{"className":["token","function"]},"children":[{"type":"text","value":"addListener"}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"("}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"("}]},{"type":"element","tagName":"span","properties":{"className":["token","parameter"]},"children":[{"type":"text","value":"message"},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":","}]},{"type":"text","value":" sender"},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":","}]},{"type":"text","value":" senderResponse"}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":")"}]},{"type":"text","value":" "},{"type":"element","tagName":"span","properties":{"className":["token","operator"]},"children":[{"type":"text","value":"=>"}]},{"type":"text","value":" "},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"{"}]},{"type":"text","value":"\n    "},{"type":"element","tagName":"span","properties":{"className":["token","keyword"]},"children":[{"type":"text","value":"if"}]},{"type":"text","value":" "},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"("}]},{"type":"text","value":"message"},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"."}]},{"type":"text","value":"name "},{"type":"element","tagName":"span","properties":{"className":["token","operator"]},"children":[{"type":"text","value":"==="}]},{"type":"text","value":" "},{"type":"element","tagName":"span","properties":{"className":["token","string"]},"children":[{"type":"text","value":"'stream'"}]},{"type":"text","value":" "},{"type":"element","tagName":"span","properties":{"className":["token","operator"]},"children":[{"type":"text","value":"&&"}]},{"type":"text","value":" message"},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"."}]},{"type":"text","value":"streamId"},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":")"}]},{"type":"text","value":" "},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"{"}]},{"type":"text","value":"\n        \n    "},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"}"}]},{"type":"text","value":"\n"},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"}"}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":")"}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":";"}]}]}]}]},{"type":"element","tagName":"p","properties":{},"children":[{"type":"text","value":"This code listens for the \"onMessage\" event and checks if the "},{"type":"element","tagName":"code","properties":{"className":["language-text"]},"children":[{"type":"text","value":"message"}]},{"type":"text","value":" received has a "},{"type":"element","tagName":"code","properties":{"className":["language-text"]},"children":[{"type":"text","value":"name"}]},{"type":"text","value":" property that's equal to "},{"type":"element","tagName":"code","properties":{"className":["language-text"]},"children":[{"type":"text","value":"stream"}]},{"type":"text","value":" and has a "},{"type":"element","tagName":"code","properties":{"className":["language-text"]},"children":[{"type":"text","value":"streamId"}]},{"type":"text","value":" property, then we'll obtain the stream and take a screenshot of it."}]},{"type":"element","tagName":"p","properties":{},"children":[{"type":"text","value":"Inside the if, we'll use "},{"type":"element","tagName":"code","properties":{"className":["language-text"]},"children":[{"type":"text","value":"getUserMedia"}]},{"type":"text","value":" which returns a Promise that resolves to a "},{"type":"element","tagName":"a","properties":{"href":"https://developer.mozilla.org/en-US/docs/Web/API/MediaStream"},"children":[{"type":"text","value":"MediaStream"}]},{"type":"text","value":":"}]},{"type":"element","tagName":"div","properties":{"className":["kg-card","kg-code-card","gatsby-highlight"],"dataLanguage":"js"},"children":[{"type":"element","tagName":"pre","properties":{"className":["language-js"]},"children":[{"type":"element","tagName":"code","properties":{"className":["language-js"]},"children":[{"type":"element","tagName":"span","properties":{"className":["token","keyword"]},"children":[{"type":"text","value":"let"}]},{"type":"text","value":" track"},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":","}]},{"type":"text","value":" canvas\nnavigator"},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"."}]},{"type":"text","value":"mediaDevices"},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"."}]},{"type":"element","tagName":"span","properties":{"className":["token","function"]},"children":[{"type":"text","value":"getUserMedia"}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"("}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"{"}]},{"type":"text","value":"\n    "},{"type":"element","tagName":"span","properties":{"className":["token","literal-property","property"]},"children":[{"type":"text","value":"video"}]},{"type":"element","tagName":"span","properties":{"className":["token","operator"]},"children":[{"type":"text","value":":"}]},{"type":"text","value":" "},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"{"}]},{"type":"text","value":"\n        "},{"type":"element","tagName":"span","properties":{"className":["token","literal-property","property"]},"children":[{"type":"text","value":"mandatory"}]},{"type":"element","tagName":"span","properties":{"className":["token","operator"]},"children":[{"type":"text","value":":"}]},{"type":"text","value":" "},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"{"}]},{"type":"text","value":"\n            "},{"type":"element","tagName":"span","properties":{"className":["token","literal-property","property"]},"children":[{"type":"text","value":"chromeMediaSource"}]},{"type":"element","tagName":"span","properties":{"className":["token","operator"]},"children":[{"type":"text","value":":"}]},{"type":"text","value":" "},{"type":"element","tagName":"span","properties":{"className":["token","string"]},"children":[{"type":"text","value":"'desktop'"}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":","}]},{"type":"text","value":"\n            "},{"type":"element","tagName":"span","properties":{"className":["token","literal-property","property"]},"children":[{"type":"text","value":"chromeMediaSourceId"}]},{"type":"element","tagName":"span","properties":{"className":["token","operator"]},"children":[{"type":"text","value":":"}]},{"type":"text","value":" message"},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"."}]},{"type":"text","value":"streamId\n        "},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"}"}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":","}]},{"type":"text","value":"\n    "},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"}"}]},{"type":"text","value":"\n"},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"}"}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":")"}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"."}]},{"type":"element","tagName":"span","properties":{"className":["token","function"]},"children":[{"type":"text","value":"then"}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"("}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"("}]},{"type":"element","tagName":"span","properties":{"className":["token","parameter"]},"children":[{"type":"text","value":"stream"}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":")"}]},{"type":"text","value":" "},{"type":"element","tagName":"span","properties":{"className":["token","operator"]},"children":[{"type":"text","value":"=>"}]},{"type":"text","value":" "},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"{"}]},{"type":"text","value":"\n    \n"},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"}"}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":")"}]}]}]}]},{"type":"element","tagName":"p","properties":{},"children":[{"type":"text","value":"Notice that the parameter we passed to "},{"type":"element","tagName":"code","properties":{"className":["language-text"]},"children":[{"type":"text","value":"getUserMedia"}]},{"type":"text","value":" takes an object of options. We're passing the "},{"type":"element","tagName":"code","properties":{"className":["language-text"]},"children":[{"type":"text","value":"chromeMediaSource"}]},{"type":"text","value":" which equals to "},{"type":"element","tagName":"code","properties":{"className":["language-text"]},"children":[{"type":"text","value":"desktop"}]},{"type":"text","value":", and "},{"type":"element","tagName":"code","properties":{"className":["language-text"]},"children":[{"type":"text","value":"chromeMediaSourceId"}]},{"type":"text","value":" which equals to the stream Id we received."}]},{"type":"element","tagName":"p","properties":{},"children":[{"type":"text","value":"Next, inside the callback function for the resolved promise, we'll get the "},{"type":"element","tagName":"a","properties":{"href":"https://developer.mozilla.org/en-US/docs/Web/API/MediaStreamTrack"},"children":[{"type":"text","value":"MediaStreamTrack"}]},{"type":"text","value":" and then capture a screenshot from it using the "},{"type":"element","tagName":"a","properties":{"href":"https://developer.mozilla.org/en-US/docs/Web/API/ImageCapture"},"children":[{"type":"text","value":"ImageCapture"}]},{"type":"text","value":" API:"}]},{"type":"element","tagName":"div","properties":{"className":["kg-card","kg-code-card","gatsby-highlight"],"dataLanguage":"js"},"children":[{"type":"element","tagName":"pre","properties":{"className":["language-js"]},"children":[{"type":"element","tagName":"code","properties":{"className":["language-js"]},"children":[{"type":"text","value":"track "},{"type":"element","tagName":"span","properties":{"className":["token","operator"]},"children":[{"type":"text","value":"="}]},{"type":"text","value":" stream"},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"."}]},{"type":"element","tagName":"span","properties":{"className":["token","function"]},"children":[{"type":"text","value":"getVideoTracks"}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"("}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":")"}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"["}]},{"type":"element","tagName":"span","properties":{"className":["token","number"]},"children":[{"type":"text","value":"0"}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"]"}]},{"type":"text","value":"\n"},{"type":"element","tagName":"span","properties":{"className":["token","keyword"]},"children":[{"type":"text","value":"const"}]},{"type":"text","value":" imageCapture "},{"type":"element","tagName":"span","properties":{"className":["token","operator"]},"children":[{"type":"text","value":"="}]},{"type":"text","value":" "},{"type":"element","tagName":"span","properties":{"className":["token","keyword"]},"children":[{"type":"text","value":"new"}]},{"type":"text","value":" "},{"type":"element","tagName":"span","properties":{"className":["token","class-name"]},"children":[{"type":"text","value":"ImageCapture"}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"("}]},{"type":"text","value":"track"},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":")"}]},{"type":"text","value":"\n"},{"type":"element","tagName":"span","properties":{"className":["token","keyword"]},"children":[{"type":"text","value":"return"}]},{"type":"text","value":" imageCapture"},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"."}]},{"type":"element","tagName":"span","properties":{"className":["token","function"]},"children":[{"type":"text","value":"grabFrame"}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"("}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":")"}]}]}]}]},{"type":"element","tagName":"p","properties":{},"children":[{"type":"text","value":"In the end, we're returning the value of "},{"type":"element","tagName":"code","properties":{"className":["language-text"]},"children":[{"type":"text","value":"imageCapture.grabFrame"}]},{"type":"text","value":" which returns a Promise that resolves to an ImageBitmap. Notice that we didn't use the "},{"type":"element","tagName":"code","properties":{"className":["language-text"]},"children":[{"type":"text","value":"takePhoto"}]},{"type":"text","value":" method of the "},{"type":"element","tagName":"code","properties":{"className":["language-text"]},"children":[{"type":"text","value":"ImageCapture"}]},{"type":"text","value":" API. The reason behind that is that there are known cases of a DOMException thrown using it and this is a "},{"type":"element","tagName":"a","properties":{"href":"https://github.com/GoogleChromeLabs/imagecapture-polyfill/issues/15"},"children":[{"type":"text","value":"workaround"}]},{"type":"text","value":" for it."}]},{"type":"element","tagName":"p","properties":{},"children":[{"type":"text","value":"Next, we'll attach another "},{"type":"element","tagName":"code","properties":{"className":["language-text"]},"children":[{"type":"text","value":"then"}]},{"type":"text","value":" method to handle the returned Promise from "},{"type":"element","tagName":"code","properties":{"className":["language-text"]},"children":[{"type":"text","value":"imageCapture.grabFrame"}]},{"type":"text","value":". The callback function will stop the stream, create a canvas and draw the ImageBitmap in it, then get the Data Url of the canvas:"}]},{"type":"element","tagName":"div","properties":{"className":["kg-card","kg-code-card","gatsby-highlight"],"dataLanguage":"js"},"children":[{"type":"element","tagName":"pre","properties":{"className":["language-js"]},"children":[{"type":"element","tagName":"code","properties":{"className":["language-js"]},"children":[{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"."}]},{"type":"element","tagName":"span","properties":{"className":["token","function"]},"children":[{"type":"text","value":"then"}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"("}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"("}]},{"type":"element","tagName":"span","properties":{"className":["token","parameter"]},"children":[{"type":"text","value":"bitmap"}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":")"}]},{"type":"text","value":" "},{"type":"element","tagName":"span","properties":{"className":["token","operator"]},"children":[{"type":"text","value":"=>"}]},{"type":"text","value":" "},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"{"}]},{"type":"text","value":"\n    track"},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"."}]},{"type":"element","tagName":"span","properties":{"className":["token","function"]},"children":[{"type":"text","value":"stop"}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"("}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":")"}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":";"}]},{"type":"text","value":"\n    canvas "},{"type":"element","tagName":"span","properties":{"className":["token","operator"]},"children":[{"type":"text","value":"="}]},{"type":"text","value":" document"},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"."}]},{"type":"element","tagName":"span","properties":{"className":["token","function"]},"children":[{"type":"text","value":"createElement"}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"("}]},{"type":"element","tagName":"span","properties":{"className":["token","string"]},"children":[{"type":"text","value":"'canvas'"}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":")"}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":";"}]},{"type":"text","value":"\n    canvas"},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"."}]},{"type":"text","value":"width "},{"type":"element","tagName":"span","properties":{"className":["token","operator"]},"children":[{"type":"text","value":"="}]},{"type":"text","value":" bitmap"},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"."}]},{"type":"text","value":"width"},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":";"}]},{"type":"text","value":"\n    canvas"},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"."}]},{"type":"text","value":"height "},{"type":"element","tagName":"span","properties":{"className":["token","operator"]},"children":[{"type":"text","value":"="}]},{"type":"text","value":" bitmap"},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"."}]},{"type":"text","value":"height"},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":";"}]},{"type":"text","value":"\n    "},{"type":"element","tagName":"span","properties":{"className":["token","keyword"]},"children":[{"type":"text","value":"let"}]},{"type":"text","value":" context "},{"type":"element","tagName":"span","properties":{"className":["token","operator"]},"children":[{"type":"text","value":"="}]},{"type":"text","value":" canvas"},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"."}]},{"type":"element","tagName":"span","properties":{"className":["token","function"]},"children":[{"type":"text","value":"getContext"}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"("}]},{"type":"element","tagName":"span","properties":{"className":["token","string"]},"children":[{"type":"text","value":"'2d'"}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":")"}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":";"}]},{"type":"text","value":"\n    context"},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"."}]},{"type":"element","tagName":"span","properties":{"className":["token","function"]},"children":[{"type":"text","value":"drawImage"}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"("}]},{"type":"text","value":"bitmap"},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":","}]},{"type":"text","value":" "},{"type":"element","tagName":"span","properties":{"className":["token","number"]},"children":[{"type":"text","value":"0"}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":","}]},{"type":"text","value":" "},{"type":"element","tagName":"span","properties":{"className":["token","number"]},"children":[{"type":"text","value":"0"}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":","}]},{"type":"text","value":" bitmap"},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"."}]},{"type":"text","value":"width"},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":","}]},{"type":"text","value":" bitmap"},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"."}]},{"type":"text","value":"height"},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":")"}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":";"}]},{"type":"text","value":"\n    "},{"type":"element","tagName":"span","properties":{"className":["token","keyword"]},"children":[{"type":"text","value":"return"}]},{"type":"text","value":" canvas"},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"."}]},{"type":"element","tagName":"span","properties":{"className":["token","function"]},"children":[{"type":"text","value":"toDataURL"}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"("}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":")"}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":";"}]},{"type":"text","value":"\n"},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"}"}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":")"}]}]}]}]},{"type":"element","tagName":"p","properties":{},"children":[{"type":"text","value":"Notice that it's important to set the width and height of the canvas to be equal to that of the "},{"type":"element","tagName":"code","properties":{"className":["language-text"]},"children":[{"type":"text","value":"bitmap"}]},{"type":"text","value":". If we don't, the canvas height and width will default to "},{"type":"element","tagName":"code","properties":{"className":["language-text"]},"children":[{"type":"text","value":"200px"}]},{"type":"text","value":" and if the width or height of the bitmap is larger than that then the screenshot will be cropped."}]},{"type":"element","tagName":"p","properties":{},"children":[{"type":"text","value":"In the end, we're returning "},{"type":"element","tagName":"code","properties":{"className":["language-text"]},"children":[{"type":"text","value":"canvas.toDataUrl"}]},{"type":"text","value":". We'll attach a last "},{"type":"element","tagName":"code","properties":{"className":["language-text"]},"children":[{"type":"text","value":"then"}]},{"type":"text","value":" method that takes the URL returned as a parameter. This URL will be used to download the image on the user's device:"}]},{"type":"element","tagName":"div","properties":{"className":["kg-card","kg-code-card","gatsby-highlight"],"dataLanguage":"js"},"children":[{"type":"element","tagName":"pre","properties":{"className":["language-js"]},"children":[{"type":"element","tagName":"code","properties":{"className":["language-js"]},"children":[{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"."}]},{"type":"element","tagName":"span","properties":{"className":["token","function"]},"children":[{"type":"text","value":"then"}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"("}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"("}]},{"type":"element","tagName":"span","properties":{"className":["token","parameter"]},"children":[{"type":"text","value":"url"}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":")"}]},{"type":"text","value":" "},{"type":"element","tagName":"span","properties":{"className":["token","operator"]},"children":[{"type":"text","value":"=>"}]},{"type":"text","value":" "},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"{"}]},{"type":"text","value":"\n    "},{"type":"element","tagName":"span","properties":{"className":["token","comment"]},"children":[{"type":"text","value":"//TODO download the image from the URL"}]},{"type":"text","value":"\n"},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"}"}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":")"}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"."}]},{"type":"element","tagName":"span","properties":{"className":["token","function"]},"children":[{"type":"text","value":"catch"}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"("}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"("}]},{"type":"element","tagName":"span","properties":{"className":["token","parameter"]},"children":[{"type":"text","value":"err"}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":")"}]},{"type":"text","value":" "},{"type":"element","tagName":"span","properties":{"className":["token","operator"]},"children":[{"type":"text","value":"=>"}]},{"type":"text","value":" "},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"{"}]},{"type":"text","value":"\n    "},{"type":"element","tagName":"span","properties":{"className":["token","function"]},"children":[{"type":"text","value":"alert"}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"("}]},{"type":"element","tagName":"span","properties":{"className":["token","string"]},"children":[{"type":"text","value":"\"Could not take screenshot\""}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":")"}]},{"type":"text","value":"\n    "},{"type":"element","tagName":"span","properties":{"className":["token","function"]},"children":[{"type":"text","value":"senderResponse"}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"("}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"{"}]},{"type":"element","tagName":"span","properties":{"className":["token","literal-property","property"]},"children":[{"type":"text","value":"success"}]},{"type":"element","tagName":"span","properties":{"className":["token","operator"]},"children":[{"type":"text","value":":"}]},{"type":"text","value":" "},{"type":"element","tagName":"span","properties":{"className":["token","boolean"]},"children":[{"type":"text","value":"false"}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":","}]},{"type":"text","value":" "},{"type":"element","tagName":"span","properties":{"className":["token","literal-property","property"]},"children":[{"type":"text","value":"message"}]},{"type":"element","tagName":"span","properties":{"className":["token","operator"]},"children":[{"type":"text","value":":"}]},{"type":"text","value":" err"},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"}"}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":")"}]},{"type":"text","value":"\n"},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"}"}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":")"}]}]}]}]},{"type":"element","tagName":"p","properties":{},"children":[{"type":"text","value":"Notice that we also added "},{"type":"element","tagName":"code","properties":{"className":["language-text"]},"children":[{"type":"text","value":"catch"}]},{"type":"text","value":" to catch any errors. As you can see in the "},{"type":"element","tagName":"code","properties":{"className":["language-text"]},"children":[{"type":"text","value":"catch"}]},{"type":"text","value":" callback, we're calling the function "},{"type":"element","tagName":"code","properties":{"className":["language-text"]},"children":[{"type":"text","value":"senderResponse"}]},{"type":"text","value":". This function is the one we'll pass from the service worker or background script to the content script when sending the message."}]},{"type":"element","tagName":"p","properties":{},"children":[{"type":"text","value":"At the end of the "},{"type":"element","tagName":"code","properties":{"className":["language-text"]},"children":[{"type":"text","value":"if"}]},{"type":"text","value":" block we'll add the following:"}]},{"type":"element","tagName":"div","properties":{"className":["kg-card","kg-code-card","gatsby-highlight"],"dataLanguage":"js"},"children":[{"type":"element","tagName":"pre","properties":{"className":["language-js"]},"children":[{"type":"element","tagName":"code","properties":{"className":["language-js"]},"children":[{"type":"element","tagName":"span","properties":{"className":["token","keyword"]},"children":[{"type":"text","value":"return"}]},{"type":"text","value":" "},{"type":"element","tagName":"span","properties":{"className":["token","boolean"]},"children":[{"type":"text","value":"true"}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":";"}]}]}]}]},{"type":"element","tagName":"p","properties":{},"children":[{"type":"text","value":"In a "},{"type":"element","tagName":"code","properties":{"className":["language-text"]},"children":[{"type":"text","value":"onMessage"}]},{"type":"text","value":" event listener if the listener returns true that means that we'll later return a response to the sender using the callback function they passed when sending the message."}]},{"type":"element","tagName":"h2","properties":{"id":"download-screenshot"},"children":[{"type":"text","value":"Download Screenshot"}]},{"type":"element","tagName":"p","properties":{},"children":[{"type":"text","value":"To download the screenshot, we'll use the "},{"type":"element","tagName":"a","properties":{"href":"https://developer.chrome.com/docs/extensions/reference/downloads"},"children":[{"type":"text","value":"Downloads"}]},{"type":"text","value":" API. It provides a lot of methods to manage downloads like search, open, remove, and more."}]},{"type":"element","tagName":"p","properties":{},"children":[{"type":"text","value":"Before we can use any of the methods, we need to add the "},{"type":"element","tagName":"code","properties":{"className":["language-text"]},"children":[{"type":"text","value":"downloads"}]},{"type":"text","value":" permission to the "},{"type":"element","tagName":"code","properties":{"className":["language-text"]},"children":[{"type":"text","value":"permissions"}]},{"type":"text","value":" array in "},{"type":"element","tagName":"code","properties":{"className":["language-text"]},"children":[{"type":"text","value":"manifest.json"}]},{"type":"text","value":":"}]},{"type":"element","tagName":"div","properties":{"className":["kg-card","kg-code-card","gatsby-highlight"],"dataLanguage":"json"},"children":[{"type":"element","tagName":"pre","properties":{"className":["language-json"]},"children":[{"type":"element","tagName":"code","properties":{"className":["language-json"]},"children":[{"type":"element","tagName":"span","properties":{"className":["token","property"]},"children":[{"type":"text","value":"\"permissions\""}]},{"type":"element","tagName":"span","properties":{"className":["token","operator"]},"children":[{"type":"text","value":":"}]},{"type":"text","value":" "},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"["}]},{"type":"text","value":"\n    "},{"type":"element","tagName":"span","properties":{"className":["token","string"]},"children":[{"type":"text","value":"\"desktopCapture\""}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":","}]},{"type":"text","value":"\n    "},{"type":"element","tagName":"span","properties":{"className":["token","string"]},"children":[{"type":"text","value":"\"tabs\""}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":","}]},{"type":"text","value":"\n    "},{"type":"element","tagName":"span","properties":{"className":["token","string"]},"children":[{"type":"text","value":"\"downloads\""}]},{"type":"text","value":"\n"},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"]"}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":","}]}]}]}]},{"type":"element","tagName":"p","properties":{},"children":[{"type":"text","value":"Now, we can use the methods of the Downloads API. We'll use the method "},{"type":"element","tagName":"code","properties":{"className":["language-text"]},"children":[{"type":"text","value":"chrome.downloads.download"}]},{"type":"text","value":" which takes an array of options as the first parameter and a callback function as the second."}]},{"type":"element","tagName":"p","properties":{},"children":[{"type":"text","value":"However, this method cannot be called from the content script. We need to call it from the service worker/background script. So, when we get to the "},{"type":"element","tagName":"code","properties":{"className":["language-text"]},"children":[{"type":"text","value":"TODO"}]},{"type":"text","value":" part in our code earlier, we need to send a message to the service worker with the URL we want to download. "}]},{"type":"element","tagName":"p","properties":{},"children":[{"type":"text","value":"To send a message in an extension, we use the "},{"type":"element","tagName":"code","properties":{"className":["language-text"]},"children":[{"type":"text","value":"chrome.runtime.sendMessage"}]},{"type":"text","value":" which takes as a first parameter the message to send (which can be of any type), and an optional callback function as the second parameter, which is the function that the receiver of the message should call to deliver the response."}]},{"type":"element","tagName":"p","properties":{},"children":[{"type":"text","value":"Add the following code in place of the "},{"type":"element","tagName":"code","properties":{"className":["language-text"]},"children":[{"type":"text","value":"TODO"}]},{"type":"text","value":" comment:"}]},{"type":"element","tagName":"div","properties":{"className":["kg-card","kg-code-card","gatsby-highlight"],"dataLanguage":"js"},"children":[{"type":"element","tagName":"pre","properties":{"className":["language-js"]},"children":[{"type":"element","tagName":"code","properties":{"className":["language-js"]},"children":[{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"."}]},{"type":"element","tagName":"span","properties":{"className":["token","function"]},"children":[{"type":"text","value":"then"}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"("}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"("}]},{"type":"element","tagName":"span","properties":{"className":["token","parameter"]},"children":[{"type":"text","value":"url"}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":")"}]},{"type":"text","value":" "},{"type":"element","tagName":"span","properties":{"className":["token","operator"]},"children":[{"type":"text","value":"=>"}]},{"type":"text","value":" "},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"{"}]},{"type":"text","value":"\n    chrome"},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"."}]},{"type":"text","value":"runtime"},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"."}]},{"type":"element","tagName":"span","properties":{"className":["token","function"]},"children":[{"type":"text","value":"sendMessage"}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"("}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"{"}]},{"type":"element","tagName":"span","properties":{"className":["token","literal-property","property"]},"children":[{"type":"text","value":"name"}]},{"type":"element","tagName":"span","properties":{"className":["token","operator"]},"children":[{"type":"text","value":":"}]},{"type":"text","value":" "},{"type":"element","tagName":"span","properties":{"className":["token","string"]},"children":[{"type":"text","value":"'download'"}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":","}]},{"type":"text","value":" url"},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"}"}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":","}]},{"type":"text","value":" "},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"("}]},{"type":"element","tagName":"span","properties":{"className":["token","parameter"]},"children":[{"type":"text","value":"response"}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":")"}]},{"type":"text","value":" "},{"type":"element","tagName":"span","properties":{"className":["token","operator"]},"children":[{"type":"text","value":"=>"}]},{"type":"text","value":" "},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"{"}]},{"type":"text","value":"\n        "},{"type":"element","tagName":"span","properties":{"className":["token","keyword"]},"children":[{"type":"text","value":"if"}]},{"type":"text","value":" "},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"("}]},{"type":"text","value":"response"},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"."}]},{"type":"text","value":"success"},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":")"}]},{"type":"text","value":" "},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"{"}]},{"type":"text","value":"\n            "},{"type":"element","tagName":"span","properties":{"className":["token","function"]},"children":[{"type":"text","value":"alert"}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"("}]},{"type":"element","tagName":"span","properties":{"className":["token","string"]},"children":[{"type":"text","value":"\"Screenshot saved\""}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":")"}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":";"}]},{"type":"text","value":"\n        "},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"}"}]},{"type":"text","value":" "},{"type":"element","tagName":"span","properties":{"className":["token","keyword"]},"children":[{"type":"text","value":"else"}]},{"type":"text","value":" "},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"{"}]},{"type":"text","value":"\n            "},{"type":"element","tagName":"span","properties":{"className":["token","function"]},"children":[{"type":"text","value":"alert"}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"("}]},{"type":"element","tagName":"span","properties":{"className":["token","string"]},"children":[{"type":"text","value":"\"Could not save screenshot\""}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":")"}]},{"type":"text","value":"\n        "},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"}"}]},{"type":"text","value":"\n        canvas"},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"."}]},{"type":"element","tagName":"span","properties":{"className":["token","function"]},"children":[{"type":"text","value":"remove"}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"("}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":")"}]},{"type":"text","value":"\n        "},{"type":"element","tagName":"span","properties":{"className":["token","function"]},"children":[{"type":"text","value":"senderResponse"}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"("}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"{"}]},{"type":"element","tagName":"span","properties":{"className":["token","literal-property","property"]},"children":[{"type":"text","value":"success"}]},{"type":"element","tagName":"span","properties":{"className":["token","operator"]},"children":[{"type":"text","value":":"}]},{"type":"text","value":" "},{"type":"element","tagName":"span","properties":{"className":["token","boolean"]},"children":[{"type":"text","value":"true"}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"}"}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":")"}]},{"type":"text","value":"\n    "},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"}"}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":")"}]},{"type":"text","value":"\n"},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"}"}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":")"}]}]}]}]},{"type":"element","tagName":"p","properties":{},"children":[{"type":"text","value":"Notice that we're sending the message "},{"type":"element","tagName":"code","properties":{"className":["language-text"]},"children":[{"type":"text","value":"{name: 'download', url}"}]},{"type":"text","value":" to the receiver. As the message is sent to every listener in the extension, it's good to include a message property in the message you're sending to be able to handle different messages. We're also sending the URL to download the image from."}]},{"type":"element","tagName":"p","properties":{},"children":[{"type":"text","value":"Let's go back to our service worker now. First, let's send a message to the content script from "},{"type":"element","tagName":"code","properties":{"className":["language-text"]},"children":[{"type":"text","value":"chooseDesktopMedia"}]},{"type":"text","value":" callback function we earlier did:"}]},{"type":"element","tagName":"div","properties":{"className":["kg-card","kg-code-card","gatsby-highlight"],"dataLanguage":"js"},"children":[{"type":"element","tagName":"pre","properties":{"className":["language-js"]},"children":[{"type":"element","tagName":"code","properties":{"className":["language-js"]},"children":[{"type":"element","tagName":"span","properties":{"className":["token","comment"]},"children":[{"type":"text","value":"//check whether the user canceled the request or not"}]},{"type":"text","value":"\n"},{"type":"element","tagName":"span","properties":{"className":["token","keyword"]},"children":[{"type":"text","value":"if"}]},{"type":"text","value":" "},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"("}]},{"type":"text","value":"streamId "},{"type":"element","tagName":"span","properties":{"className":["token","operator"]},"children":[{"type":"text","value":"&&"}]},{"type":"text","value":" streamId"},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"."}]},{"type":"text","value":"length"},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":")"}]},{"type":"text","value":" "},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"{"}]},{"type":"text","value":"\n    "},{"type":"element","tagName":"span","properties":{"className":["token","function"]},"children":[{"type":"text","value":"setTimeout"}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"("}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"("}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":")"}]},{"type":"text","value":" "},{"type":"element","tagName":"span","properties":{"className":["token","operator"]},"children":[{"type":"text","value":"=>"}]},{"type":"text","value":" "},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"{"}]},{"type":"text","value":"\n        chrome"},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"."}]},{"type":"text","value":"tabs"},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"."}]},{"type":"element","tagName":"span","properties":{"className":["token","function"]},"children":[{"type":"text","value":"sendMessage"}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"("}]},{"type":"text","value":"tab"},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"."}]},{"type":"text","value":"id"},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":","}]},{"type":"text","value":" "},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"{"}]},{"type":"element","tagName":"span","properties":{"className":["token","literal-property","property"]},"children":[{"type":"text","value":"name"}]},{"type":"element","tagName":"span","properties":{"className":["token","operator"]},"children":[{"type":"text","value":":"}]},{"type":"text","value":" "},{"type":"element","tagName":"span","properties":{"className":["token","string"]},"children":[{"type":"text","value":"\"stream\""}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":","}]},{"type":"text","value":" streamId"},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"}"}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":","}]},{"type":"text","value":" "},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"("}]},{"type":"element","tagName":"span","properties":{"className":["token","parameter"]},"children":[{"type":"text","value":"response"}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":")"}]},{"type":"text","value":" "},{"type":"element","tagName":"span","properties":{"className":["token","operator"]},"children":[{"type":"text","value":"=>"}]},{"type":"text","value":" console"},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"."}]},{"type":"element","tagName":"span","properties":{"className":["token","function"]},"children":[{"type":"text","value":"log"}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"("}]},{"type":"text","value":"response"},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":")"}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":")"}]},{"type":"text","value":"\n    "},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"}"}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":","}]},{"type":"text","value":" "},{"type":"element","tagName":"span","properties":{"className":["token","number"]},"children":[{"type":"text","value":"200"}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":")"}]},{"type":"text","value":"\n"},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"}"}]}]}]}]},{"type":"element","tagName":"p","properties":{},"children":[{"type":"text","value":"Notice that to send a message to the content script we're using "},{"type":"element","tagName":"code","properties":{"className":["language-text"]},"children":[{"type":"text","value":"chrome.tabs.sendMessage"}]},{"type":"text","value":". The difference between this one and "},{"type":"element","tagName":"code","properties":{"className":["language-text"]},"children":[{"type":"text","value":"chrome.runtime.sendMessage"}]},{"type":"text","value":" is that the former one sends the message to content scripts in a specific tab, whereas the first one sends the message to all scripts in the extension that listen to the "},{"type":"element","tagName":"code","properties":{"className":["language-text"]},"children":[{"type":"text","value":"onMessage"}]},{"type":"text","value":" handler."}]},{"type":"element","tagName":"p","properties":{},"children":[{"type":"text","value":"Next, we'll add a listener to the "},{"type":"element","tagName":"code","properties":{"className":["language-text"]},"children":[{"type":"text","value":"onMessage"}]},{"type":"text","value":" event to receive the "},{"type":"element","tagName":"code","properties":{"className":["language-text"]},"children":[{"type":"text","value":"download"}]},{"type":"text","value":" message and download the file to the user's machine:"}]},{"type":"element","tagName":"div","properties":{"className":["kg-card","kg-code-card","gatsby-highlight"],"dataLanguage":"js"},"children":[{"type":"element","tagName":"pre","properties":{"className":["language-js"]},"children":[{"type":"element","tagName":"code","properties":{"className":["language-js"]},"children":[{"type":"text","value":"chrome"},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"."}]},{"type":"text","value":"runtime"},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"."}]},{"type":"text","value":"onMessage"},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"."}]},{"type":"element","tagName":"span","properties":{"className":["token","function"]},"children":[{"type":"text","value":"addListener"}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"("}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"("}]},{"type":"element","tagName":"span","properties":{"className":["token","parameter"]},"children":[{"type":"text","value":"message"},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":","}]},{"type":"text","value":" sender"},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":","}]},{"type":"text","value":" senderResponse"}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":")"}]},{"type":"text","value":" "},{"type":"element","tagName":"span","properties":{"className":["token","operator"]},"children":[{"type":"text","value":"=>"}]},{"type":"text","value":" "},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"{"}]},{"type":"text","value":"\n    "},{"type":"element","tagName":"span","properties":{"className":["token","keyword"]},"children":[{"type":"text","value":"if"}]},{"type":"text","value":" "},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"("}]},{"type":"text","value":"message"},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"."}]},{"type":"text","value":"name "},{"type":"element","tagName":"span","properties":{"className":["token","operator"]},"children":[{"type":"text","value":"==="}]},{"type":"text","value":" "},{"type":"element","tagName":"span","properties":{"className":["token","string"]},"children":[{"type":"text","value":"'download'"}]},{"type":"text","value":" "},{"type":"element","tagName":"span","properties":{"className":["token","operator"]},"children":[{"type":"text","value":"&&"}]},{"type":"text","value":" message"},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"."}]},{"type":"text","value":"url"},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":")"}]},{"type":"text","value":" "},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"{"}]},{"type":"text","value":"\n        chrome"},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"."}]},{"type":"text","value":"downloads"},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"."}]},{"type":"element","tagName":"span","properties":{"className":["token","function"]},"children":[{"type":"text","value":"download"}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"("}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"{"}]},{"type":"text","value":"\n            "},{"type":"element","tagName":"span","properties":{"className":["token","literal-property","property"]},"children":[{"type":"text","value":"filename"}]},{"type":"element","tagName":"span","properties":{"className":["token","operator"]},"children":[{"type":"text","value":":"}]},{"type":"text","value":" "},{"type":"element","tagName":"span","properties":{"className":["token","string"]},"children":[{"type":"text","value":"'screenshot.png'"}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":","}]},{"type":"text","value":"\n            "},{"type":"element","tagName":"span","properties":{"className":["token","literal-property","property"]},"children":[{"type":"text","value":"url"}]},{"type":"element","tagName":"span","properties":{"className":["token","operator"]},"children":[{"type":"text","value":":"}]},{"type":"text","value":" message"},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"."}]},{"type":"text","value":"url\n        "},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"}"}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":","}]},{"type":"text","value":" "},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"("}]},{"type":"element","tagName":"span","properties":{"className":["token","parameter"]},"children":[{"type":"text","value":"downloadId"}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":")"}]},{"type":"text","value":" "},{"type":"element","tagName":"span","properties":{"className":["token","operator"]},"children":[{"type":"text","value":"=>"}]},{"type":"text","value":" "},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"{"}]},{"type":"text","value":"\n            "},{"type":"element","tagName":"span","properties":{"className":["token","function"]},"children":[{"type":"text","value":"senderResponse"}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"("}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"{"}]},{"type":"element","tagName":"span","properties":{"className":["token","literal-property","property"]},"children":[{"type":"text","value":"success"}]},{"type":"element","tagName":"span","properties":{"className":["token","operator"]},"children":[{"type":"text","value":":"}]},{"type":"text","value":" "},{"type":"element","tagName":"span","properties":{"className":["token","boolean"]},"children":[{"type":"text","value":"true"}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"}"}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":")"}]},{"type":"text","value":"\n        "},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"}"}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":")"}]},{"type":"text","value":"\n\n        "},{"type":"element","tagName":"span","properties":{"className":["token","keyword"]},"children":[{"type":"text","value":"return"}]},{"type":"text","value":" "},{"type":"element","tagName":"span","properties":{"className":["token","boolean"]},"children":[{"type":"text","value":"true"}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":";"}]},{"type":"text","value":"\n    "},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"}"}]},{"type":"text","value":"\n"},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"}"}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":")"}]}]}]}]},{"type":"element","tagName":"p","properties":{},"children":[{"type":"text","value":"First, we're checking if the "},{"type":"element","tagName":"code","properties":{"className":["language-text"]},"children":[{"type":"text","value":"name"}]},{"type":"text","value":" property of the message is equal to "},{"type":"element","tagName":"code","properties":{"className":["language-text"]},"children":[{"type":"text","value":"download"}]},{"type":"text","value":" to make sure that the message received is the correct one. Then, we're downloading the file using "},{"type":"element","tagName":"code","properties":{"className":["language-text"]},"children":[{"type":"text","value":"chrome.downloads.download"}]},{"type":"text","value":", passing it the options object that has two options here: "},{"type":"element","tagName":"code","properties":{"className":["language-text"]},"children":[{"type":"text","value":"filename"}]},{"type":"text","value":" which is the name of the file to download, and "},{"type":"element","tagName":"code","properties":{"className":["language-text"]},"children":[{"type":"text","value":"url"}]},{"type":"text","value":" which is the URL to download. In the callback of the "},{"type":"element","tagName":"code","properties":{"className":["language-text"]},"children":[{"type":"text","value":"downloads"}]},{"type":"text","value":" method we're calling the callback function passed by the sender."}]},{"type":"element","tagName":"p","properties":{},"children":[{"type":"text","value":"Our extension is now ready. Go to "},{"type":"element","tagName":"a","properties":{"href":"chrome://extensions"},"children":[{"type":"text","value":"chrome://extensions"}]},{"type":"text","value":" again and reload the extension. Then, go to any page, click the icon of the extension. You'll be prompted to choose either the entire screen, a window, or a tab. Once you choose, a screenshot will be taken and saved on your machine."}]},{"type":"element","tagName":"hr","properties":{},"children":[]},{"type":"element","tagName":"h2","properties":{"id":"conclusion"},"children":[{"type":"text","value":"Conclusion"}]},{"type":"element","tagName":"p","properties":{},"children":[{"type":"text","value":"In this tutorial, we learned how to a screenshot and a few of the concepts of a chrome extension shortly. If you want to learn more about Chrome extensions, make sure to check out the rest of my tutorials about "},{"type":"element","tagName":"a","properties":{"href":"https://blog.shahednasser.com/tag/browser-extensions"},"children":[{"type":"text","value":"browser extensions"}]},{"type":"text","value":"."}]}],"data":{"quirksMode":false}},"tableOfContents":[{"id":"creating-the-extension","heading":"Creating The Extension"},{"id":"add-service-worker-or-background-script-","heading":"Add Service Worker (or Background Script)"},{"id":"add-content-script","heading":"Add Content Script"},{"id":"download-screenshot","heading":"Download Screenshot"},{"id":"conclusion","heading":"Conclusion"}]},"featureImageSharp":{"base":"photo-1509966756634-9c23dd6e6815.jpg","publicURL":"/static/e7b786c1095d434670e646e2afd6405a/photo-1509966756634-9c23dd6e6815.jpg","imageMeta":{"width":2000,"height":2441},"childImageSharp":{"fluid":{"base64":"data:image/jpeg;base64,/9j/2wBDABALDA4MChAODQ4SERATGCgaGBYWGDEjJR0oOjM9PDkzODdASFxOQERXRTc4UG1RV19iZ2hnPk1xeXBkeFxlZ2P/2wBDARESEhgVGC8aGi9jQjhCY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2P/wgARCAAYABQDASIAAhEBAxEB/8QAGAABAAMBAAAAAAAAAAAAAAAAAAEFBgP/xAAWAQEBAQAAAAAAAAAAAAAAAAAAAQL/2gAMAwEAAhADEAAAAbVnYs0KiMuJNAf/xAAcEAACAQUBAAAAAAAAAAAAAAAAAgEDERITFCH/2gAIAQEAAQUC2Kp00jqpjO1laT0nAspip//EABcRAQADAAAAAAAAAAAAAAAAAAEAEBH/2gAIAQMBAT8BZrf/xAAXEQADAQAAAAAAAAAAAAAAAAAAARAR/9oACAECAQE/AUZf/8QAHBAAAgICAwAAAAAAAAAAAAAAAAEhMTKhEUGR/9oACAEBAAY/Am24L0d+Flls4cGejM//xAAdEAEAAgICAwAAAAAAAAAAAAABABExkSFhgeHw/9oACAEBAAE/IRFdadnaIuV5RzuUhy3F9qWC2Gane2nwGf/aAAwDAQACAAMAAAAQEDe9/8QAFhEBAQEAAAAAAAAAAAAAAAAAARAx/9oACAEDAQE/EEmQMn//xAAYEQEBAAMAAAAAAAAAAAAAAAABABARMf/aAAgBAgEBPxAD20nH/8QAHRABAAICAgMAAAAAAAAAAAAAAQARITFx0RBBUf/aAAgBAQABPxAMBAWly61Pg/HXKocbqgfYKKBWmJu9SiLdsV2AyBbPoxKijxBP/9k=","aspectRatio":0.8177570093457944,"src":"/static/e7b786c1095d434670e646e2afd6405a/ea4ab/photo-1509966756634-9c23dd6e6815.jpg","srcSet":"/static/e7b786c1095d434670e646e2afd6405a/477ba/photo-1509966756634-9c23dd6e6815.jpg 175w,\n/static/e7b786c1095d434670e646e2afd6405a/06776/photo-1509966756634-9c23dd6e6815.jpg 350w,\n/static/e7b786c1095d434670e646e2afd6405a/ea4ab/photo-1509966756634-9c23dd6e6815.jpg 700w,\n/static/e7b786c1095d434670e646e2afd6405a/3055e/photo-1509966756634-9c23dd6e6815.jpg 1050w,\n/static/e7b786c1095d434670e646e2afd6405a/eff08/photo-1509966756634-9c23dd6e6815.jpg 1400w,\n/static/e7b786c1095d434670e646e2afd6405a/4e5f3/photo-1509966756634-9c23dd6e6815.jpg 2000w","srcWebp":"/static/e7b786c1095d434670e646e2afd6405a/89afa/photo-1509966756634-9c23dd6e6815.webp","srcSetWebp":"/static/e7b786c1095d434670e646e2afd6405a/9fca7/photo-1509966756634-9c23dd6e6815.webp 175w,\n/static/e7b786c1095d434670e646e2afd6405a/37a4e/photo-1509966756634-9c23dd6e6815.webp 350w,\n/static/e7b786c1095d434670e646e2afd6405a/89afa/photo-1509966756634-9c23dd6e6815.webp 700w,\n/static/e7b786c1095d434670e646e2afd6405a/78e7a/photo-1509966756634-9c23dd6e6815.webp 1050w,\n/static/e7b786c1095d434670e646e2afd6405a/03d34/photo-1509966756634-9c23dd6e6815.webp 1400w,\n/static/e7b786c1095d434670e646e2afd6405a/49d6b/photo-1509966756634-9c23dd6e6815.webp 2000w","sizes":"(max-width: 700px) 100vw, 700px"}}}}}]}},"pageContext":{"slug":"chrome-extension-tutorial-replace-images-in-any-website-with-pikachu","prev":"react-hooks-tutorial-create-a-number-trivia-generator-website","next":"website-ideas-to-practice-your-web-development-skills","tag":"browser-extensions","limit":3,"skip":0,"primaryTagCount":9,"collectionPaths":{}}},"staticQueryHashes":["1272700106","1676991999","2138873178","2546165603","2681841279","2938721187","293880488","3052966952","4156497161"]}