DEV Community

Cover image for How to Build and Publish a Chrome Extension: Step-by-Step Guide to Creating a Custom Session Saver
Jesus Esquer
Jesus Esquer

Posted on

How to Build and Publish a Chrome Extension: Step-by-Step Guide to Creating a Custom Session Saver

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.

plugin-enabled

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

Enter fullscreen mode Exit fullscreen mode

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"
    }
}

Enter fullscreen mode Exit fullscreen mode

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();
    });
  }
});
Enter fullscreen mode Exit fullscreen mode

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>
Enter fullscreen mode Exit fullscreen mode

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();
});

Enter fullscreen mode Exit fullscreen mode

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 a li 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 session ul from popup.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 trigger loadSession to reload the list and reflect the new item.

  • We add a listener to handle refreshSessions messages from background.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.

Icon

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;
}
Enter fullscreen mode Exit fullscreen mode

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.

Extension_unpacked_visible

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.

submit_for_review_popup

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.

extension_published

Public extension link

Code repository

Top comments (2)

Collapse
 
chariebee profile image
Charles Brown

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!

Collapse
 
jm27 profile image
Jesus Esquer

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 :)