Omnibox & Address Bar Integration

Add a keyword to the browser's address bar with chrome.omnibox — suggestions as the user types, handling Enter and Alt-Enter, safe navigation, and what Firefox and Safari offer instead.

The address bar is the one piece of browser UI every user touches dozens of times a day, and chrome.omnibox lets an extension claim a small corner of it: the user types a keyword, presses Tab or Space, and every keystroke after that goes to your extension instead of to search. For search-shaped extensions — a documentation lookup, an internal link shortener, a ticket finder, a bookmark search — it is the fastest interface available, faster than any popup, because the user never takes their hands off the keyboard. This section sits inside UI/UX Patterns & Interactive Components.

The API is small: one manifest key, four events, and a suggestion format with a sliver of markup. The difficulty is in the details — suggestions must arrive quickly enough to keep up with typing from a service worker that may be asleep, every suggestion’s text is rendered with the browser’s own XML-like markup rules, and what happens on Enter depends on a disposition you must honour. Two of the three engines support it; the third needs a different design.

An omnibox session from keyword to navigationThe user types the keyword and a query; the worker receives onInputStarted and onInputChanged for each keystroke, suggests matches, and on Enter receives onInputEntered with a disposition and navigates.UserAddress barService workerYour index"docs" + Tabkeyword modeonInputStartedtypes "alarms"onInputChanged(text, suggest)per keystrokequerysuggest([…])EnteronInputEntered(text, disposition)
Every keystroke is an event to the worker — the suggestion path has to be fast enough to keep pace with typing.

Prerequisites checklist

  • A short, memorable keyword that does not collide with a common word users type at the start of searches.
  • A data source that can answer prefix queries in a few milliseconds — an in-memory index, IndexedDB, or a fast endpoint.
  • The listeners for onInputChanged and onInputEntered registered at the top level of the service worker.
  • A plan for escaping suggestion descriptions, which use a restricted markup rather than plain text.
  • A decision about Firefox (supported) and Safari (not supported) — including what Safari users get instead.
  • A fallback URL for text that matches nothing, so Enter always does something useful.

Manifest registration

1{
2  "manifest_version": 3,
3  "omnibox": { "keyword": "docs" },     // the user types "docs" then Tab or Space
4  "background": { "service_worker": "sw.js", "type": "module" },
5  "permissions": ["storage"]             // no special permission needed for omnibox
6}

Execution context: parsed at install. The keyword is fixed in the manifest; the user can override it for search engines in browser settings but not for extensions in most versions. Keep it short and lower-case. The omnibox API itself requires no permission and produces no install warning.

1. Answer suggestions quickly

onInputChanged fires on every keystroke with the current text and a suggest callback. Slow suggestions arrive after the user has typed past them, and the dropdown flickers with stale results.

1chrome.omnibox.onInputChanged.addListener(async (text, suggest) => {
2  const q = text.trim().toLowerCase();
3  if (q.length < 2) return suggest([]);
4  const hits = await searchIndex(q, 5);
5  suggest(hits.map((h) => ({ content: h.url, description: `<match>${escapeXml(h.title)}</match> <dim>${escapeXml(h.section)}</dim>` })));
6});

Execution context: the service worker, registered at the top level. The first keystroke may wake a cold worker, so keep the index loadable in a few milliseconds from storage. The full treatment — caching, cancellation, the markup rules — is in providing omnibox suggestions asynchronously.

2. Handle Enter with the disposition

1chrome.omnibox.onInputEntered.addListener(async (text, disposition) => {
2  const url = resolveToUrl(text);
3  if (disposition === "currentTab") return chrome.tabs.update({ url });
4  return chrome.tabs.create({ url, active: disposition === "newForegroundTab" });
5});

Execution context: the service worker. disposition reflects how the user pressed Enter — plain Enter, Alt-Enter for a new tab, or a middle-click on a suggestion — and ignoring it breaks muscle memory the user brings from every other address-bar interaction. Resolving text safely is covered in handling omnibox input entered navigation.

3. Set the default suggestion

The first row in the dropdown — the one Enter uses when the user has not arrowed to a suggestion — is set separately and should describe what Enter will do.

1chrome.omnibox.onInputStarted.addListener(() => {
2  chrome.omnibox.setDefaultSuggestion({ description: "Search the docs for <match>%s</match>" });
3});

