Oleksii Khoroshun, k-o.pro homeAudits and advisory

How to Build a Chrome Extension: Steps, Permissions and Publishing

Build a Google Chrome extension in three files, then debug it, choose permissions and publish it to the Chrome Web Store. Tested code and a real case.

Oleksii KhoroshunSEO at SE Ranking7 min read1,612 words

A Chrome extension needs one required file, manifest.json, plus whatever HTML, CSS and JavaScript your feature uses. Writing that takes minutes. The parts that take longer are choosing permissions, debugging code that runs inside other people's pages, and getting through the Chrome Web Store review.

This guide builds a small Manifest V3 extension from scratch, then covers debugging, permissions and publishing. The case behind it is Keep Optimized, a free on-page SEO checker with over 100 users that I built with Claude Code. Technical details were checked against Chrome for Developers documentation on Oct 23, 2026.

What a Chrome extension is made of

According to Chrome's getting-started guide, the manifest is the only required file with a fixed name. It records the extension's metadata, declares its permissions and says which files run where. The pieces you can declare in it:

  • A popup. The window that opens when someone clicks your toolbar icon. It is a normal HTML page.
  • Content scripts. JavaScript and CSS that run inside web pages and can read and change the page's DOM.
  • A service worker. Background logic that handles browser events.

A small extension needs only the manifest and a popup.

Manifest V3 is the current extensions platform. It replaces long-lived background pages with service workers that run only when needed, and it removes remotely hosted code: an extension can run only the JavaScript included in its package. That rule matters if an AI agent writes your code, because a script loaded from a CDN at runtime is not allowed.

Build a working extension in three files

The example reads the title, H1 and meta description of the page you are on. It is the smallest useful version of what an on-page SEO checker does.

1. Create a folder called page-snapshot.

2. Add manifest.json:

{
  "manifest_version": 3,
  "name": "Page Snapshot",
  "version": "1.0",
  "description": "Shows the title, H1 and meta description of the current page.",
  "permissions": ["activeTab", "scripting"],
  "action": {
    "default_popup": "popup.html"
  }
}

action.default_popup ties the toolbar icon to your popup. activeTab gives the extension temporary access to the current tab after a user gesture such as clicking the icon. scripting is needed to inject code into that tab with chrome.scripting.executeScript().

3. Add popup.html:

<!doctype html>
<html>
  <head>
    <meta charset="utf-8">
    <style>
      body { width: 320px; font: 14px/1.4 system-ui, sans-serif; margin: 12px; }
      dt { font-weight: 600; margin-top: 8px; }
      dd { margin: 2px 0 0; word-break: break-word; }
    </style>
  </head>
  <body>
    <dl>
      <dt>Title</dt><dd id="title">…</dd>
      <dt>H1</dt><dd id="h1">…</dd>
      <dt>Meta description</dt><dd id="description">…</dd>
    </dl>
    <script src="popup.js"></script>
  </body>
</html>

4. Add popup.js:

function readPage() {
  const h1 = document.querySelector("h1");
  const meta = document.querySelector('meta[name="description"]');
  return {
    title: document.title,
    h1: h1 ? h1.textContent.trim() : null,
    description: meta ? meta.content : null
  };
}

async function main() {
  const [tab] = await chrome.tabs.query({ active: true, currentWindow: true });
  const [{ result }] = await chrome.scripting.executeScript({
    target: { tabId: tab.id },
    func: readPage
  });
  for (const key of ["title", "h1", "description"]) {
    document.getElementById(key).textContent = result[key] ?? "(missing)";
  }
}

main();

readPage runs inside the page, and its return value comes back as result. The popup writes it with textContent, not innerHTML, because the strings come from pages you don't control.

5. Open chrome://extensions, turn on Developer mode, click Load unpacked and select the page-snapshot folder. Pin the extension from the puzzle-piece menu, open any regular web page and click the icon.

After you edit files, click the refresh icon on the extension's card in chrome://extensions. According to Chrome's Hello World tutorial, the manifest and the service worker need that reload, content scripts need it plus a refresh of the host page, and the popup and options pages don't.

Choose permissions before you write features

The permission list decides what the store reviewer looks at and what users see at install time.

activeTab is the narrow option. Chrome's documentation describes it as an alternative to <all_urls>: it shows no warning at install, access starts only after an explicit user gesture, and it ends when the user navigates to a different site or closes the tab. Broad host permissions give the extension persistent access to every website instead.

You need more than activeTab when the extension must act without a click, for example a content script that runs on every page matching a pattern. That script declares matches in the manifest, and the match patterns become part of what you ask users to trust.

Pick the narrowest set that makes the feature work. Adding a permission later is a one-line change, but a broad one slows down review, as the publishing section below shows.

