I love Chrome extensions. They make everything easier like listening to music, learning new languages, setting study timers, and so much more. I have one for almost every need! But instead of just using extensions, why not try building one yourself?
In this blog, I’ll show you how to create a very simple Chrome extension using Manifest Version 3. The goal is to help you get started with understanding the basics. This guide will focus only on the most important part of any extension: the manifest file.
How Are Chrome Extensions Different From Websites?
Browser extensions and websites share similarities but also have key differences.
Both use the core technologies of HTML, CSS, and JavaScript, making it easier for web developers to build extensions. Extensions, like websites, can manipulate webpages. They can access browser APIs, which allow them to modify the DOM, control tabs, and run background scripts, something websites cannot do.
While websites exist as independent pages, extensions operate as overlays within the browser. They also follow a specific file structure, defined by the manifest.json
file, which is essential for their functionality.
Reasons to build chrome extensions:
Build something valuable that others can use by sharing your extension on the Chrome Web Store.
Having your own extension can show future employers your practical skills and initiative in tackling problems.
Extensions are built using familiar web technologies (HTML, CSS, and JavaScript), making it easier to get started.
What We Are Building Today
Today, we’re creating a simple Daily Motivation Quotes Extension. This extension will give you an inspiring quote every time you open it.
Here are the features we’ll build step by step:
See a new motivational quote each time user clicks on the button
Copy a quote to the clipboard with one click.
Heart your favorite quotes
What Is the Manifest File?
The manifest.json
file uses a JSON format and has different fields to define what your extension does. Some fields are required, others are recommended, and many are entirely optional depending on the features you want to include.
One important thing to note is that we’ll use Manifest Version 3, which is the latest version supported by Chrome.
“Manifest V3 removes the ability for an extension to use remotely hosted code, which presents security risks by allowing unreviewed code to be executed in extensions. With this change, an extension can only execute JavaScript that is included within its package and subject to review by the Chrome Web Store."
If you’ve worked with JavaScript before, you can think of it as being similar to the package.json
file.
This is what our manifest file will look like. Read more about the other fields that you can use here.
{
"manifest_version": 3,
"name": "Daily Motivation",
"version": "1.0",
"description": "Displays a random motivational quote every time you open the popup.",
"action": {
"default_popup": "popup.html"
},
"icons": {
"16": "logo.png",
"48": "logo.png",
"128": "logo.png"
},
"permissions" : ["storage"]
}
You can create different logo images but if you have only one, Chrome will automatically scale it for each dimension.
Tutorial
The manifest file does not contain the functioning of our extension. We can use modern web frameworks and development tool to build our extension just the way we build a website.
This is the basic flow of the extension. It is a good practice to outline your extension in your notes like so:
Popup.html
When a user clicks on the extension, the manifest.json file executes its default actions. As you can notice, the default popup is to launch the popup.html file.
For now this is the basic html code of the extension.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Daily Motivation</title>
</head>
<body>
<div id="quote-box">
<h2 id="today">Today's Date </h2>
<h2 id="time">Current Time</h2>
<p id="quote">Quote</p>
<script src="script.js"></script>
</body>
</html>
Loading an extension
Once you have the directory setup with manifest file , icons and popup.html file. We will test the extension in our browser. To do that, go over to your chrome browser and switch on developer mode. Once you have done that go to the extensions settings and click on the load unpacked button. You can upload the entire extension directory here. Make sure the manifest file is in the root of this directory.
Once completed you will find the extension along with other extensions.
On clicking the extension, I can see the layout of our popup.html file. You have to refresh the extension every time you change the manifest file.
Displaying time
Now we keep on building the extension feature by feature. We can add today’s date and current time with simple Date object in the script.js file. This automatically shows the current time and today’s date on the web page as soon as it finishes loading.
document.addEventListener("DOMContentLoaded", function () {
const timeElement = document.getElementById("time");
const todayElement = document.getElementById("today");
const currentTime = new Date().toLocaleTimeString();
const todayDate = new Date().toLocaleDateString();
timeElement.textContent = `${currentTime}`;
todayElement.textContent = `${todayDate}`;
});
If you found this blog helpful, please like it
Displaying a quote
This code selects a random motivational quote from a predefined list and displays it in the quote
element on the page. I kept it simple by using an array to focus on explaining the manifest file, but you could easily extend it to fetch quotes from a JSON file or even a database for a larger collection.
const quotes = [
"Believe you can and you're halfway there.",
"The future depends on what you do today.",
"Act as if what you do makes a difference. It does.",
"Keep going, you're getting there!",
"You are stronger than you think.",
];
const quoteElement = document.getElementById('quote');
const randomQuote = quotes[Math.floor(Math.random() * quotes.length)];
quoteElement.textContent = randomQuote;
Copy to clipboard
This code adds functionality to the "Copy" button, allowing users to copy the current quote to their clipboard and see a confirmation alert. Make sure to update your popup.html file as well.
const copyButton = document.getElementById('copy-quote');
const favoriteButton = document.getElementById('favorite-quote');
copyButton.addEventListener('click', () => {
navigator.clipboard.writeText(randomQuote).then(() => {
alert("Quote copied!");
}).catch(err => {
console.error('Failed to copy:', err);
});
});
To debug, visit the Chrome errors page. It displays issues like so:
Favorite feature and chrome.local.storage
The code snippet demonstrates how to use the Chrome Storage API to manage a user’s favorite quotes in a browser extension.
favoriteButton.addEventListener('click', () => {
chrome.storage.local.get(['favorites'], (result) => {
const favorites = result.favorites || [];
if (!favorites.includes(randomQuote)) {
favorites.push(randomQuote);
chrome.storage.local.set({ favorites }, () => {
favoriteButton.classList.add('saved');
alert("Quote added to favorites!");
});
} else {
alert("Quote already in favorites!");}
});
});
chrome.storage.local.get(['favorites'], (result) => {
const favorites = result.favorites || [];
if (favorites.includes(randomQuote)) {
favoriteButton.classList.add('saved');
}
});
To use the storage API, declare the "storage"
permission in the extension’s manifest file.
Data saved using this API is:
Retained even if users clear their browser history or cache.
Shared across all extension scripts, including background workers and popups.
When a user clicks the "favorite" button, the script checks for any existing favorites in Chrome's local storage using chrome.storage.local.get
. If the current quote isn’t already saved, it’s added to the favorites list, which is then updated in storage with chrome.storage.local.set
. The button is visually updated with a "saved" class, and the user receives an alert confirming that the quote was saved.
On loading, the script retrieves any previously stored favorites. If the current quote is already in the favorites list, the button is styled with the "saved" class to indicate its favored state.
The chrome.storage.local
method used in the snippet stores data on the user’s device, with a default 10 MB limit (or higher with additional permissions). To explore advanced features, such as managing storage quotas or integrating with policies using storage.managed
, visit the Chrome Storage API documentation.
Style
Now that all the functions are added, we can style it using simple css.
The final directory setup
This is the set up of the extension directory at the end
daily-motivation-quotes/
├── manifest.json # Core configuration file for the Chrome extension.
├── popup.html # The HTML for the popup interface users see.
├── style.css # The CSS file to style the popup.
├── script.js # JavaScript to add functionality to your extension.
└── icons/ # Folder containing extension icons.
├── icon16.png # Icon for small sizes (16x16).
├── icon48.png # Icon for medium sizes (48x48).
└── icon128.png # Icon for large sizes (128x128).
What’s Level 02
This extension serves as a Level 01 project, using only one Chrome API while showcasing the fundamental structure of the manifest file and explaining how the other files interact with it. In the next blog, I'll dive deeper into more advanced features of the manifest file, explain background scripts, and introduce additional Chrome APIs.
If you found this blog helpful, please like and share it with others! Also, feel free to leave a comment sharing what you loved about this tutorial and any topics you'd like to see covered next.