Introduction
Creating a Chrome extension can seem daunting at first. But with the right guidance, it's manageable. This guide will walk you through building and publishing a Chrome extension. Called "Custom Session Saver." This extension allows users to save and restore custom sessions. For example, "Work," "Personal," "Development," etc.
By the end of this guide, you will have a functional Chrome extension, ready for publishing.
Project Setup
Start by creating a directory for your project, named "custom_session_storage." Inside this directory, create the following folder structure:
project\_root/
│
├── background.js # Background script specified in manifest
├── images/ # Directory for icons
│ ├── icon16.png # 16x16 icon
│ ├── icon48.png # 48x48 icon
│ └── icon128.png # 128x128 icon
├── manifest.json # Chrome extension manifest file
├── popup.js # popup script file
├── popup.css # popup style file
└── popup.html # Default popup HTML file
Manifest File
Open manifest.json
and paste the following code:
{
"manifest_version": 3,
"name": "Custom Session Saver",
"version": "1.0",
"description": "Save and restore custom sessions, for example: 'Work', 'Personal', 'Development', etc.",
"permissions": [
"storage",
"tabs"
],
"background": {
"service_worker": "background.js"
},
"action": {
"default_popup": "popup.html"
},
"icons": {
"16": "images/icon16.png",
"48": "images/icon48.png",
"128": "images/icon128.png"
}
}
In this file, we are doing the following:
Defining the version of the manifest we are using.
Giving it our custom name, version, and description.
Adding permissions for "storage" to save local data and "tabs" to manipulate tabs.
Setting the background script. This handles all functionality for tabs and session storage.
Defining the UI for the popup.
Setting the default icon for the following sizes: 16x16, 48x48, and 128x128.
Background Script
Create background.js
to handle the extension's background processes. Open it up and paste the following code:
console.log("Background script loaded");
chrome.runtime.onInstalled.addListener(() => {
console.log("Extension installed");
});
chrome.runtime.onMessage.addListener((message, sender, sendResponse) => {
if (message.action === "saveSession") {
chrome.storage.local.set({ [message.sessionName]: message.session }, () => {
console.log(`Session ${message.sessionName} saved.`);
chrome.runtime.sendMessage({ action: "refreshSessions" });
if (sendResponse) sendResponse();
});
} else if (message.action === "openSession") {
chrome.storage.local.get([message.sessionName], (result) => {
let session = result[message.sessionName];
if (session) {
session.forEach((tab) => {
chrome.tabs.create({ url: tab.url });
});
}
});
} else if (message.action === "deleteSession") {
chrome.storage.local.remove(message.sessionName, () => {
console.log(`Session ${message.sessionName} deleted.`);
chrome.runtime.sendMessage({ action: "refreshSessions" });
if (sendResponse) sendResponse();
});
}
});
Here we added a console log message and some event listeners:
- onInstalled: After installation, this event gets triggered. This logs that the installation was successful.
- onMessage: This handles events from popup.js
. If the message is to “saveSession,” it saves the session in local storage. If the message is “openSession,” it looks in local storage for that session and opens new tabs based on saved data. Finally, if the message is “deleteSession,” it removes the session name from storage.
- sendingMessage: To separate concerns, popup.js
handles the refresh session logic. When saving or deleting, we send an event that popup.js
listens for and triggers the refresh.
User Interface
Set up the user interface in popup.html
. Open up the file and paste the following code:
<!DOCTYPE html>
<html>
<head>
<link rel="stylesheet" type="text/css" href="popup.css">
<title>Session Saver</title>
</head>
<body>
<div class="title-container">
<img src="./images/icon48.png" alt="icon" width="25" height="25">
<h3 class="title">Custom Session Saver</h3>
</div>
</br>
<div class="input-container">
<input class="input" id="sessionName" type="text" placeholder="Enter session name" />
<button class="save-session-btn" id="saveSession">SAVE</button>
</div>
<div id="sessions">
<ul class="sessions-list" id="sessionsUL"></ul>
</div>
<script src="popup.js"></script>
</body>
</html>
In this file, we added our extension’s style path and title. Inside the body tag, we included:
A header with a small icon and title.
A div container with an input, and a button for saving the session.
Another div that will hold our saved sessions.
A script tag to import our JavaScript file**
popup.js
**.
Script
In popup.js
, we will handle the UI interactions. Open up the file and paste the following code:
document.addEventListener("DOMContentLoaded", () => {
const saveSessionButton = document.getElementById("saveSession");
const sessionNameInput = document.getElementById("sessionName");
const sessionsUL = document.getElementById("sessionsUL");
const loadSessions = () => {
while (sessionsUL.firstChild) {
sessionsUL.removeChild(sessionsUL.firstChild);
}
chrome.storage.local.get(null, (sessions) => {
for (let sessionName in sessions) {
let sessionLI = document.createElement("li");
sessionLI.className = "session";
let sessionTitle = document.createElement("span");
sessionTitle.textContent = sessionName;
let openButton = document.createElement("button");
openButton.textContent = "OPEN";
openButton.addEventListener("click", () => {
chrome.runtime.sendMessage({ action: "openSession", sessionName });
});
let deleteButton = document.createElement("button");
deleteButton.textContent = "DELETE";
deleteButton.addEventListener("click", () => {
chrome.runtime.sendMessage({ action: "deleteSession", sessionName });
});
sessionLI.appendChild(sessionTitle);
sessionLI.appendChild(openButton);
sessionLI.appendChild(deleteButton);
sessionLI.appendChild(document.createElement("br"));
sessionsUL.appendChild(sessionLI);
}
});
};
saveSessionButton.addEventListener("click", () => {
let sessionName = sessionNameInput.value;
if (sessionName) {
chrome.tabs.query({ currentWindow: true }, (tabs) => {
let session = tabs.map((tab) => ({ url: tab.url, title: tab.title }));
chrome.runtime.sendMessage(
{ action: "saveSession", sessionName, session },
loadSessions
);
});
}
});
chrome.runtime.onMessage.addListener((message) => {
if (message.action === "refreshSessions") {
loadSessions();
}
});
loadSessions();
});
Inside this script:
We create a listener on the document, to get triggered when all content has loaded.
We get a couple of elements from the UI: the save session button, the input element, and the sessions list element.
We define a
loadSession
method. Inside, we use a while loop to clear the list element, reloading on each change. We retrieve the session property from local storage using Chrome's built-in methods.We loop through all our sessions using a
for-in
loop, creating ali
element and giving it a class name of the session. Create a span to hold our title. A button to open the session when clicked, and a delete button to remove the session when clicked.We append the title and buttons to our session
li
element and this element to our sessionul
frompopup.html
.We add an event listener to the save session button. On click, it grabs the input value and saves it as the session name. Using Chrome methods, we loop through the current window's open tabs. Then create a map, and send a message to
background.js
to save the sessions in local storage. We triggerloadSession
to reload the list and reflect the new item.We add a listener to handle
refreshSessions
messages frombackground.js
.Finally, we trigger this method on the first load to display existing sessions.
Icons
Prepare three icon sizes: 16x16, 48x48, and 128x128 pixels. These are for Chrome's toolbar and the Chrome Web Store.
Search for free tools like resizeimage.net, AI icon generator, and FreeConvert. To create and resize your icons.
Styles
Style the popup with popup.css
to look user-friendly and smooth. Open up the file and paste the following code:
body {
width: 300px;
font-family: Arial, sans-serif;
}
.title-container {
border-bottom: 1px solid rgba(0, 0, 0, .06);
display: flex;
justify-content: flex-start;
text-align: center;
align-items: center;
}
.title {
margin-left: 4px;
}
.input-container {
margin-top: 4px;
padding-bottom: 10px;
}
.input {
border: 1px solid lightgray;
box-shadow: 2px 2px 5px rgba(0, 0, 0, 0.1);
margin-right: 5px;
border-radius: 4px;
font-size: larger;
padding-top: 4px;
padding-bottom: 4px;
padding-left: 4px;
}
input:focus-visible,
button:focus-visible {
outline: 1px solid #007bff;
outline-offset: 0px;
}
button {
box-shadow: 2px 2px 5px rgba(0, 0, 0, 0.1);
border: none;
padding: 5px 10px;
cursor: pointer;
border-radius: 4px;
}
.save-session-btn {
background-color: #007bff;
color: #ffffff;
}
.save-session-btn:hover,
.save-session-btn:active,
.save-session-btn:focus,
.save-session-btn:focus-visible {
background-color: #0056b3;
}
.sessions-list {
padding-inline-start: 20px;
}
.session {
display: flex;
align-items: center;
margin-bottom: 15px;
padding-left: 5px;
}
.session::before {
content: "\2022";
display: inline-block;
width: 20px;
margin-left: -25px;
text-align: center;
}
.session span {
flex-grow: 1;
font-weight: bold;
font-size: larger;
}
.session button {
margin-left: 10px;
background-color: #ffffff;
color: #007bff;
border: 1px solid #007bff;
}
.session button:hover,
.session button:active,
.session button:focus,
.session button:focus-visible {
background-color: #dbe5f2;
}
For this style, we aimed for a modern look:
Custom width and font for the whole body.
Text-centered title container with a slight border at the bottom.
A small margin to separate the title from the icon.
Custom styles for the input and buttons, including rounded corners, a bit of shadow, and blue outlines. There are two buttons: one blue-colored and the other with blue outlines.
Flex positioning, making list items take the same space as a column with flex-grow. Since "flex" breaks our list items, we added the list item symbol before each.
Testing
To test your extension:
1. Navigate to Chrome's extension page (chrome://extensions/).
2. Enable developer mode.
3. Click "Load unpacked" and select your project directory. Now your extension should be visible in Chrome.
Deploying to the Chrome Web Store
Deploying to the Chrome Web Store is straightforward. It does need attention to detail due to Google's review process. Follow these steps:
1. Zip Your Application: Ensure your project follows the correct folder structure. manifest.json
should be at the root.
2. Set Up a Developer Account: Navigate to the Chrome Web Store Developer Dashboard. Then pay a one-time fee of $5 to publish extensions.
3. Upload Your Extension: Click "Add a new item," upload your zip file. Fill out the required details such as title, description, and privacy policy.
4. Submit for Review: After completing the form, submit your extension for review. This process can take from a few hours to a few days. After the review, you will receive an email.
Enhancements
Consider adding these features to improve your extension:
Renaming sessions or organizing sessions into categories.
Implementing test cases for your script file.
Interacting with users to gather feedback and improve the extension.
Monetizing your extension by offering a premium version with extra features.
Conclusion
Congratulations, you've accomplished creating a Chrome extension! Added full functionality and styling, and published it to the Chrome Web Store. By following this guide, you have learned extension development and deployment steps. Keep experimenting and enhancing your extension to make it even more useful.
Top comments (2)
Great guide on building a Chrome extension! Could you elaborate more on the review process for getting an extension published on the Chrome Web Store? Thanks!
Thank you for the positive feedback! I'm glad you found the guide helpful.
The review process for publishing a Chrome extension on the Chrome Web Store involves a few key steps:
Automated Review: Google runs automated scans to check for malware and other security issues.
Manual Review: A human reviewer ensures your extension complies with Chrome Web Store policies, including functionality, privacy, and user experience.
Feedback: If issues are found, you'll receive feedback on necessary changes. You can address these and resubmit.
I hope this helps :)