Chrome Extension Side Panel API — How to Build a Sidebar UI
9 min readChrome Extension Side Panel API — How to Build a Sidebar UI
Introduction
The Chrome Side Panel API, introduced in Chrome 114, provides a modern way to create persistent sidebar UIs for your Chrome extension. Unlike popup windows that disappear when you click away, side panels remain open alongside the web page, giving users easy access to your extension’s features without interrupting their browsing experience.
This guide covers everything you need to build a sidebar UI: manifest configuration, the chrome.sidePanel API methods, per-tab panel customization, and UX best practices for creating a polished user experience.
Manifest Setup
To use the Side Panel API, you need to declare the sidePanel permission in your manifest.json and specify a default panel path.
{
"name": "My Side Panel Extension",
"version": "1.0.0",
"manifest_version": 3,
"permissions": ["sidePanel"],
"side_panel": {
"default_path": "sidepanel.html"
}
}
The side_panel key defines the default HTML file that loads when the side panel opens. This file should contain your complete sidebar UI, including any CSS and JavaScript needed for the panel’s functionality.
The chrome.sidePanel API
The chrome.sidePanel API provides methods to control the side panel’s behavior, content, and visibility. Here’s an overview of the main methods:
Setting Panel Behavior
By default, clicking the extension’s toolbar icon opens the action popup. You can redirect this to open the side panel instead:
// background.js
chrome.sidePanel.setPanelBehavior({ openPanelOnActionClick: true });
This single line changes the toolbar icon behavior to show the side panel when clicked. The panel persists open until the user explicitly closes it or navigates to a new tab where it’s disabled.
Opening the Side Panel Programmatically
You can open the side panel from your background script or content script using the open() method:
// Open in the current window
await chrome.sidePanel.open({ windowId: chrome.windows.WINDOW_ID_CURRENT });
// Open for a specific tab
await chrome.sidePanel.open({ tabId: tab.id });
The open() method requires a user gesture (such as a click or keyboard shortcut) to work. If you try to call it without a user gesture, it will fail silently or throw an error.
Setting Panel Options
Use setOptions() to configure the panel’s content and visibility:
// Set a global default panel
chrome.sidePanel.setOptions({
path: "sidepanel.html",
enabled: true
});
// Set a tab-specific panel
chrome.sidePanel.setOptions({
tabId: tab.id,
path: "tab-specific-panel.html",
enabled: true
});
Getting Panel Options
Retrieve the current panel configuration:
// Get options for a specific tab
const options = await chrome.sidePanel.getOptions({ tabId: tab.id });
console.log(options.path, options.enabled);
Per-Tab Side Panels
One of the most powerful features of the Side Panel API is the ability to show different content for different tabs. This is perfect for extensions that need context-aware content.
// content-script.js or background.js
chrome.tabs.onUpdated.addListener((tabId, changeInfo, tab) => {
if (changeInfo.status === 'complete' && tab.url) {
if (tab.url.includes('github.com')) {
// Show GitHub-specific panel
chrome.sidePanel.setOptions({
tabId: tabId,
path: "github-panel.html",
enabled: true
});
} else if (tab.url.includes('docs.')) {
// Show documentation-specific panel
chrome.sidePanel.setOptions({
tabId: tabId,
path: "docs-panel.html",
enabled: true
});
} else {
// Disable panel for non-matching tabs
chrome.sidePanel.setOptions({
tabId: tabId,
enabled: false
});
}
}
});
This pattern allows your extension to provide relevant tools based on the current page, making the side panel feel intelligent and context-aware.
Closing the Side Panel
Users can close the side panel by clicking the close button (X) or by toggling it off. From code, you cannot directly close the side panel—the user must take action. However, you can disable it for specific tabs or windows:
// Disable side panel for a specific tab
chrome.sidePanel.setOptions({
tabId: tabId,
enabled: false
});
UX Patterns and Best Practices
Responsive Width Design
Side panels have a default width, but users can resize them. Design your panel to handle varying widths gracefully:
/* sidepanel.css */
body {
min-width: 250px;
max-width: 400px;
width: 100%;
box-sizing: border-box;
}
.panel-content {
display: flex;
flex-direction: column;
height: 100vh;
padding: 16px;
}
Communication with the Active Tab
Your side panel often needs to interact with the current page. Use message passing:
// sidepanel.js — Send message to content script
chrome.tabs.query({ active: true, currentWindow: true }, (tabs) => {
chrome.tabs.sendMessage(tabs[0].id, { action: "getData" }, (response) => {
console.log("Page data:", response);
});
});
// content-script.js — Listen for messages
chrome.runtime.onMessage.addListener((message, sender, sendResponse) => {
if (message.action === "getData") {
sendResponse({ data: document.title, url: window.location.href });
}
});
Persisting User Preferences
Store user settings and panel preferences using the storage API:
// sidepanel.js
async function loadPreferences() {
const prefs = await chrome.storage.local.get(['theme', 'collapsedSections']);
applyTheme(prefs.theme);
restoreSections(prefs.collapsedSections);
}
document.getElementById('theme-toggle').addEventListener('click', async () => {
const newTheme = currentTheme === 'light' ? 'dark' : 'light';
await chrome.storage.local.set({ theme: newTheme });
applyTheme(newTheme);
});
Keyboard Shortcuts Integration
The side panel works seamlessly with keyboard shortcuts defined in your manifest:
{
"commands": {
"toggle-side-panel": {
"suggested_key": {
"default": "Ctrl+Shift+S",
"mac": "Command+Shift+S"
},
"description": "Toggle the side panel"
}
}
}
// background.js
chrome.commands.onCommand.addListener(async (command) => {
if (command === "toggle-side-panel") {
// Check if panel is open and toggle accordingly
const tabs = await chrome.tabs.query({ active: true, currentWindow: true });
const options = await chrome.sidePanel.getOptions({ tabId: tabs[0].id });
if (options.enabled) {
chrome.sidePanel.setOptions({ tabId: tabs[0].id, enabled: false });
} else {
await chrome.sidePanel.open({ tabId: tabs[0].id });
}
}
});
Common Issues and Solutions
Panel Not Opening
If your side panel doesn’t open, check these common issues:
- Missing
sidePanelpermission in manifest - Incorrect path in
side_panel.default_path - Calling
open()without a user gesture
Content Script Not Running in Panel
Side panel HTML files don’t load content scripts automatically. If you need scripts in your panel, include them directly:
<!-- sidepanel.html -->
<!DOCTYPE html>
<html>
<head>
<link rel="stylesheet" href="sidepanel.css">
</head>
<body>
<div id="app"></div>
<script src="sidepanel.js"></script>
</body>
</html>
Memory Management
Since side panels persist, be mindful of memory usage:
- Clean up event listeners when the panel unloads
- Release resources when disabled for specific tabs
- Use
chrome.tabs.onRemovedto clean up tab-specific resources
Conclusion
The Side Panel API opens up new possibilities for Chrome extension UIs. By providing a persistent, context-aware sidebar, you can create more engaging and useful extensions that don’t interrupt the user’s workflow. Remember to test with different panel widths, integrate with keyboard shortcuts, and use per-tab panels to provide relevant content for each page your users visit.