Debug each part where it runs

Extensions fail in places a normal web page doesn't, and each part logs to a different DevTools window. This follows Chrome's debugging tutorial:

  • Popup. Inspect the popup itself to open its DevTools.
  • Service worker. Open DevTools from the blue link next to Inspect views on the extension's card. Inspecting the worker keeps it active, so close DevTools when you test how the extension behaves after the worker is terminated.
  • Content scripts. Their errors show in the DevTools console of the page the script runs on. In that console, the context dropdown next to top lets you pick your extension and run code in the content script's context.
  • The Errors button on chrome://extensions. It records runtime errors and console warnings and errors, but content script errors often won't appear there.

Building it with an AI coding agent

I built Keep Optimized with Claude Code. It handled the bulk of the work well on the first pass: scaffolding the project, wiring up the interface and writing the first version of the logic from a clear description.

It was weaker on anything that only shows up once the code runs in a browser: a quirk on one type of page, state that doesn't update when it should, timing between different parts of the extension. Those needed the console and manual fixes, not another prompt. AI raised the floor on how quickly I got to a working version. It didn't remove the debugging.

Two habits follow from the platform rules above. State the constraints before the agent writes code: Manifest V3, no remotely hosted code, and the permissions you are willing to request. Then read the manifest first. It is short, and it decides what review will examine.

The case: Keep Optimized

Keep Optimized checks a page's title, headings, images, links and technical setup, then shows a score with specific fixes. Click a fix and it highlights the exact spot on the page. It also compares what the browser renders with what the server sends back, which catches SEO problems that appear only after JavaScript runs.

keep-optimized-on-page-seo-checker

The decision that shaped everything else was having no backend. Nothing about your audit goes to a server I run: the extension reads the page in your browser and shows the results there. That made it simpler to build and easier for people to trust, and it is a big part of why the extension is under a quarter megabyte.

It does one job, and that scope is what made it worth finishing: you click it, and it audits the SEO of the page you are looking at. There is no dashboard and no account.

how to build chrome extension

Publish it to the Chrome Web Store

  1. Register a developer account. Registration requires a one-time fee of US$5 (the registration page confirms it is one-time). After that, submitting extensions is free. You also provide a developer email, and it can't be changed after the account is created.
  2. Zip the extension folder and, in the Chrome Developer Dashboard, click Add new item and upload the ZIP.
  3. Fill in the item details, per Chrome's publishing guide: Store Listing, Privacy (the extension's single purpose and how it handles user data), Distribution (paid or free, countries, who can see it) and test instructions if a reviewer needs them.
  4. Click Submit for Review. If you want to control the release date, uncheck the publish-after-review box or use Defer publish later. You then have 30 days after approval to publish before the submission reverts to a draft.

A new publisher can have up to two published extensions, and themes don't count toward that limit. You can request an increase from the dashboard.

How long review takes

Chrome's review documentation says that for most extensions review is completed within a few days but can take up to a few weeks. If a review is pending for more than three weeks, it tells you to contact developer support. The same page lists what prompts a closer look: new developers, new extensions, dangerous permission requests and significant code changes. Broad host permissions such as <all_urls> and other powerful capabilities take longer to review. Minified code is allowed. Obfuscated code is not.

Keep Optimized asks for the minimum it needs to read the page you are auditing, save your settings locally and check response headers. Review passed on the first submission with no rejections. I can't prove the narrow scope is the reason, but it matches what the documentation says reviewers examine.

FAQ

Is building a Chrome extension hard?

A basic one isn't: the example above is three files and a few lines of JavaScript. The difficulty is in the code that runs inside real pages, where layouts, timing and page scripts differ from site to site.

Can an AI tool build a Chrome extension?

Yes. AI coding assistants can write the manifest, popup and scripts from a description. I built Keep Optimized with Claude Code. You still need to read the manifest, load the extension and test it on real pages, because runtime problems are where AI-written code is weakest.

How much does it cost to build and publish a Chrome extension?

Building costs nothing beyond your tools. Publishing requires the one-time US$5 developer registration fee. After that, submitting extensions is free.

Does a Chrome extension need a backend server?

Not necessarily. Keep Optimized runs entirely in the browser with no server behind it. You need a backend only if the extension has to store or process something the browser can't handle on its own.

How long does Chrome Web Store review take?

Chrome's documentation says most reviews finish within a few days and some take up to a few weeks. New developers, new extensions and dangerous permission requests get a closer look.

Where to start

Pick something you do by hand every week on the same kind of page, build the smallest version that removes that step and use it yourself for a while. Publish only after it has earned its place in your own workflow.

Open for select advisory

Audits and advisory

Fixed-scope audits and strategic guidance across search architecture, AI visibility, and automation. Currently taking on select projects.