Listing Configured Shortcuts with commands.getAll
Read the extension's current keyboard shortcuts with chrome.commands.getAll: what each command object contains, detecting unassigned shortcuts, rendering a shortcut list in options, and keeping it fresh after users rebind keys.
Table of Contents
The manifest suggests Ctrl+Shift+S for “Save page”, so the onboarding page proudly tells users to press it — but on this user’s machine another extension already claimed that combination, Chrome silently left the command unassigned, and the advertised shortcut does nothing. The manifest’s suggested_key is only a suggestion. What actually happens when a user presses keys depends on what the browser assigned at install and what the user changed later, and the only reliable source is chrome.commands.getAll(). Every piece of UI that mentions a shortcut should read it from there. This guide shows what the API returns and how to build shortcut lists and hints on top of it. It belongs to keyboard shortcuts and commands.
What getAll returns
chrome.commands.getAll() resolves to an array of Command objects, one for each command declared under commands in the manifest — including the special _execute_action (and _execute_side_panel where supported). Each has a name (the manifest key), a description (from the manifest, localised if you used __MSG_ keys), and a shortcut string with the currently assigned combination in the platform’s display format, such as Ctrl+Shift+S or ⇧⌘S. If the command has no shortcut — because none was suggested, the suggestion conflicted at install, or the user cleared it — shortcut is an empty string. There is no event when a user changes a shortcut, so code that displays shortcuts re-reads them when its UI opens.
Step-by-step: shortcut lists from the source of truth
1. Declare commands with good descriptions
1"commands": {
2 "_execute_action": { "suggested_key": { "default": "Alt+Shift+R", "mac": "Alt+Shift+R" } },
3 "save-page": { "suggested_key": { "default": "Ctrl+Shift+S", "mac": "Command+Shift+S" }, "description": "__MSG_cmdSavePage__" },
4 "toggle-reader": { "suggested_key": { "default": "Alt+Shift+F" }, "description": "__MSG_cmdToggleReader__" },
5 "open-notes": { "description": "__MSG_cmdOpenNotes__" }
6}
Execution context: the manifest. Descriptions appear on the browser’s shortcut page and in getAll results, so they are user-facing copy — localise them. open-notes has no suggestion, so it starts unassigned; users can still bind it. Only four commands may carry suggested keys, as explained in the _execute_action command and the four-shortcut limit.
2. Read the current shortcuts
1// shortcuts.js — usable from any extension page
2export async function getShortcuts() {
3 const commands = await chrome.commands.getAll();
4 return commands.map((c) => ({
5 name: c.name,
6 label: c.name === "_execute_action" ? chrome.i18n.getMessage("cmdOpenPopup") : c.description,
7 shortcut: c.shortcut || null,
8 }));
9}
Execution context: extension pages (popup, options, side panel, onboarding tab) or the service worker. _execute_action has no description of its own in some versions, so supply a label. Normalising empty strings to null makes “unassigned” explicit in UI code. Content scripts cannot call chrome.commands; send them the data from the worker if they need it.
3. Render a shortcut list in options
1// options.js
2const list = document.querySelector("#shortcut-list");
3async function renderShortcuts() {
4 const items = await getShortcuts();
5 list.replaceChildren(...items.map(({ label, shortcut }) => {
6 const row = document.createElement("li");
7 const name = Object.assign(document.createElement("span"), { textContent: label });
8 const keys = shortcut ? keyChips(shortcut) : Object.assign(document.createElement("em"), { textContent: chrome.i18n.getMessage("shortcutNotSet") });
9 row.append(name, keys);
10 return row;
11 }));
12}
13renderShortcuts();
14document.addEventListener("visibilitychange", () => { if (!document.hidden) renderShortcuts(); });
Execution context: the options page. Re-rendering when the page becomes visible picks up changes the user made in the browser’s shortcut page in another tab — there is no change event to listen for. Render key combinations as <kbd> chips so they are visually distinct and readable by screen readers.
4. Turn a shortcut string into key chips
1function keyChips(shortcut) {
2 const wrap = document.createElement("span");
3 wrap.className = "keys";
4 const parts = shortcut.includes("+") ? shortcut.split("+") : [...shortcut]; // mac strings like "⇧⌘S" have no "+"
5 for (const p of parts) wrap.append(Object.assign(document.createElement("kbd"), { textContent: p }));
6 wrap.setAttribute("aria-label", shortcut);
7 return wrap;
8}
Execution context: extension pages. On Windows and Linux the shortcut comes back as Ctrl+Shift+S; on macOS Chrome returns symbol form such as ⇧⌘S. Handling both keeps one renderer. See Mac and Windows modifier keys in commands.
5. Detect conflicts at install
1// sw.js
2chrome.runtime.onInstalled.addListener(async ({ reason }) => {
3 if (reason !== chrome.runtime.OnInstalledReason.INSTALL) return;
4 const missing = (await chrome.commands.getAll())
5 .filter((c) => !c.shortcut && SUGGESTED.has(c.name))
6 .map((c) => c.name);
7 if (missing.length) await chrome.storage.local.set({ unassignedShortcuts: missing });
8});
Execution context: the service worker. SUGGESTED is the set of command names that had a suggested_key in your manifest — if one of those is empty right after install, it lost a conflict. Storing the list lets the onboarding page explain what happened instead of advertising keys that do nothing. See resolving keyboard shortcut conflicts.
6. Link to the shortcut page
1document.querySelector("#change-shortcuts").addEventListener("click", () => {
2 const url = navigator.userAgent.includes("Firefox") ? "about:addons" : "chrome://extensions/shortcuts";
3 chrome.tabs.create({ url });
4});
Execution context: an extension page. tabs.create can open chrome://extensions/shortcuts even though links to it from web pages are blocked. Firefox manages shortcuts from the add-ons manager (“Manage Extension Shortcuts” in the gear menu). See letting users rebind extension shortcuts.
7. Never hard-code shortcuts in copy
Every tooltip, onboarding line and help page that mentions a shortcut should render from getAll(), or omit the shortcut when it is unassigned. A static “Press Ctrl+Shift+S” in a locale file will eventually be wrong for some users. See showing shortcut hints in extension UI.
8. Share the list with content scripts
1// sw.js
2chrome.runtime.onMessage.addListener((msg, sender, sendResponse) => {
3 if (msg.type !== "get-shortcuts") return;
4 chrome.commands.getAll().then(sendResponse);
5 return true;
6});
Execution context: the service worker answering content scripts. Injected overlays that want to mention a shortcut (“Press Alt+Shift+F to close reader view”) cannot call chrome.commands themselves. A small message handler gives them the same current data the extension pages use, and returning true keeps the response channel open for the async reply.
Common mistakes
- Trusting
suggested_key. Conflicts leave commands unassigned. - Hard-coded shortcut text. Wrong after rebinding.
- Caching shortcuts forever. There is no change event; re-read when UI opens.
- Calling
chrome.commandsfrom a content script. Not available there. - Not handling Mac symbol strings. Splitting on
+fails.
Cross-browser variation
- Chrome / Edge:
commands.getAllas described; Mac returns symbol strings. - Firefox:
browser.commands.getAllreturns the same shape; Firefox also offerscommands.updateandcommands.resetso an extension can change its own shortcuts, andcommands.onChangedreports changes. - Safari: supports
commandswithgetAll; shortcuts are configured differently — test what the API returns on macOS.
Verification
- Install with a conflicting shortcut already in use and confirm the command reports
""and the onboarding notice appears. - Rebind a command in the browser’s shortcut page, return to options and confirm the list updates.
- Check the rendering on macOS and Windows.
- Confirm no UI shows a shortcut that
getAllreports as unassigned.
FAQ
Is there an event when the user changes a shortcut?
Not in Chrome. Firefox has commands.onChanged. In Chrome, re-read on UI open or visibility change.
Can my extension set its own shortcut programmatically?
Not in Chrome — users change shortcuts. Firefox supports commands.update.
Does getAll require a permission?
No. Any extension with a commands key can call it.
Why is _execute_action in the list without a description?
It is a reserved command; give it a label yourself in the UI.
Can I tell whether a shortcut is global?
Not from getAll. Global scope is chosen by the user on the shortcut page; the API reports only the key combination.
Related
- Showing shortcut hints in extension UI — using the list.
- Letting users rebind extension shortcuts — the shortcut page.
- Resolving keyboard shortcut conflicts — why commands end up unassigned.
- Keyboard shortcuts and commands — the parent topic.