jvinhit//lab

Search posts

Type to search across journal entries.

navigate open esc close

Build Chrome Extensions · Part 1 — The Manifest V3 Mental Model & Your First Extension

Start from zero: what a browser extension really is, the four moving parts, the Manifest V3 model, and shipping a working "Hello World" extension you load unpacked in under 10 minutes. With a live anatomy explorer.

This is Part 1 of a 12-part series that takes you from “I want to build a browser extension” to confidently designing, building, debugging, and publishing one on the Chrome Web Store {Đây là Phần 1 của series 12 bài đưa bạn từ “tôi muốn làm extension trình duyệt” đến tự tin thiết kế, dựng, debug và xuất bản lên Chrome Web Store}. Every part ships real config (setup → demo), an interactive simulator, and exercises {Mỗi phần đều có config thật (setup → demo), một trình mô phỏng tương tác, và bài tập}.

We target Manifest V3 (MV3) — the current and required format for new Chrome extensions {Ta nhắm Manifest V3 (MV3) — định dạng hiện hành và bắt buộc cho extension Chrome mới}. Most of what you learn applies to Edge, Brave, and (with small tweaks) Firefox {Phần lớn kiến thức áp dụng cho Edge, Brave, và (với chỉnh sửa nhỏ) Firefox}.


1. What an extension actually is {Extension thực chất là gì}

An extension is just web technology — HTML, CSS, and JavaScript — bundled with a manifest file, given special permissions to interact with the browser and web pages through chrome.* APIs {Một extension chỉ là công nghệ web — HTML, CSS, JavaScript — đóng gói cùng một file manifest, được cấp quyền đặc biệt để tương tác với trình duyệt và trang web qua API chrome.*}.

That’s the liberating part {Phần giải phóng là đây}: if you can build a web page, you already know 80% of extension development {nếu bạn dựng được trang web, bạn đã biết 80% việc làm extension}. The new 20% is the architecture (where each piece runs) and the chrome.* APIs (what extra powers you get) {20% mới là kiến trúc (mỗi phần chạy ở đâu) và API chrome.* (bạn có thêm quyền năng gì)}.


2. The four moving parts {Bốn bộ phận chính}

Almost every extension is some combination of four pieces, each running in a different context {Gần như mọi extension là tổ hợp của bốn phần, mỗi phần chạy trong một ngữ cảnh khác nhau}:

PartWhere it runsJob
Manifest— (a JSON file)the blueprint: name, version, permissions, which files are what
Action + Popupthe toolbar icon & a tiny windowquick UI when the user clicks your icon
Content scriptinside a web pageread/modify the page’s DOM
Background service workeroff-screen, event-driventhe “brain”: reacts to events, coordinates everything

The crucial insight that confuses every beginner {Nhận thức then chốt làm rối mọi người mới}: these run in separate, isolated contexts and cannot directly call each other’s functions {chúng chạy trong các ngữ cảnh tách biệt, cô lập, và không thể gọi trực tiếp hàm của nhau}. They communicate by message passing (Part 6) {Chúng giao tiếp bằng truyền tin nhắn (Phần 6)}.

Click each piece in the explorer to see where it lives and what it does {Bấm vào từng phần trong explorer để xem nó ở đâu và làm gì}:


3. The manifest — the one required file {Manifest — file bắt buộc duy nhất}

Every extension has exactly one manifest.json at its root {Mỗi extension có đúng một manifest.json ở gốc}. It’s the entry point Chrome reads to understand your extension {Đó là điểm vào Chrome đọc để hiểu extension của bạn}. The minimum valid MV3 manifest {Manifest MV3 tối thiểu hợp lệ}:

{
  "manifest_version": 3,
  "name": "Hello World",
  "version": "1.0.0",
  "description": "My first extension",
  "action": {
    "default_popup": "popup.html"
  }
}

Three keys are mandatory: manifest_version (must be 3), name, and version {Ba key bắt buộc: manifest_version (phải là 3), name, và version}. Everything else declares a capability {Mọi thứ khác khai báo một khả năng}. We dissect the full manifest in Part 2 {Ta mổ xẻ manifest đầy đủ ở Phần 2}.


4. Build “Hello World” — step by step {Dựng “Hello World” — từng bước}

Create a folder hello-world/ with three files {Tạo thư mục hello-world/ với ba file}.

manifest.json {manifest.json}:

{
  "manifest_version": 3,
  "name": "Hello World",
  "version": "1.0.0",
  "description": "Says hello and changes the page background.",
  "action": { "default_popup": "popup.html" },
  "permissions": ["activeTab", "scripting"]
}

