5 Types of Chrome Extensions You Can Build (And How Each One Works)
By BasharathEver wondered why some Chrome extensions open in a small popup, while others take over your entire tab or quietly work in the background? The type of Chrome extension you build depends entirely on how users will interact with it. And here's the interesting part: you can build all these different types using the same HTML, CSS, and JavaScript code with just a few tweaks to your manifest file.
In this guide, we'll explore the five main types of Chrome extensions, when to use each one, and exactly what changes you need to make to transform a basic todo list extension into any of these formats. By the end, you'll know which extension type fits your idea and how to build it.
The 5 Main Types of Chrome Extensions
Chrome extensions come in different flavors based on how and where they display their interface. Each type serves a specific purpose and offers a unique user experience. Every chrome extension's type is determined in the manifest.json. Here is a comprehensive manifest.json guide to learn more about it. Now Let's dive into the 5 types of Chrome extensions.
1. Popup Extensions (Action Popup)
What it is: A popup extension displays a small window when users click the extension icon in the toolbar. This is the most common type of Chrome extension and perfect for quick interactions.
Best for:
Todo lists, note-taking apps
Quick tools (calculators, converters, timers)
Shortcut actions that don't need full-screen space
Lightweight utilities
User Experience: Users click your icon, interact with your popup, and close it. The popup disappears when they click outside of it.
Real-world examples: Grammarly (quick grammar check), LastPass (password access), Google Translate (quick translations)
2. Side Panel Extensions
What it is: A side panel extension opens in Chrome's side panel (the area on the left or right side of the browser). It stays open while users browse, making it perfect for persistent tools.
Best for:
Reading lists or bookmarks
Notes that need to stay visible while browsing
Research tools, dictionaries
Multi-tab productivity apps
User Experience: Users open the side panel and your extension stays there, accessible across all tabs without disrupting their browsing.
Real-world examples: Reading list apps, bookmark managers, note-taking tools that need to be always accessible
3. Full Tab Extensions (Override Pages)
What it is: A full tab extension takes over an entire new tab, replacing Chrome's default new tab page or opening in its own dedicated tab.
Best for:
Dashboard apps (analytics, monitoring)
Full-featured productivity tools
Custom new tab pages with widgets
Apps that need more screen space
User Experience: Users open your extension in a full browser tab, just like visiting a website. They get maximum screen space and can bookmark the tab.
Real-world examples: Momentum (new tab replacement), Trello power-ups, custom dashboards
4. Options Page Extensions
What it is: An options page is a settings or configuration page for your extension. It's accessed by right-clicking the extension icon and selecting "Options" or through the extension management page.
Best for:
Extension settings and preferences
Configuration panels
User account management
Advanced customization options
User Experience: Users access this page separately from the main extension to configure how your extension behaves.
Real-world examples: Ad blockers (whitelist settings), Link Snapper(link format settings) theme managers (color preferences), privacy extensions (configuration)
5. Content Script Extensions (Background/Invisible)
What it is: Content script extensions run in the background or inject code directly into web pages. They don't have a visible UI but modify or enhance existing websites.
Best for:
Ad blockers, privacy tools
Page enhancers (dark mode, font changers)
Auto-fill tools
Web scrapers or data extractors
User Experience: Users may not even know the extension is running. It silently works in the background or modifies pages they visit.
Real-world examples: uBlock Origin (ad blocking), Dark Reader (dark mode), Honey (coupon finder)
The Base Code: A Simple Todo List Extension
Before we dive into how to create each type, let's establish our base code. We'll use a simple todo list extension that we can adapt for each extension type. This code uses vanilla JavaScript with Chrome's storage API for persistence.
The File Structure
my-extension/
├── manifest.json
├── popup.html
├── popup.css
├── popup.js
└── icon.pngBase manifest.json
{
"manifest_version": 3,
"name": "Simple Todo List",
"version": "1.0",
"description": "A simple and elegant todo list extension",
"permissions": ["storage"],
"action": {
"default_title": "My Todo List",
"default_popup": "popup.html",
"default_icon": "icon.png"
}
}popup.html (Base HTML)
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Todo List</title>
<link rel="stylesheet" href="popup.css">
</head>
<body>
<div class="container">
<h1>To-Do List</h1>
<div class="input-section">
<input
type="text"
id="todoInput"
placeholder="Add your task"
autocomplete="off"
>
<button id="addBtn">ADD</button>
</div>
<ul id="todoList" class="todo-list"></ul>
<div id="emptyState" class="empty-state">
<p>No tasks yet. Add one to get started!</p>
</div>
</div>
<script src="popup.js"></script>
</body>
</html>popup.css (Base CSS)
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
width: 420px;
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto,
'Helvetica Neue', sans-serif;
margin: 0;
}
.container {
padding: 24px;
background: white;
min-height: 500px;
border-radius: 12px;
box-shadow: 0 8px 32px rgba(0, 0, 0, 0.1);
}
h1 {
font-size: 28px;
font-weight: 700;
color: #1a1a1a;
margin-bottom: 24px;
}
.input-section {
display: flex;
gap: 10px;
margin-bottom: 24px;
}
#todoInput {
flex: 1;
padding: 12px 16px;
border: 1px solid #e0e0e0;
border-radius: 8px;
font-size: 15px;
outline: none;
background: #f5f5f5;
transition: all 0.3s ease;
}
#todoInput:focus {
background: white;
border-color: #ff6b6b;
box-shadow: 0 0 0 3px rgba(255, 107, 107, 0.1);
}
#addBtn {
padding: 12px 28px;
background: #ff6b6b;
color: white;
border: none;
border-radius: 8px;
font-weight: 700;
font-size: 15px;
cursor: pointer;
transition: all 0.3s ease;
}
#addBtn:hover {
background: #ff5252;
transform: translateY(-2px);
box-shadow: 0 4px 12px rgba(255, 107, 107, 0.3);
}
#addBtn:active {
transform: translateY(0);
}
.todo-list {
list-style: none;
margin-bottom: 16px;
}
.todo-item {
display: flex;
align-items: center;
padding: 16px;
background: #f8f9fa;
border-radius: 8px;
margin-bottom: 10px;
gap: 12px;
transition: all 0.2s ease;
}
.todo-item:hover {
background: #f0f1f3;
}
.todo-item input[type='checkbox'] {
width: 20px;
height: 20px;
cursor: pointer;
accent-color: #ff6b6b;
flex-shrink: 0;
border-radius: 50%;
}
.todo-item.completed input[type='checkbox'] {
accent-color: #ff6b6b;
}
.todo-item span {
flex: 1;
font-size: 15px;
color: #333;
word-break: break-word;
transition: all 0.2s ease;
}
.todo-item.completed span {
text-decoration: line-through;
color: #999;
opacity: 0.7;
}
.delete-btn {
background: transparent;
color: #ff6b6b;
border: none;
padding: 6px 8px;
cursor: pointer;
font-size: 18px;
transition: all 0.2s ease;
flex-shrink: 0;
display: flex;
align-items: center;
justify-content: center;
}
.delete-btn:hover {
color: #ff5252;
transform: scale(1.2);
}
.empty-state {
text-align: center;
padding: 40px 20px;
color: #999;
font-size: 14px;
}
.empty-state.hidden {
display: none;
}popup.js (Base JavaScript)
const todoInput = document.getElementById('todoInput');
const addBtn = document.getElementById('addBtn');
const todoList = document.getElementById('todoList');
const emptyState = document.getElementById('emptyState');
// Load todos when popup opens
document.addEventListener('DOMContentLoaded', loadTodos);
// Add todo on button click
addBtn.addEventListener('click', addTodo);
// Add todo on Enter key press
todoInput.addEventListener('keypress', (e) => {
if (e.key === 'Enter') {
addTodo();
}
});
function addTodo() {
const todoText = todoInput.value.trim();
if (todoText === '') {
todoInput.focus();
return;
}
const todo = {
id: Date.now(),
text: todoText,
completed: false
};
chrome.storage.local.get('todos', (result) => {
const todos = result.todos || [];
todos.push(todo);
chrome.storage.local.set({ todos }, () => {
todoInput.value = '';
todoInput.focus();
loadTodos();
});
});
}
function loadTodos() {
chrome.storage.local.get('todos', (result) => {
const todos = result.todos || [];
todoList.innerHTML = '';
if (todos.length === 0) {
emptyState.classList.remove('hidden');
return;
}
emptyState.classList.add('hidden');
todos.forEach((todo) => {
const li = document.createElement('li');
li.className = `todo-item ${todo.completed ? 'completed' : ''}`;
const checkbox = document.createElement('input');
checkbox.type = 'checkbox';
checkbox.checked = todo.completed;
checkbox.addEventListener('change', () => toggleTodo(todo.id));
const span = document.createElement('span');
span.textContent = todo.text;
const deleteBtn = document.createElement('button');
deleteBtn.className = 'delete-btn';
deleteBtn.textContent = '✕';
deleteBtn.addEventListener('click', () => deleteTodo(todo.id));
li.appendChild(checkbox);
li.appendChild(span);
li.appendChild(deleteBtn);
todoList.appendChild(li);
});
});
}
function toggleTodo(id) {
chrome.storage.local.get('todos', (result) => {
const todos = result.todos || [];
const todo = todos.find(t => t.id === id);
if (todo) {
todo.completed = !todo.completed;
chrome.storage.local.set({ todos }, loadTodos);
}
});
}
function deleteTodo(id) {
chrome.storage.local.get('todos', (result) => {
let todos = result.todos || [];
todos = todos.filter(t => t.id !== id);
chrome.storage.local.set({ todos }, loadTodos);
});
}Now that we have our base code, let's see what changes are needed for each extension type.
How to Adapt the Code for Each Extension Type
1. Popup Extension (Already Complete!)
Manifest changes: None needed. The base code is already a popup extension.
CSS changes: Keep the body width at 420px for a compact popup experience.
When to use: This is your default choice for quick-access tools that don't need to stay open permanently.
2. Side Panel Extension
Manifest changes required:
{
"manifest_version": 3,
"name": "Simple Todo List - Side Panel",
"version": "1.0",
"description": "A simple and elegant todo list extension in the side panel",
"permissions": ["storage", "sidePanel"],
"side_panel": {
"default_path": "popup.html"
},
"action": {
"default_title": "My Todo List",
"default_icon": "icon.png"
}
}Key changes:
Add
"sidePanel"to permissionsAdd
"side_panel"object withdefault_pathpointing to your HTML fileRemove
default_popupfrom action (clicking the icon now opens the side panel)
CSS changes:
body {
width: 100%; /* Remove fixed width */
min-width: 300px;
height: 100vh;
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto,
'Helvetica Neue', sans-serif;
margin: 0;
}
.container {
padding: 24px;
background: white;
min-height: 100vh; /* Full height */
border-radius: 0; /* Remove border radius for side panel */
box-shadow: none; /* Remove shadow for side panel */
}When to use: Your extension needs to stay visible while users browse other tabs, like a persistent notepad or research tool.
3. Full Tab Extension
Manifest changes required:
{
"manifest_version": 3,
"name": "Simple Todo List - Full Tab",
"version": "1.0",
"description": "A full-page todo dashboard",
"permissions": ["storage"],
"chrome_url_overrides": {
"newtab": "popup.html"
},
"action": {
"default_title": "My Todo List",
"default_icon": "icon.png"
}
}Alternative approach (opens in a tab when clicking icon):
Add a background script:
{
"manifest_version": 3,
"name": "Simple Todo List - Full Tab",
"version": "1.0",
"description": "A full-page todo dashboard",
"permissions": ["storage"],
"action": {
"default_title": "My Todo List",
"default_icon": "icon.png"
},
"background": {
"service_worker": "background.js"
}
}Create background.js:
chrome.action.onClicked.addListener((tab) => {
chrome.tabs.create({ url: 'popup.html' });
});CSS changes:
body {
width: 100%;
height: 100vh;
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
display: flex;
justify-content: center;
align-items: center;
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto,
'Helvetica Neue', sans-serif;
margin: 0;
}
.container {
width: 90%;
max-width: 800px; /* Larger for full screen */
min-height: 600px;
margin: 40px auto;
padding: 40px;
background: white;
border-radius: 16px;
box-shadow: 0 20px 60px rgba(0, 0, 0, 0.2);
}When to use: You're building a dashboard, complex tool, or want to replace Chrome's new tab page.
4. Options Page Extension
Manifest changes required:
{
"manifest_version": 3,
"name": "Simple Todo List with Settings",
"version": "1.0",
"description": "A simple and elegant todo list extension with customizable options",
"permissions": ["storage"],
"action": {
"default_title": "My Todo List",
"default_popup": "popup.html",
"default_icon": "icon.png"
},
"options_page": "options.html"
}Additional file needed: Create options.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Extension Options</title>
<link rel="stylesheet" href="popup.css">
</head>
<body>
<div class="container">
<h1>Settings</h1>
<div class="setting">
<label>
<input type="checkbox" id="darkMode">
Enable Dark Mode
</label>
</div>
<div class="setting">
<label>
Theme Color:
<input type="color" id="themeColor" value="#ff6b6b">
</label>
</div>
<button id="saveBtn" class="add-btn">Save Settings</button>
<p id="status"></p>
</div>
<script src="options.js"></script>
</body>
</html>Create options.js:
const saveBtn = document.getElementById('saveBtn');
const status = document.getElementById('status');
document.addEventListener('DOMContentLoaded', () => {
chrome.storage.local.get(['darkMode', 'themeColor'], (result) => {
document.getElementById('darkMode').checked = result.darkMode || false;
document.getElementById('themeColor').value = result.themeColor || '#ff6b6b';
});
});
saveBtn.addEventListener('click', () => {
const darkMode = document.getElementById('darkMode').checked;
const themeColor = document.getElementById('themeColor').value;
chrome.storage.local.set({ darkMode, themeColor }, () => {
status.textContent = 'Settings saved!';
setTimeout(() => status.textContent = '', 2000);
});
});When to use: Your extension has user preferences, settings, or configuration options that don't fit in the main interface.
5. Content Script Extension
Manifest changes required:
{
"manifest_version": 3,
"name": "Page Highlighter",
"version": "1.0",
"description": "Highlights text on web pages",
"permissions": ["storage", "activeTab"],
"content_scripts": [
{
"matches": ["<all_urls>"],
"js": ["content.js"],
"css": ["content.css"]
}
],
"action": {
"default_title": "My Todo List",
"default_icon": "icon.png"
}
}New file needed: Create content.js
// This code runs on every webpage
document.addEventListener('mouseup', () => {
const selectedText = window.getSelection().toString().trim();
if (selectedText.length > 0) {
highlightText(selectedText);
}
});
function highlightText(text) {
const span = document.createElement('span');
span.style.backgroundColor = 'yellow';
span.style.padding = '2px';
// Highlight logic here
console.log('Highlighting:', text);
}
// Listen for messages from popup
chrome.runtime.onMessage.addListener((request, sender, sendResponse) => {
if (request.action === 'highlight') {
// Perform highlighting
sendResponse({ success: true });
}
});When to use: Your extension modifies web pages, blocks ads, changes styles, or extracts data from websites.
Choosing the Right Extension Type
Here's a quick decision guide:
Quick tool, small UI: → Popup Extension
Always accessible, side-by-side browsing: → Side Panel
Dashboard, lots of features, full screen: → Full Tab
Settings or preferences: → Options Page (combined with another type)
Modifies websites, works invisibly: → Content Script
Most real-world extensions actually combine multiple types. For example, Grammarly has a popup for quick access, content scripts to work on web pages, and an options page for settings.
What You've Learned
You now understand the five main types of Chrome extensions and exactly what code changes are needed to transform your basic extension into each type. The beauty of Chrome extensions is that the core logic (your HTML, CSS, and JavaScript) stays mostly the same. It's just the manifest configuration and a few CSS tweaks that determine how your extension appears to users.
Start with the type that matches your use case, build it out, and then expand to other types if needed. The same todo list code works beautifully as a popup, side panel, or full tab—you just need to tell Chrome where to display it.
Happy building!
Written by Basharath
Chrome extension developer sharing insights, tutorials, and best practices for building better browser extensions.
