You opened your popup, dragged it around, and realized something that every Chrome extension developer eventually realizes: the popup is a terrible place to live. It closes the second a user clicks away, it cannot persist state across tabs, and it boxes your entire UI into a 600 by 800 pixel cage. The side panel solves all of that, and yet most extensions on the Chrome Web Store in 2026 still ignore it. If you are about to build something that needs to stay visible while the user keeps browsing, this is the surface you want, and this guide walks through building one end to end with Manifest V3.
1.
2.
3.
4.
5.
6.
7.
8.
9.
Why the Side Panel Is the Most Underused Surface in Chrome Extensions
The side panel landed as a stable API in Chrome 114 and has quietly become the best place to put almost anything that is not a one-tap action. It docks to the side of the browser, stays open as the user navigates between tabs, and gives you a real, resizable canvas. Compare that to a popup, which dies the instant the user clicks the page, or a content script, which has to fight the host website for every pixel and z-index. The side panel is the only built in surface that lets you build something that feels like an actual app.
The reason it is still underused in 2026 is that the documentation is scattered, half the tutorials online are written against pre-114 APIs that no longer work, and a few of the most important methods are not what you would expect. So most developers default to a popup, ship something half as good as it could be, and never circle back. Skip that mistake.
What the Side Panel API Actually Gives You
chrome.sidePanel exposes three things that matter. First, you can declare a default panel in your manifest so every tab has the same UI ready to open. Second, you can swap that panel per tab using sidePanel.setOptions, which means you can show one experience on YouTube and a completely different one on GitHub. Third, you can open the panel programmatically with sidePanel.open(), but only inside a user gesture, which is the single most common gotcha and the reason most "why won't my panel open" Stack Overflow threads exist.
A side panel is just an extension page, which means it has full access to the Chrome APIs. You can call chrome.tabs, chrome.storage, chrome.runtime.sendMessage, anything you would call from a popup or background service worker. The difference is that the panel persists, so you do not have to re-fetch and re-render every time the user clicks your icon.
The Minimum Viable Manifest V3 Setup
Here is the smallest manifest that gives you a working side panel. Save this as manifest.json.
{
"manifest_version": 3,
"name": "My Side Panel Extension",
"version": "1.0.0",
"permissions": ["sidePanel", "tabs", "storage"],
"side_panel": {
"default_path": "sidepanel.html"
},
"action": {
"default_title": "Open side panel"
},
"background": {
"service_worker": "background.js"
}
}
Then create a sidepanel.html file with whatever you want inside it. It is just a normal HTML page with access to extension APIs. Load the unpacked extension from chrome://extensions, click your toolbar icon, and… nothing happens. That is expected, and it is the next problem to solve.
Opening the Panel the Right Way
This is where almost everyone gets stuck. There are two ways to open the side panel and they behave very differently.
The first is to tell Chrome to open it whenever the user clicks your action icon. You do this from your background service worker, not from the manifest. There is no openPanelOnActionClick field in the manifest in 2026, despite what older blog posts claim. You have to set it from code.
// background.js
chrome.runtime.onInstalled.addListener(() => {
chrome.sidePanel
.setPanelBehavior({ openPanelOnActionClick: true })
.catch((error) => console.error(error));
});
The second way is to call chrome.sidePanel.open() directly. This is what you want when you need to open the panel from a context menu, a keyboard shortcut, or a content script. The catch is that open() has to be called inside a user gesture, so you cannot fire it from a setTimeout or after an awaited fetch. If you try, Chrome silently ignores it and you spend an hour wondering why your code looks correct but does nothing. Move the open() call to the synchronous handler of the click or command, and it works.
ExtensionFast
Ship Your Chrome Extension This Weekend
ExtensionFast gives you auth, payments, and a landing page out of the box — so you can focus on your unique features, not the boilerplate.

Per-Site Side Panels with setOptions
One of the best uses of the side panel is to show a different UI depending on which site the user is on. A YouTube transcript tool, a GitHub PR helper, a Notion clipper, all of these benefit from per-tab logic. You enable that with chrome.sidePanel.setOptions from your background service worker, gated on the active tab URL.
// background.js
chrome.tabs.onUpdated.addListener(async (tabId, info, tab) => {
if (!tab.url) return;
const url = new URL(tab.url);
if (url.hostname === "www.youtube.com") {
await chrome.sidePanel.setOptions({
tabId,
path: "youtube-panel.html",
enabled: true,
});
} else {
await chrome.sidePanel.setOptions({
tabId,
enabled: false,
});
}
});
The enabled: false line is what hides the panel entry on tabs where it does not apply, instead of showing a useless empty UI. This is the part of the API that earns its keep, and it is what makes a side panel feel intentional rather than bolted on.
Side Panel Plus React: A Pattern That Actually Ships
Once you go past a few buttons and a list, you want a real component framework. The side panel is just an extension page, so any React, Vue, or Svelte setup that works for a popup works here too. The trick is wiring up Vite or Webpack to emit sidepanel.html as a separate entry point alongside popup.html and background.js. If you have not done this before, the full toolchain is covered in how to build a Chrome extension with React and TypeScript in 2026, which walks through the multi-entry build that the side panel needs.
The thing to remember is that the side panel re-mounts when the user closes and reopens it, so any state you want to survive needs to live in chrome.storage or in your service worker, not in component state. Treat the panel as a view, not as the source of truth.
Cross-Browser Reality Check
If you care about Edge and Firefox, the picture is not symmetrical. Edge ships a compatible chrome.sidePanel API, so the same code usually runs unchanged. Firefox has its own sidebar_action manifest key with a different shape and a different lifecycle, so you have to write a small adapter or fall back to the legacy sidebar. There is no single API that covers all three browsers in 2026, and pretending otherwise will burn you at submission time. For a deeper look at the trade-offs, see which browser is the best to build an extension on in 2026.
Common Mistakes That Will Burn You
The first one is assuming the service worker is alive. It is not. Manifest V3 service workers go to sleep after 30 seconds of inactivity, so any state you stash in a top-level variable will vanish. Persist anything important to chrome.storage.local and re-read it on startup.
The second is trying to load remotely hosted code. Manifest V3 does not allow it. If your panel pulls in a script from a CDN, the Chrome Web Store will reject the extension. Bundle everything locally. If you skip this and only catch it during review, you will have a bad time, which is why the broader review checklist in how to pass the Chrome Web Store review on your first try is worth reading before submission.
The third is debugging the panel like it is a popup. The side panel has its own DevTools window, which you open by right-clicking inside the panel and choosing Inspect. Console logs from the panel do not show up in the background service worker DevTools. If you have not set up a proper debugging workflow yet, the complete guide to debugging your Chrome extension covers the inspector layout for every extension surface, including the panel.
Shipping It in a Day with ExtensionFast
The hard part of a side panel extension is not the API. It is the build pipeline, the auth, the payments, and the store listing that surround it. If you try to wire all of that up by hand, you spend a week on plumbing for an extension that should have taken a day. ExtensionFast comes with a pre-configured side panel entry point, a working service worker, Stripe payments, Supabase auth, and a Chrome Web Store ready manifest, which is the same setup the ship your Chrome extension in five days walkthrough is built around. Start with the boilerplate, drop your idea into the panel, and you can have a real extension in users' browsers by the end of the weekend.