Execution context: the service worker. %s is replaced with the typed text. Setting it on onInputStarted resets it at the start of each session, which matters if you change it dynamically while the user types.

The four omnibox eventsonInputStarted, onInputChanged, onInputEntered and onInputCancelled compared on when they fire, what they receive and what to do in each.EventFires whenReceivesDoonInputStartedKeyword mode enteredNothingWarm the index, set defau…onInputChangedEvery keystroketext, suggest()Query fast, suggest ≤ 5onInputEnteredEnter or clicktext, dispositionNavigate per dispositiononInputCancelledEsc or focus lostNothingDrop pending work
onInputChanged is the hot path — it fires on every keystroke and must answer in milliseconds.

4. Is the omnibox right for your extension?

The omnibox is a narrow, powerful surface. It shines when the extension’s core job is finding something by name — a documentation page, an internal ticket, a saved link, a team member, a product in a catalogue — and the user already knows roughly what they are looking for. In those cases a keyword in the address bar is faster than any other interface, because the user never takes their hands off the keyboard and no UI has to open before they can start typing.

It fits poorly when the result is not a place to go. Actions that need confirmation, options that need explanation, or results that need rich previews — images, tables, multiple fields — are cramped into a single line of text and are better served by a popup or side panel. It also fits poorly when users do not think of the extension as a search tool, because they will never discover or remember the keyword. A useful test is whether you can describe the feature as “type the keyword, then the name of the thing you want”. If you can, the omnibox is probably worth building; if the description needs more words, another surface is likely better.

5. Building a fast index

Suggestions are requested on every keystroke, from a service worker that may have just woken up, and they must arrive before the user types the next character. That rules out a network request per keystroke and makes the index the heart of the feature.

Build the index ahead of time, on a schedule driven by an alarm, and store it in chrome.storage.local — or IndexedDB for large corpora. On onInputStarted, which fires as soon as the user activates the keyword, load it into memory so the first keystroke pays only for a lookup. Keep each entry small: a title, a few aliases, the URL or id, a section label and a popularity score. Aliases do much of the work, because people type the abbreviations they use rather than the titles you chose.

1chrome.omnibox.onInputStarted.addListener(() => { indexPromise ??= loadIndex(); });

Execution context: the service worker. The memoised promise survives for the worker’s lifetime and is rebuilt on the next activation after an eviction, which costs one storage read rather than a network fetch. The full suggestion path, including staleness checks, is in providing omnibox suggestions asynchronously.

6. Ranking that matches what people type

Ranking decides whether the keyword feels clever or literal. Omnibox users type the start of what they want, so prefix matches on the title should dominate, followed by matches at the start of later words and in aliases, with matches elsewhere in the text last. Every typed term should be required to match somewhere, so adding words narrows results rather than widening them.

Personal popularity improves ranking noticeably within a few days of use. Each time the user chooses a suggestion, increment a local counter for that entry and add a small popularity term to its score; decay the counters over time so a page needed heavily for one week does not dominate for months. Because the counts never leave the device, this personalisation has no privacy cost.

7. Safe handling of Enter

When the user presses Enter on the default row, the extension receives whatever they typed — arbitrary text that is about to become a navigation. Treat it as untrusted input. Suggestions the extension generated should carry a recognisable content value, such as an id prefixed with doc:, that resolves against the current index; anything else is typed text, and should become a search on the extension’s own site with the query encoded, never a raw navigation to whatever was typed. Honour the disposition — current tab, new foreground tab or new background tab — because users bring those habits from every other address-bar interaction. The details are in handling omnibox input entered navigation.

8. Teaching users the keyword

The browser does not advertise extension keywords anywhere a user would notice, so a keyword nobody is told about is a feature nobody uses. Mention it in onboarding, in the store listing and — until the user has used it once — as a single line in the popup. Retire the hint after the first successful use so it does not become wallpaper. Choose the keyword itself with the same care: short, lower-case, typable on every keyboard layout, and not a word people start ordinary searches with, because an intercepted search is the fastest way to get the extension uninstalled. See registering an omnibox keyword.

9. Supporting every browser

Chrome renders suggestion descriptions with <match>, <dim> and <url> markup; Firefox shows the same descriptions as plain text; Safari has no omnibox at all. A portable extension keeps search in one shared core — index, ranking and resolution — with two thin front ends: the omnibox handlers where the API exists, formatting descriptions per engine, and a keyboard-reachable popup search that serves every engine and is the only search interface on Safari. Because both front ends call the same functions, results are identical wherever the user searches. The pattern is in omnibox support and alternatives across browsers.

