Sebastian Gomez
How to Create and Debug Chrome Extension Popups
Introduction
In this post I'll show you how to create and debug popups in Chrome extensions. We'll explore how to set up a popup.html file, add stylesheets and scripts, and debug possible errors. This step by step guide will help you build functional, attractive popups for your extensions.
The popup is, in most cases, the application's entry point and often corresponds to a small window that appears right below your extension's icon. You can define the file's name and location, but it must be clearly specified in the manifest like this:
{
"action": {
"default_popup": "popup.html" // or index.html or hola.html
}
}Valid as of 2026: This post uses Manifest V3 ("manifest_version": 3) with the action.default_popup key, which is the correct, non deprecated approach. Chrome fully retired Manifest V2 in 2024, so MV3 is exactly what you should use today.
Creating the popup.html File
To get started, we need to create the popup.html file, which will be the content of our extension's popup.
Create the popup.html File: create a file named popup.html in your project and add the basic HTML below. Notice that both Bootstrap's stylesheet and its JavaScript load from relative paths, that is, from files that will live inside the extension package itself:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Login Form</title>
<!-- Bootstrap 5 bundled locally inside the extension -->
<link href="vendor/bootstrap.min.css" rel="stylesheet">
<link href="popup.css" rel="stylesheet">
</head>
<body>
<div class="login-container">
<h2>Welcome Back!</h2>
<form>
<div class="mb-3">
<label for="email" class="form-label">Email Address</label>
<input type="email" class="form-control" id="email" placeholder="Enter your email address">
</div>
<div class="mb-3">
<label for="password" class="form-label">Password</label>
<input type="password" class="form-control" id="password" placeholder="Enter your password">
</div>
<div class="d-grid">
<button type="submit" class="btn btn-custom">Log In</button>
</div>
</form>
<p class="mt-3">Don't have an account? <a href="#" style="color: #ff6f61;">Sign Up</a></p>
</div>
<!-- Bootstrap 5 already includes Popper in its bundle; jQuery is not needed -->
<script src="vendor/bootstrap.bundle.min.js"></script>
<script src="popup.js"></script>
</body>
</html>Why local files and not a CDN? Later we'll see that the default Manifest V3 content security policy only allows scripts from the extension's own origin (script-src 'self'). That means a <script src="https://..."> pointing to a remote CDN would be blocked at runtime and simply would not execute. That's why we download Bootstrap and keep it inside the extension, for example in a vendor/ folder, and reference it with relative paths.
To get those files, download Bootstrap 5.3.x from its official site or from npm and copy bootstrap.min.css and bootstrap.bundle.min.js into your vendor/ folder. If you prefer npm:
npm install bootstrap@5.3.8Historical note: older versions of this tutorial used Bootstrap 4 from StackPath BootstrapCDN, which has since been shut down, plus standalone jQuery and Popper, an obsolete pattern in Bootstrap 5. Today you install it from npm (the bootstrap package, compiled files under node_modules/bootstrap/dist/).
Update the Manifest: modify the manifest.json file to include the popup:
{
"manifest_version": 3,
"name": "My Extension",
"version": "0.0.1",
"action": {
"default_popup": "popup.html",
"default_icon": {
"16": "icon-16.png",
"48": "icon-48.png",
"128": "icon-128.png"
}
}
}Verify the Changes: reload the extension in Chrome and verify that the popup appears correctly when you click the extension's icon.
Adding Styles and Scripts
To improve your popup's design and functionality, you can add your own CSS stylesheets and JavaScript scripts, always inside the extension package.
Create a Stylesheet: create a file named popup.css in your project and add the following CSS:
body {
background: linear-gradient(135deg, #f6d365 0%, #fda085 100%);
width: 400px;
height: 100vh;
display: flex;
justify-content: center;
align-items: center;
font-family: 'Comic Sans MS', cursive, sans-serif;
margin: 0;
}
.login-container {
background: #fff;
padding: 2rem;
border-radius: 15px;
box-shadow: 0 4px 10px rgba(0, 0, 0, 0.1);
text-align: center;
width: 100%;
max-width: 400px;
}
.login-container h2 {
color: #ff6f61;
margin-bottom: 1rem;
}
.btn-custom {
background: #ff6f61;
border: none;
color: white;
transition: background 0.3s;
}
.btn-custom:hover {
background: #ff8f81;
}
.form-label {
float: left;
}Make sure popup.html links correctly to this stylesheet (we already included it in the <head> of the previous example):
<link href="popup.css" rel="stylesheet">Add a Script: create a file named popup.js and add the following JavaScript:
document.addEventListener('DOMContentLoaded', function() {
alert("This way we've fully disabled React and use a standard Chrome extension popup with just HTML, CSS and JavaScript");
});Remember we already included this script in popup.html:
<script src="popup.js"></script>Reload and Test: reload the extension and verify that the styles and scripts apply correctly.
Debugging the Extension
Extension errors can be seen in several ways. First, non critical errors appear when you click the "Errors" button on the extension's card in the chrome://extensions list:
Second are the critical errors. These don't even let the extension run and mainly come from errors in the manifest.json:
The third way is to debug the popup as if it were a normal web app, using "Inspect element".
Debugging the Popup
Remember that the popup is nothing more than an independent application, isolated and self contained inside the application's container, which makes debugging popups crucial to ensure your extension works correctly. Here are some steps to help you find and fix errors.
Check Errors in Chrome: after reloading your extension, open the popup and right click to select "Inspect", as if it were a website. This will open the Chrome developer tools.
Review the Console: the console will show you any errors in your code. You can search these errors on Google to find solutions.
In Depth Debugging: use the "Sources" and "Network" tabs in the developer tools to identify problems with scripts and network requests.
Note: It's not good practice to load external JavaScript or CSS files in your extension, since that introduces security problems and is generally considered an unaccepted bad practice when developing Chrome extensions. That's exactly why, in this post's example, we bundle Bootstrap inside the extension itself and load it with relative paths instead of pulling it from a CDN. You can explore the content_security_policy option in the manifest and add exceptions, but in general it's not recommended.
The content security policy (CSP) is an essential security feature for Chrome extensions that helps prevent various kinds of attacks, such as Cross Site Scripting (XSS) and data injection. In the context of Chrome extensions, the CSP configuration is defined in the manifest.json file, under the content_security_policy key.
Defining CSP for Extension Pages
In Chrome extensions, the content security policy for the extension's pages can be specified as follows:
{
"manifest_version": 3,
"name": "My Secure Extension",
"version": "1.0",
"content_security_policy": {
"extension_pages": "default-src 'self'; script-src 'self' https://apis.google.com"
}
}Valid as of 2026: This syntax is the correct one for Manifest V3. In Manifest V2 the content_security_policy key was a flat string, whereas in MV3 it's an object with keys like extension_pages. It remains the current approach.
Components of the Policy
- `default-src 'self'`: states that, by default, all resources can only be loaded from the same origin as the extension, that is, from the extension package itself. This is precisely what forced us to bundle Bootstrap locally.
- `script-src 'self' https://apis.google.com`: allows scripts to load only from the same origin and from the
apis.google.comdomain. This setting is useful if your extension needs to interact with Google services.
Why CSP Matters in Extensions
Configuring the content security policy correctly is crucial to protect your extension and its users. By restricting the sources from which resources can be loaded, you minimize the chances of code injection attacks and other security threats.
Conclusion
Creating and debugging popups in Chrome extensions can seem complex at first, but by following these steps you'll be able to build functional, attractive popups. From the basic popup.html setup to integrating a locally bundled Bootstrap 5 and debugging errors, this guide gives you the tools you need to improve your Chrome extensions.
Suggested exercises
- Create a basic extension with Manifest V3 and a
popup.htmlthat shows a simple form, using Bootstrap 5 downloaded into the extension'svendor/folder. - Add a
popup.jsthat reacts to the form submission (for example, logging a message to the console) and debug it by opening the developer tools over the popup. - Define a
content_security_policywithextension_pagesand check what happens if you try to load a script from a remote CDN: watch how the CSP blocks it in the console.
3-point summary
- The popup is the entry point of many extensions; it's declared with
action.default_popupinmanifest.jsonusing Manifest V3. - Bundle your styles and scripts (including Bootstrap 5) inside the extension and reference them with relative paths, because the default MV3 CSP blocks remote resources.
- Debug the popup with the Chrome developer tools and review the non critical and critical errors from
chrome://extensions.
That's all, I hope this post is useful to you and that you can apply it to a project you have in mind.
Leave me a comment if it helped, if you want to add an opinion, or if you have any questions. And remember, if you liked it, you can also share it using the social links below. Keep practicing and improving your skills to build even more useful and sophisticated extensions!
Sebastian Gomez
Creador de contenido principalmente acerca de tecnología.