The _execute_action Command and the Four-Shortcut Limit

How MV3's reserved _execute_action command opens the popup, why only four commands can have suggested keys, and how to decide which actions earn a default shortcut.

Published September 18, 2026 Updated September 18, 2026 8 min read
Table of Contents

The commands key looks like a free-form map of actions to keys, and two rules make it anything but. First, _execute_action is reserved: it does not fire commands.onCommand, it opens your popup (or fires action.onClicked if there is no popup). Second, only four commands may declare a suggested_key — define more and the extra suggestions are silently dropped, leaving commands with no shortcut at all until the user assigns one. This guide is part of keyboard shortcuts and commands.

How reserved and ordinary commands differ

Reserved and ordinary commands_execute_action, _execute_side_panel and an ordinary named command compared on what they trigger, whether onCommand fires, whether they count toward the suggested-key limit, and user-gesture status.CommandTriggersonCommand firesCounts toward 4_execute_actionOpens the popupNoYes_execute_side_panelOpens the side panelNoYesNamed commandYour handlerYesYesNamed, no suggested_keyYour handler, once assign…YesNo
_execute_action counts toward the four — which leaves three suggestions for everything else.

Step-by-step

1. Give the popup a shortcut with _execute_action

1{
2  "action": { "default_popup": "popup.html" },
3  "commands": {
4    "_execute_action": {
5      "suggested_key": { "default": "Alt+Shift+R", "mac": "Alt+Shift+R" },
6      "description": "__MSG_cmdOpenPopup__"
7    }
8  }
9}

Execution context: parsed at install. There is no listener to write — pressing the shortcut opens the popup exactly as a click would, including granting activeTab. This is also the keyboard entry point screen-reader users rely on, as described in testing extension UI with a screen reader.

2. Handle ordinary commands in the worker

1{
2  "commands": {
3    "save-page":   { "suggested_key": { "default": "Alt+Shift+S" }, "description": "__MSG_cmdSavePage__" },
4    "toggle-view": { "suggested_key": { "default": "Alt+Shift+V" }, "description": "__MSG_cmdToggleView__" }
5  }
6}
1chrome.commands.onCommand.addListener(async (command, tab) => {
2  if (command === "save-page") return savePage(tab);
3  if (command === "toggle-view") return toggleView(tab);
4});

Execution context: the service worker, registered at the top level so a shortcut that wakes a sleeping worker is not lost. The tab argument is the active tab when the shortcut was pressed; a command counts as a user gesture and grants activeTab for it.

3. Stay within four suggested keys

_execute_action plus three named commands is the maximum that ship with a default. Anything beyond that must be declared without a suggested_key.

 1{
 2  "commands": {
 3    "_execute_action": { "suggested_key": { "default": "Alt+Shift+R" }, "description": "…" },
 4    "save-page":       { "suggested_key": { "default": "Alt+Shift+S" }, "description": "…" },
 5    "toggle-view":     { "suggested_key": { "default": "Alt+Shift+V" }, "description": "…" },
 6    "next-article":    { "suggested_key": { "default": "Alt+Shift+N" }, "description": "…" },
 7    "prev-article":    { "description": "__MSG_cmdPrevArticle__" },
 8    "archive-article": { "description": "__MSG_cmdArchive__" }
 9  }
10}

Execution context: parsed at install. The last two commands appear on the shortcuts page with no key assigned; the user can bind them there. A fifth suggested_key would not error — it would just be ignored, which is worse, because nothing tells you.

4. Tell users where to assign the rest

Unassigned commands are invisible unless you point at them. Link to the shortcuts page from your options page.

1document.querySelector("#shortcuts").addEventListener("click", () => {
2  chrome.tabs.create({ url: "chrome://extensions/shortcuts" });
3});

Execution context: the options page. Extension pages may open chrome://extensions/shortcuts with tabs.create, even though they cannot script it. The Firefox equivalent and the rebinding flow are in letting users rebind extension shortcuts.

5. Show the effective shortcut in your UI

What you suggested and what the user has are different things: the browser may have refused the suggestion because of a conflict, or the user may have changed it.

1const commands = await chrome.commands.getAll();
2const open = commands.find((c) => c.name === "_execute_action");
3hint.textContent = open?.shortcut
4  ? `Tip: press ${open.shortcut} to open Reader.`
5  : "Tip: assign a shortcut to open Reader from the keyboard.";

Execution context: any extension page. shortcut is an empty string when no key is bound — including when your suggestion collided with a browser shortcut and was not applied. Displaying the real value avoids telling users to press a key that does nothing.

From manifest suggestion to a working keyUp to four suggested keys are applied at install unless they conflict; unapplied or unsuggested commands wait for the user to assign them on the shortcuts page.suggested_key (≤ 4)in the manifestNo conflictapplied at installKey worksshortcut populatedotherwiseConflict or 5th+ commandnot appliedshortcut: ""getAll() shows itUser assigns on the shortcuts pageyour UI must link there
Only the top path is automatic — everything else depends on the user finding the shortcuts page.

