gh in the address bar, enter a query, and land on GitHub search results. Along the way you will wire a manifest.json and handle input in a background service worker. You will also practice the dev loop (create → dev → build) that every project follows.
What you will build
The plan
Make GitHub search as fast as a native browser shortcut. The extension reserves the keywordgh; after you type gh and a query, it opens GitHub search results.
Step 1: create the extension
Use the Extension.jscreate command to scaffold an extension named github-search.
The default template is a working sidebar starter, so the scaffold already contains
src/manifest.json and src/background.ts. The next two steps replace those two files.
Everything you write in this tutorial goes in src/, because Extension.js prefers
src/manifest.json when it is present.
Using Yarn? The
yarn dlx command requires Yarn 2 or later. Yarn 1 does
not include dlx and fails with a “Command not found” error. On Yarn 1, use
the npm tab (npx) instead.Using Deno? Deno caches npm packages aggressively, including the After
@latest
tag, so npm:extension@latest can keep resolving to an older cached release.
If a scaffold or dev build fails with an error that a newer version already
fixed (for example manifest.json references files that were not emitted to disk), refresh the cache, or pin an exact release in place of @latest:deno install, confirm the resolved version (extension should match
the latest release) before running deno task dev.What create generates
create scaffolds a complete project and initializes a git repository. The default template produces this tree:
tsconfig.json and extension-env.d.ts for typed extension APIs.
Two files anchor the layout:
package.jsonmarks the project root. Special folders (pages/,scripts/,public/) and thedist/output resolve from that directory, never fromsrc/.manifest.jsonmarks the extension source. It can sit at the root or insrc/. When both exist,src/manifest.jsonwins.
Step 2: create the manifest file
Every extension starts with a manifest file. It defines metadata, permissions, and runtime files. Based on the plan above, set thegh shortcut and add a service worker for user events.
Open the src/manifest.json the scaffold created and replace its contents with:
omnibox.keyword: When you typegh, the browser fires an event.background.service_worker: Listens to the event you triggered.
Step 3: create the background service worker
In browser extensions, the background service worker (a script that runs independently of any visible page) handles browser events. For this example, add a script that listens to Omnibox input and routes the query to GitHub search. Createsrc/service_worker.js, and delete the src/background.ts the scaffold shipped so nothing else claims the background entry:
Step 4: load your extension
Move into the project and install its dependencies.create scaffolds the project but
does not install for you, and the dev script runs the local extension binary:
package.json file now looks like this, with extension pinned to the release you scaffolded with:
First run.
extension dev targets chromium by default and runs your
extension in a version-pinned Chrome for Testing with an isolated profile, so
it never touches the browser you use every day. When that browser is not on
the machine yet, the first run asks to download it once and continues into
the dev session as soon as it lands.Answer n and nothing is downloaded. Run npx extension install chromium
whenever you want it, reach a browser you already have with --browser=edge
or --browser=brave, pin any binary with --chromium-binary <path>, or
start the dev server alone with --no-browser. A non-interactive shell, CI
included, is never asked: it prints the install command and stops. See
why Extension.js downloads a browser
and skip the managed download.github-search as an unpacked extension, and prints a ready banner in your terminal. The Chrome address bar now recognizes gh as a keyword.
Type gh followed by a space, enter extension.js, press Enter. A new tab opens to https://github.com/search?q=extension.js&type=issues.
You now have a working browser extension that searches on GitHub.
Step 5: make it better
Improve the search experience by adding suggestions directly in the address bar with an Omnibox input listener. Updateservice_worker.js to fetch GitHub suggestions and display them while typing.
service_worker.js
Next steps
- Create another extension with templates.
- Add automated checks with Playwright E2E.
- Review Troubleshooting, Security checklist, and Performance playbook as your extension grows.

