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}:
| Part | Where it runs | Job |
|---|---|---|
| Manifest | — (a JSON file) | the blueprint: name, version, permissions, which files are what |
| Action + Popup | the toolbar icon & a tiny window | quick UI when the user clicks your icon |
| Content script | inside a web page | read/modify the page’s DOM |
| Background service worker | off-screen, event-driven | the “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ộ}:
- Open
chrome://extensions{Mởchrome://extensions}. - Toggle Developer mode (top-right) {Bật Developer mode (góc trên phải)}.
- Click Load unpacked and select your
hello-world/folder {Bấm Load unpacked và chọn thư mụchello-world/}. - 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ềnchrome.*}. - 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.jsonis the one required file; onlymanifest_version/name/versionare mandatory {manifest.jsonlà file bắt buộc duy nhất; chỉmanifest_version/name/versionlà 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ì}.