Choosing which actions earn a default key

Three suggested keys is a small budget, and the choice shapes how the extension feels. The candidates worth a default share three properties: they are used often, they are used while the user’s hands are on the keyboard, and they act on the current page without further input.

“Save this page” qualifies. “Open settings” does not — it is rare, and the popup is one click away. “Next article” qualifies for a reader extension where it is pressed dozens of times a session. “Export all data” does not, and binding it to a key invites accidents.

Key choice is the other half. Ctrl+Shift+<letter> and Alt+Shift+<letter> are the conventional extension spaces; plain Ctrl+<letter> collides with browser and page shortcuts almost everywhere, and the browser will refuse many of them. Avoid letters that common web applications use with the same modifiers — Alt+Shift+ combinations collide less often with site shortcuts than Ctrl+Shift+ ones do. The conflict problem is explored in resolving keyboard shortcut conflicts.

Does this action deserve a default shortcut?A decision tree based on frequency of use, whether the action targets the current page, and whether triggering it accidentally is harmful.Is it used several times per session?NoNo defaultdeclare without suggested_keyAssignable laterlink to the shortcuts pageYes, on the current pageCandidatecheck the next questionHarmless if hit by accident?yes → default keyYes, but destructiveNo defaultdelete, export, sign outKeep it in the UIwith confirmation
Anything that fails the last question should never have a default, however often it is used.

Global commands and the popup’s own keys

Two more distinctions round out the model.

A command may declare "global": true, which makes the shortcut work even when the browser is not focused — a media-control extension might want play/pause from anywhere on the desktop. Global shortcuts are limited to Ctrl+Shift+[0–9] on most platforms, are not available on ChromeOS, and are a much larger imposition on the user’s system than a browser-scoped key. They are the exception, and they share the same four-suggestion budget. The rules are set out in implementing global keyboard shortcuts safely.

Keys inside the popup are a separate matter entirely. Once the popup is open, it is an ordinary document and ordinary keydown handlers work — j/k to move through a list, Enter to open, / to focus search. These do not go through commands, do not count toward any limit and cannot conflict with browser shortcuts, because the popup has focus.

1// popup.js — in-popup keys, no manifest entry needed
2document.addEventListener("keydown", (e) => {
3  if (e.target instanceof HTMLInputElement) return;   // do not hijack typing
4  if (e.key === "j") moveSelection(+1);
5  if (e.key === "k") moveSelection(-1);
6  if (e.key === "/") { e.preventDefault(); searchInput.focus(); }
7});

Execution context: the popup document. The input guard is essential: without it, typing a j into the search box moves the selection instead. A good pattern is one manifest command to open the popup, and single-key shortcuts once inside — the global budget stays small and the in-popup vocabulary can be as rich as the UI needs.

Cross-browser variation

  • Chrome / Edge: _execute_action opens the popup; _execute_side_panel opens the side panel (Chrome 116+). Four suggested keys maximum. Users manage shortcuts at chrome://extensions/shortcuts.
  • Firefox: supports _execute_action (and the older _execute_browser_action in MV2), _execute_sidebar_action, and the same four-suggestion guidance. browser.commands.update lets the extension change a binding itself — Chrome has no equivalent.
  • Safari: supports commands with suggested keys; the popup-opening reserved command works. Shortcut management is less discoverable for users, so the in-UI hint matters more.
  • All three: mac in suggested_key lets you use Command on macOS; Ctrl in the manifest maps to Command on macOS automatically unless you specify MacCtrl.

Verification

  1. After a fresh install, list the effective bindings:
1(await chrome.commands.getAll()).map((c) => [c.name, c.shortcut || "(none)"]);
2// [["_execute_action","⌥⇧R"], ["save-page","⌥⇧S"], ["toggle-view","⌥⇧V"], ["next-article","⌥⇧N"],
3//  ["prev-article","(none)"], ["archive-article","(none)"]]

Execution context: any extension context. Any of the first four showing (none) means the suggestion conflicted and was not applied.

  1. Press the _execute_action shortcut and confirm the popup opens with no onCommand log.
  2. Press each named shortcut on a page and confirm the handler runs with a populated tab.url (the activeTab grant).
  3. Open the options page and confirm the hint shows the real binding.

FAQ

Can I have more than four shortcuts?

You can declare any number of commands; only four may suggest a key. The rest are assignable by the user.

Does _execute_action work without a popup?

Yes — it then fires action.onClicked, exactly as a click on the toolbar button would.

Why did my suggested key not apply?

Most often a conflict with a browser shortcut or another extension that claimed it first. The browser silently leaves your command unbound.

Other UI/UX Patterns & Interactive Components Resources