Registering an Omnibox Keyword

Choose and declare an omnibox keyword for an MV3 extension — keyword rules and collisions, the default suggestion, teaching users the keyword exists, and measuring whether they use it.

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

The omnibox keyword is a single line in the manifest and the most consequential naming decision in a search-shaped extension. Too long and nobody types it; too common and it hijacks ordinary searches — a user typing “go to the bank” finds your go keyword swallowing the query. Once shipped it is effectively permanent, because users build muscle memory around it. And a keyword nobody knows about is a feature nobody uses: the browser does nothing to advertise it. This guide is part of omnibox and address bar integration.

What makes a good keyword

Candidate keywords comparedFive candidate keywords for a documentation search extension evaluated on length, collision risk with ordinary searches, memorability and whether they can be typed on every keyboard layout.KeywordLengthCollision riskMemorablego2High — common first wordYesdocs4LowYesmdn3LowIf users know itdocumentation13LowNobody types itñ1LowLayout-dependent
Short, distinctive and not a word people start ordinary searches with — the middle two rows are the ones to aim for.

Step-by-step

1. Declare the keyword

1{
2  "manifest_version": 3,
3  "omnibox": { "keyword": "docs" },
4  "background": { "service_worker": "sw.js", "type": "module" }
5}

Execution context: parsed at install. The keyword must be non-empty; use lower-case ASCII letters for portability. The user activates it by typing the keyword followed by Tab or Space — at which point the address bar shows your extension’s name and routes input to your listeners.

2. Set a default suggestion that explains itself

The first row the user sees after activating the keyword is the default suggestion. It should say what Enter will do, in words that also teach what the keyword is for.

1chrome.omnibox.onInputStarted.addListener(() => {
2  chrome.omnibox.setDefaultSuggestion({
3    description: "Search the extension docs for <match>%s</match> — or type an API name like <dim>alarms.create</dim>",
4  });
5});

Execution context: the service worker, registered at the top level. %s is replaced with the text typed so far. The <match> and <dim> tags are the omnibox’s own markup, covered in providing omnibox suggestions asynchronously.

3. Prepare the index when the keyword activates

onInputStarted fires the moment the user presses Tab after the keyword — before the first character of the query. It is the ideal moment to warm anything the suggestion path needs.

1let indexPromise = null;
2chrome.omnibox.onInputStarted.addListener(() => {
3  indexPromise ??= loadIndex();          // storage read, not a network fetch
4});
5
6async function loadIndex() {
7  const { docIndex } = await chrome.storage.local.get("docIndex");
8  return docIndex ?? [];
9}

Execution context: the service worker. The memoised promise is lost on eviction and recreated on the next activation, which costs one storage read. Keeping the index in storage.local rather than fetching it means the first suggestion does not wait on the network.

4. Teach users the keyword exists

The browser does not announce extension keywords anywhere a user would notice. If you do not tell users, most will never find it.

1// popup.js — a one-line hint, shown until the user has used the keyword once
2const { omniboxUsed } = await chrome.storage.local.get("omniboxUsed");
3if (!omniboxUsed) {
4  hint.textContent = "Tip: type “docs” then Space in the address bar to search without opening this.";
5  hint.hidden = false;
6}
1// sw.js — record first use, which retires the hint
2chrome.omnibox.onInputEntered.addListener(async () => {
3  await chrome.storage.local.set({ omniboxUsed: true });
4});

Execution context: the popup and the service worker. Retiring the hint after the first real use stops it becoming wallpaper. The first-run page is another good place to mention it, as described in showing a first-run setup page after install.

5. Measure whether it earns its place

A small, local counter of omnibox sessions against popup opens tells you whether the keyword is actually used — without any telemetry leaving the device.

1chrome.omnibox.onInputEntered.addListener(async () => {
2  const { usage = { omnibox: 0 } } = await chrome.storage.local.get("usage");
3  usage.omnibox += 1;
4  await chrome.storage.local.set({ usage });
5});