popup.html — the UI shown when the icon is clicked {popup.html — UI hiện khi bấm icon}:

<!DOCTYPE html>
<html>
  <body style="width: 200px; font-family: system-ui; padding: 12px;">
    <h3>Hello 👋</h3>
    <button id="paint">Paint this page</button>
    <script src="popup.js"></script>
  </body>
</html>

popup.js — uses a chrome.* API to inject code into the current tab {popup.js — dùng API chrome.* để tiêm code vào tab hiện tại}:

document.getElementById('paint').addEventListener('click', async () => {
  const [tab] = await chrome.tabs.query({ active: true, currentWindow: true });
  await chrome.scripting.executeScript({
    target: { tabId: tab.id },
    func: () => { document.body.style.background = '#c8ff00'; },
  });
});

That’s a complete, useful extension: click the icon → click the button → the current page turns lime {Đó là một extension hoàn chỉnh, hữu ích: bấm icon → bấm nút → trang hiện tại chuyển màu lime}.


5. Load it — the dev loop {Nạp nó — vòng lặp dev}

You don’t need a build step or the Web Store to run it locally {Bạn không cần bước build hay Web Store để chạy cục bộ}:

  1. Open chrome://extensions {Mở chrome://extensions}.
  2. Toggle Developer mode (top-right) {Bật Developer mode (góc trên phải)}.
  3. Click Load unpacked and select your hello-world/ folder {Bấm Load unpacked và chọn thư mục hello-world/}.
  4. The 🧩 icon appears — pin it, click it, try the button {Icon 🧩 xuất hiện — ghim lại, bấm, thử nút}.

The dev loop {Vòng lặp dev}: edit a file → click the reload ⟳ button on your extension’s card in chrome://extensions → test again {sửa file → bấm nút reload ⟳ trên card extension ở chrome://extensions → thử lại}. For popup/content changes you usually also reload the page {Với thay đổi popup/content thường phải reload cả trang}.

Debugging tip: each context has its own DevTools {Mẹo debug: mỗi ngữ cảnh có DevTools riêng}. Right-click the popup → Inspect. For the service worker, click the “service worker” link on the extension card. For content scripts, use the page’s normal DevTools console {Chuột phải popup → Inspect. Với service worker, bấm link “service worker” trên card. Với content script, dùng console DevTools bình thường của trang}.


6. Exercises {Bài tập}

1. Build and load the Hello World extension above; confirm the button paints the active tab {Dựng và nạp extension Hello World ở trên; xác nhận nút tô màu tab đang mở}.

2. Without running it, name the three mandatory manifest keys {Không chạy, nêu ba key manifest bắt buộc}.

Solution {Lời giải}

manifest_version, name, version {manifest_version, name, version}.

3. Why can’t your popup.js directly call a function defined in content.js? {Vì sao popup.js không gọi trực tiếp được hàm định nghĩa trong content.js?}

Solution {Lời giải}

They run in separate, isolated execution contexts; cross-context communication must go through message passing (Part 6) {Chúng chạy trong các ngữ cảnh thực thi tách biệt, cô lập; giao tiếp giữa ngữ cảnh phải qua truyền tin nhắn (Phần 6)}.

Stretch {Nâng cao}: change the painted color, then add a second button that resets the background to white {đổi màu tô, rồi thêm nút thứ hai để reset nền về trắng}.


Key takeaways {Điểm chính}

  • An extension is web tech + a manifest + chrome.* permissions {Extension là công nghệ web + manifest + quyền chrome.*}.
  • Four parts in isolated contexts: manifest, action/popup, content script, background service worker {Bốn phần trong ngữ cảnh cô lập: manifest, action/popup, content script, service worker nền}.
  • manifest.json is the one required file; only manifest_version/name/version are mandatory {manifest.jsonfile bắt buộc duy nhất; chỉ manifest_version/name/version là bắt buộc}.
  • The dev loop is Load unpacked → edit → reload — no build needed to start {Vòng lặp dev là Load unpacked → sửa → reload — không cần build để bắt đầu}.

Next up {Tiếp theo}

Part 2 — The manifest deep dive: every important key — action, permissions vs host_permissions, content_scripts, background, icons, web_accessible_resources — and an interactive manifest builder so you can see exactly what each line unlocks {Phần 2 — Manifest chuyên sâu: mọi key quan trọng — action, permissions vs host_permissions, content_scripts, background, icons, web_accessible_resources — và một trình dựng manifest tương tác để thấy đúng từng dòng mở khóa điều gì}.