10. Measuring whether the keyword is used

Omnibox support is cheap to build and easy to overestimate. A local counter of omnibox sessions, compared with popup searches, shows whether users actually adopt the keyword. If they rarely do, invest in the popup search instead; if they do, invest in ranking and aliases, which improve the experience most for the users who have made the keyword a habit.

11. Beyond navigation: keyword commands

Search is the omnibox’s natural use, but a keyword can also accept short commands: notes add buy milk, tabs close duplicates, timer 25. The input is parsed in onInputChanged, where the default suggestion can describe exactly what Enter will do — “Add a note: buy milk” — and executed in onInputEntered. Commands that change something should confirm through a visible signal afterwards, such as a brief badge tick, because the address bar reverts to the current page’s URL the moment Enter is pressed and gives no feedback of its own.

Keep the command vocabulary small and discoverable. Suggest the available commands when the user has typed only the keyword, show the expected syntax in the default suggestion as they type, and prefer commands that are safe to repeat. Anything destructive — deleting, sending, publishing — is better offered as a suggestion that opens a confirmation page than executed straight from the address bar.

12. Testing the omnibox

The address bar is browser chrome, so end-to-end tools cannot type into it. That is less of a limitation than it sounds, because nearly all of the logic lives in functions the omnibox listeners call. Test the index, ranking and input resolution thoroughly in Node, including hostile input such as javascript: URLs and descriptions containing ampersands. Then add a thin end-to-end test that calls the registered listeners directly from the service worker with a fake suggest callback, confirming the wiring returns escaped suggestions and that Enter resolves to the expected URL. A short manual check on each supported browser before release covers the one thing automation cannot: that the keyword activates and the dropdown looks right.

13. Performance and privacy

Omnibox handlers run on every keystroke while the keyword is active, so their cost is felt directly as typing lag. Keep the in-memory index small, cap suggestions at about six, and discard results for text the user has already typed past. On the privacy side, the omnibox receives everything the user types after the keyword — often search terms that reveal a great deal. Process it locally, never send it to a server by default, and if a remote search is part of the feature, say so plainly in the listing and debounce it so partial keystrokes are not transmitted one by one.

The same care applies to anything the extension learns from omnibox use. Popularity counts for ranking should be stored as counts per entry id, never as the typed queries themselves, and cleared along with other extension data when the user asks. A keyword that users trust with their queries is one they will keep using; one that feels like it is watching what they type will be disabled, however useful its results are.

Document this in the extension itself: a line on the options page stating that omnibox input is processed on the device, next to a control that clears the learned rankings, is enough to make the promise visible and verifiable.

MV3 constraints box

  • Cold starts are on the typing path. The first keystroke may wake the worker; the index must be loadable in milliseconds, not fetched from the network.
  • Suggestion descriptions are XML. Only <url>, <match> and <dim> are recognised, and &, < and > must be escaped — an unescaped ampersand makes the whole suggestion list fail silently.
  • No arbitrary UI. Suggestions are text rows. Icons, images and custom layout are not available.
  • One keyword per extension. Multiple keywords need multiple extensions, or a prefix convention inside the query.
  • Listeners must be top-level. A keystroke that wakes the worker is dispatched at the end of the first synchronous pass.

Cross-browser notes

CapabilityChrome / EdgeFirefoxSafari
omnibox keywordYesYes (browser.omnibox)Not available
Suggestion markup<url>, <match>, <dim>Plain text descriptions
onInputEntered dispositionYesYes
setDefaultSuggestionYesYes
AlternativePopup search, keyboard command

What this section covers

The guides go through the flow in order: registering an omnibox keyword covers choosing and declaring the keyword; providing omnibox suggestions asynchronously covers the hot path; handling omnibox input entered navigation covers Enter and safe URL resolution; and omnibox support and alternatives across browsers covers Firefox’s differences and what to build for Safari.

Time to reach a known page by interfaceMedian time for a user to open a known documentation page via the omnibox keyword, a popup search, a bookmark folder and a web search.Omnibox keyword2.1 secondsPopup search3.8 secondsBookmark folder5.2 secondsWeb search7.4 seconds
The omnibox is fastest because the user's hands never leave the keyboard and no surface has to open first.