Execution context: the service worker. Surfacing these counts in a diagnostics view — or in an opt-in usage report — tells you whether to invest in the omnibox experience or in the popup. The privacy constraints on reporting are in reporting errors without breaking your privacy policy.

From keyword to first suggestionThe user types the keyword and Tab, onInputStarted warms the index and sets the default suggestion, and the first keystroke's onInputChanged queries an index that is already loaded."docs" + Tabkeyword modeonInputStartedworker may be coldloadIndex()storage.localby the time the first character arrivessetDefaultSuggestionexplains EnteronInputChanged("a")first keystrokeIndex already warmlookup only
Warming on activation means the first keystroke pays for a lookup, not for a storage read.

Choosing a keyword that survives contact with users

Three failure modes are common enough to plan against.

Collisions with ordinary typing. Some users press Space immediately after typing a word, and if that word is your keyword, their search is intercepted. Short common English words — go, to, my, new, the — collide constantly. A quick test: would a user ever start an ordinary search with this word followed by a space? If yes, pick another.

Collisions with other extensions and search engines. Chrome resolves keyword conflicts in favour of whichever was registered first and does not warn the second. Users who also run a popular extension with the same keyword will simply find yours never activates. Distinctive keywords avoid most of this; your product name, shortened, is usually the best candidate.

Localisation. The keyword cannot be localised through _locales in most engines, and non-ASCII keywords are awkward on many keyboard layouts. Choose something typable everywhere — Latin letters, no accents — and accept that it is the same across languages.

The keyword is also very hard to change once shipped. Users who learned it will find the old one silently stops working. If you must change it, keep the old behaviour discoverable — a popup notice for a release or two — rather than breaking the habit without explanation.

Is this keyword safe to ship?A decision tree checking a candidate keyword against length, ordinary-search collisions, likely clashes with other extensions and keyboard-layout portability.Would anyone start an ordinary search with it?Yes (go, my, new)Rejectintercepts real searchesPick something distinctiveproduct-derivedNo, but it is longShorten≤ 5 charactersRecheck collisionswith the short formNo, short and ASCIIShip itdocs, mdn, jiraTeach it in the popupuntil first use
Fail any branch and choose again — changing a shipped keyword costs every user their habit.

Cross-browser variation

  • Chrome / Edge: omnibox.keyword in the manifest; conflicts resolve to the first registrant silently. onInputStarted, onInputChanged, onInputEntered, onInputCancelled and setDefaultSuggestion are all available.
  • Firefox: browser.omnibox with the same manifest key and events. Firefox shows the extension’s icon in the address bar when the keyword is active.
  • Safari: no omnibox API. A Safari build should drop the omnibox key and point users at a keyboard shortcut for the popup instead, as covered in omnibox support and alternatives across browsers.
  • All three: the keyword is global to the browser profile. Test it alongside the extensions your users are likely to run.

Verification

  1. Type the keyword and Space in the address bar; the extension’s name should appear and the default suggestion should show.
  2. Confirm the listeners are registered and the default is set:
1chrome.omnibox.setDefaultSuggestion({ description: "probe <match>%s</match>" });
2// then type the keyword + Space + "x" and confirm "probe x" appears

Execution context: the service worker console. If nothing appears, check for a keyword conflict with another extension or a search engine shortcut.

  1. Type an ordinary search that starts with a common word and confirm your keyword does not intercept it.
  2. Open the popup before and after first use and confirm the hint disappears.

FAQ

Can users change my keyword?

Not in most Chrome versions — extension keywords are fixed by the manifest. That is part of why choosing well matters.

Can one extension have several keywords?

No. Use prefixes inside the query instead — docs api:alarms, docs guide:popup — and parse them in onInputChanged.

Does the keyword need to match my extension’s name?

No, but a keyword derived from the name is easier to remember and less likely to collide.

What happens if a search engine uses the same keyword?

User-defined search-engine shortcuts and extension keywords share one namespace in the address bar. Whichever claims it first wins, and the other simply never triggers. There is no API to detect this, so distinctiveness is the only defence.

Other UI/UX Patterns & Interactive Components Resources