This story will walk you through the basic steps needed to get started building a new tab Chrome Extension powered by an API without any dependencies. Check out our extension for reference. Tech Stories Tab Developing a new extension can be a little overwhelming. Google provides a ton of boilerplate examples, but digging through that mess can be a little like navigating through a labyrinth. A lot of examples are incomplete or lead you down the wrong path. Here's what you need to get started building a new tab extension that fetches content from an API. Step 1: Create a new folder for your extension However you choose to make a new folder ( ) and where you put it ( ) is up to you. mkdir ~/ Don't get too caught up in the name, it really doesn't matter. I called my folder , but it won't be visible once you start marketing your extension. You can always rename this later. tech-stories-extension Step 2: Create a manifest.json file in your new folder This is a necessary file for every Chrome Extension. Don't worry about every field you could add to your manifest at this stage. That's a future problem. For now, just start with something like this. { : , : , : , : , : , : { : }, : [ , ], : { : , : } } "manifest_version" 2 "name" "Tech Stories Tab by Hacker Noon" "version" "1.0" "description" "The latest Hacker Noon tech stories." "author" "Hacker Noon" "chrome_url_overrides" "newtab" "index.html" "permissions" "storage" "https://api.hackernoon.com/" "icons" "16" "icon16.png" "128" "icon128.png" This is basically just a number to tell Google how to interpret your manifest.json file. Changing this will throw an error when you try and install your extension. manifest_version These fields will be used in the web store to describe your extension. You'll eventually want to be thoughtful in what you add here but for testing purposes, feel free to go with something random. name/description/author While testing, go with or . Once you get published, you'll have to worry about incrementing by for a minor upgrade and for a major upgrade. version "0.1" "1.0" .1 1 This is where you determine which html file will be used whenever a user opens a new tab. I've named mine but feel free to stick it to the man and break convention. chrome_url_overrides index.html Security is a pretty big deal when it comes to Chrome Extensions. By default, everything is pretty locked down. For example, you can't just go storing data to willy nilly like some random webpage. You've got to ask users permission to store things locally. You'll also want to add urls to any APIs you're fetching data from. permissions localStorage At minimum, Google recommends having a 128x128 pixel icon and a 16x16 pixel icon. icons Step 3: Create an index.html file Fancy Title < > html < > head < = > meta charset "utf-8" < > title </ > title < = = > meta name "description" content "Fancy description..." < = = > link rel "stylesheet" href "style.css" </ > head < > body < = > div id "stories" </ > div < = > script src "script.js" </ > script </ > body </ > html You'll probably want to change the fancy title and description to something a little more imaginative, but this will get you started. Take note of the element, we'll reference this in our javascript. <div id="stories"></div> Step 4: Create a script.js file storiesDiv = .querySelector( ); storyNode = { template = .createElement( ); template.innerHTML = story; template.content.childNodes[ ]; } addStories = { ( index stories) { story = stories[index]; html = ; storiesDiv.appendChild(storyNode(html)); } } (localStorage.lastFetch && localStorage.stories && ( () - localStorage.lastFetch) < ( * * )) { addStories( .parse(localStorage.stories)); } { (localStorage.stories) { addStories( .parse(localStorage.stories)); } fetch( ,{ : , : , : }) .then( response.json()) .then( { (!localStorage.stories) { addStories(data); } localStorage.setItem( , .stringify(data)); localStorage.setItem( , () ); }); } const document '#stories' const ( ) => story var document 'template' return 0 const ( ) => stories for let in const const `<div class="story"> <a href=" "> </a> </div>` ${story.url} ${story.title} if new Date 1000 60 60 JSON else if JSON 'https://api.hackernoon.com/featured-stories' method 'GET' mode 'cors' credentials 'include' => response => data if "stories" JSON "lastFetch" new Date -1 This is where the bulk of the work is happening in the chrome extension. You might be tempted to load Vue.js, React, or your favorite flavor of javascript, but if you just want to inject a few chunks for HTML into a static page, go with vanilla JS. Anything else is just not worth the trouble, in my opinion. The above javascript is basically doing the following: 1) Check to see if you have cached stories ( ) and you most recently fetched stories less than an hour ago. 2) If your check passes, grab stories from the cache. localStorage.stories 3) If the check fails, fetch new stories from the API. 4) Add the stories to the element from . <div id="stories"></div> index.html It's ok if you don't quite follow some of the logic here. The code is far from pristine. We can dive into the logic line-by-line in the comments below. I just don't want to get too deep into the weeds here so it's easier to understand what is going on at a high level. Step 5: Create a style.css file , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , { : ; : ; :inherit; :baseline; : ; : } , , , , , , , , , , { :block} { : } , { :none} , { :none} , , , { :none} { :collapse; : } {} {} html body div span applet object iframe h1 h2 h3 h4 h5 h6 p blockquote pre a abbr acronym address big cite code del dfn em img ins kbd q s samp small strike strong sub sup tt var b u i center dl dt dd ol ul li fieldset form label legend table caption tbody tfoot thead tr th td article aside canvas details embed figure figcaption footer header hgroup menu nav output ruby section summary time mark audio video border 0 font-size 100% font vertical-align margin 0 padding 0 article aside details figcaption figure footer header hgroup menu nav section display body line-height 1 ol ul list-style blockquote q quotes blockquote :before blockquote :after q :before q :after content table border-collapse border-spacing 0 #stories .story This sad CSS is going to look like garbage. It's really just a CSS reset and some boilerplate to get started. Check out for tips on spicing things up a bit. CSS Tricks Step 6: Install your extension 1) Go to chrome://extensions/ 2) Turn on "Developer mode" in the top right corner 3) Click "load unpacked" and select the folder containing your extension files. 4) Open and new tab and see your code in action! Step 7: Iterate Once you can see your code functioning in a new tab, start tweaking the HTML, JS, and CSS until you get things looking good enough to launch. Step 8: Submit your extension for inclusion into the web store You'll need to create a few promotional images, add a longer description and add security information in order to submit. Here is a walkthrough of the . publishing process Conclusion Without dependencies, you can build a new tab Chrome extension in less than 100 lines of code. Here is a in case you aren't the copy and pasting type. github boilerplate repo This tutorial is a WIP, if you get stuck on any step, please leave a comment. If you have any ideas for improvements to our extension, please leave a comment on our . Product Hunt page