Skip to content

manifest.json In Depth: MV3 Field Quick Reference ​

💡 One-sentence summary: manifest.json is the one file an extension cannot do without—3 platform-required fields as the base, 2 store-required fields for publishing, and the rest added as needed for functionality.

In the previous lesson C2 Your First Chrome Extension, the manifest.json was copied verbatim: it runs, but you may not be able to explain why each field is written the way it is. This lesson takes it apart field by field—which fields cause Chrome to refuse to load outright, which are only checked at publishing time, and how to write the high-frequency optional fields. From now on, whether you write it yourself or have AI generate it, you will be able to read it, modify it, and debug it.

What you'll be able to do after this lesson ​

  • Name the 3 platform-required fields and 2 store-required fields of manifest.json, along with their length and format restrictions
  • Starting from the minimal example, add action, background, content_scripts, and permissions one by one into your own manifest
  • Use an MV3 field quick-reference table to look up the purpose of all 46 fields
  • Avoid the three big beginner pitfalls: writing comments in JSON, copying old MV2 tutorials, and non-compliant version numbers
  • Go through a checklist before publishing, and know which pieces of information cannot be changed after upload

Your current struggles ​

  • "AI generated a manifest.json for me, but I can't understand at all which fields are required or what each field does"
  • "I copied from old tutorials online and the fields never matched—only later did I learn that was MV2 syntax, so it was all wasted effort"
  • "It loaded fine locally, but as soon as I uploaded it to the store I got Cannot parse the manifest, and I searched forever without finding the error"

When to use this technique ​

When you need to:

  • Look up the syntax and restrictions of a specific manifest field (how long can name be, how many segments can version have)
  • Build a loadable manifest.json from scratch, or add fields for new features to an existing extension
  • Check the required items before publishing, nipping "finding the mistake only after upload" in the bud

And you don't want to:

  • Read through the official documentation's full explanations of 46 fields—the quick-reference table is one screen below
  • Have Chrome refuse to load or the store reject your extension because of a miswritten field
  • Confuse MV2 and MV3 syntax, copy the wrong tutorial, and think Chrome is broken

🎒 Preparation before starting ​

  • Have completed C2 Your First Chrome Extension, with that runnable Hello World manifest.json on hand
  • A tool that can edit JSON files (Notepad works, editors like VS Code are more comfortable)
  • The chrome://extensions extension management page open, so you can refresh and verify at any time
  • 10 minutes

Core idea ​

Chrome has only one hard requirement for each extension: there must be a manifest.json in the root directory, which lists the extension's structure and behavior information—Chrome relies on it to recognize the extension, and the store relies on it to review the extension.

The official documentation divides all 46 fields into four groups; beginners should memorize the first two groups first:

GroupFieldsWhat happens if missing
Platform-required (3)manifest_version, name, versionChrome refuses to load outright and shows an error dialog
Required for store publishing (2)description, iconsRuns locally, but fails store review
Optional (37)action, background, content_scripts, permissions, etc.Not missing anything; add as needed for functionality
Optional · ChromeOS-only (4)file_handlers, etc.Basically never used by desktop Chrome extensions

Two iron rules that run through this whole page—note them down first:

  • You cannot write comments in JSON—uploading to the store will report Cannot parse the manifest
  • manifest.json must be in the root directory——when loading locally you select the extension folder, and when packaging a zip for the store, manifest must also be at the root of the archive, not wrapped in an extra folder

Follow along ​

Next, we will grow a manifest.json from 3 fields all the way to 10. Edit inside the Hello World folder from the previous lesson, and after each step go back to chrome://extensions and click the refresh icon on the extension card — after changing the manifest you must refresh the extension for it to take effect.

Step 1: Write down the three platform-required fields——the foundation of manifest.json ​

Why: The first thing Chrome does when loading an extension is read manifest.json; if any of these three fields is missing, loading fails outright.

Create (or open) manifest.json in the extension root directory and write:

json
{
  "manifest_version": 3,
  "name": "My First Extension",
  "version": "1.0.0"
}

The constraints for each of the three fields:

FieldConstraintNotes
manifest_versionOnly 3 is supportedMV3 is the only accepted form today; don't copy the 2 from old tutorials
nameAt most 75 charactersDisplayed in the store, the install dialog, and the chrome://extensions page
version1-4 dot-separated integers, each 0-65536e.g. 1.0.0; if the format is wrong, Chrome reports a clear error

You should see: a JSON with only three keys——that's all the platform requires.

To see the format rules in action, deliberately change version to "1.0.0.0.0" (five segments) and refresh the extension; Chrome will pop up an error dialog: Required value version is missing or invalid. It must be between 1-4 dot-separated integers each between 0 and 65536.——change it back to 1.0.0 to recover.

  • Quick check: manifest.json is in the root directory, all three fields are present, and the JSON contains no comments

Step 2: Add description and icons——completing the official minimal example ​

Why: These two items don't affect local running, but the Chrome Web Store checks them when you publish; the official documentation's "minimal manifest example" also includes them.

Add these two items to the JSON you just wrote, and you have the official documentation's Minimal Manifest verbatim (you may replace name with your own extension's name):

json
{
  "manifest_version": 3,
  "name": "Minimal Manifest",
  "version": "1.0.0",
  "description": "A basic example extension with only required keys",
  "icons": {
    "48": "images/icon-48.png",
    "128": "images/icon-128.png"
  }
}
  • description: at most 132 characters, displayed in the store and the extensions management page
  • icons: one or more icons, with sizes as keys and image paths as values

You should see: at this point it is exactly the official Minimal Manifest. The image icons points to must actually exist——the icon file used in the previous lesson's Hello World works; just change the path in icons to its actual location.

  • Quick check: description is no more than 132 characters; every path in icons has a corresponding file in the folder

Step 3: Add action and background——the popup and the event hub ​

Why: action determines what happens when the toolbar icon is clicked; background specifies the extension's service worker (event handler). The popup in the previous lesson was the work of action.

Add these two keys to the manifest (note the comma added after the previous key):

json
"action": {
  "default_popup": "hello.html",
  "default_icon": "hello_extensions.png"
},
"background": {
  "service_worker": "service-worker.js"
}
  • action: defines the toolbar icon's appearance and behavior; default_popup specifies the page that pops up when the icon is clicked, and default_icon specifies the icon image
  • background: the JS file that service_worker points to is the extension's "brain"——started on demand, stopped when idle; we'll cover the deeper techniques in the next lesson

You should see: after refreshing the extension, clicking the toolbar icon opens the popup as usual. Remember to create the service-worker.js file first (a single line of console.log("background ready") is fine for now)——every file path declared in the manifest must actually exist.

Step 4: Add the content_scripts configuration——scripts run automatically on specified pages ​

Why: If you want the extension to "automatically modify a page as soon as a certain kind of page opens", the static content_scripts declaration is what does it——no button clicks needed, the script injects itself.

Add this section to the manifest:

json
"content_scripts": [
  {
    "matches": ["https://*.nytimes.com/*"],
    "css": ["my-styles.css"],
    "js": ["content-script.js"]
  }
]
  • matches: required. Determines which pages the script is injected into, written in match pattern syntax (e.g. https://*.nytimes.com/*); every automatically running content script must specify a match pattern
  • js / css: optional. JS / CSS files injected in array order, with paths relative to the extension root directory

You should see: after refreshing the extension, open a page matching matches and the script takes effect automatically. After changing a content script file, refresh the extension and also refresh the host page. As for "why can't my script and the page's variables see each other"——the next lesson C4 covers isolated worlds.

Step 5: Add the permissions and host_permissions declarations ​

Why: permissions enables specific extension APIs; without declaring them they cannot be called; host_permissions declares which websites the extension can interact with——permissions for these sites are requested from the user at install time.

Add these two lines to the manifest:

json
"permissions": ["storage"],
"host_permissions": ["https://*.example.com/"]
  • permissions: an array of the API names to enable, e.g. storage, scripting, activeTab; declare only what you actually use
  • host_permissions: lists the pages you can interact with using match patterns

You should see: refresh the extension and it loads normally. A misspelled permission name won't be rejected at load, but a red Errors button appears on the extensions page——for example, writing activeTab as activetab yields the message Permission 'activetab' is unknown or URL pattern is malformed.. The full debugging playbook is in C5.

Step 6: Verify with a local load, then check item by item before publishing ​

Why: Only Chrome and the store have the final say on whether the fields are written correctly. Run it locally first, then check again before publishing, and you avoid the most expensive rework.

Steps:

  1. Go back to chrome://extensions and click the refresh icon on the extension card (the three-step loading method was covered in the previous lesson)
  2. If loading fails, an error dialog pops up——fix the manifest according to the error, then refresh again
  3. When preparing to publish, check item by item:
Check itemStandard
name / description≤75 / ≤132 characters, no typos—manifest metadata cannot be changed after upload; if you find a typo, the only fix is to modify the file, bump the version number, and repackage
versionmust be greater than the previously uploaded version; for the first release, set a low value such as 0.0.0.1 to leave room for future increments
CommentsThe JSON must not contain any comments, otherwise the upload fails with Cannot parse the manifest
PackagingThe root of the zip archive must contain manifest.json directly, with no extra folder wrapping it

You should see: local loading with no error dialog and no red Errors; the published zip shows manifest.json at the first level when unpacked. The full workflow of registration, submission, and review is in C6.

📋 MV3 Field Quick Reference ​

💡 How to use this table

Ask two questions first: which layer does this field belong to (platform-required / store-required / optional)? Which group does the feature I need fall into (UI / permissions / network / distribution)? Locate the row, write it accordingly, check the constraints, then go back to the corresponding step on the page for expanded details. The fields and constraints in this table are compiled from Chrome's official Manifest reference documentation.

Platform-required (3 fields) ​

FieldConstraintsPurpose
manifest_versionInteger, the only supported value is 3Manifest format version
name≤75 charactersName shown in the store, the install dialog, and chrome://extensions
version1-4 dot-separated integers, each segment 0-65536Version number; a newly uploaded version must be greater than the previous one

Required for store listing (2 fields) ​

FieldConstraintsPurpose
description≤132 charactersDescription for the store and the extensions management page
iconsOne or more iconsIcon representing the extension

Frequently used optional fields (get to know these 5 first) ​

FieldKey points for writingPurpose
actiondefault_popup points to the popup page, default_icon specifies the iconAppearance and behavior of the toolbar icon
background{"service_worker": "service-worker.js"}specifies the service worker that acts as the event handler
content_scriptsmatches required + js / css arraysJS/CSS automatically injected when a matching page is opened
permissionsArray of API names, e.g. ["storage"]Enables specific extension APIs
host_permissionsArray of match patternsDeclares the web pages the extension can interact with; authorization is requested at install time

Other optional fields (grouped by purpose) ​

UI and interaction

FieldPurpose
commandsDefines keyboard shortcuts within the extension
options_pageSpecifies options.html as the extension's settings page
options_uiLets users modify extension options within the extensions management page
side_panelSpecifies the HTML page displayed in the side panel
omniboxRegisters a keyword in the Chrome address bar
chrome_url_overridesOverrides Chrome's default pages
devtools_pageDefines pages that use the DevTools API

Permissions and security

FieldPurpose
optional_permissionsDeclares optional API permissions
optional_host_permissionsDeclares optional host permissions
content_security_policyRestricts the scripts, styles, and other resources the extension can load
externally_connectableSpecifies which web pages and extensions can connect to your extension
web_accessible_resourcesDefines files inside the extension that web pages or other extensions can access
sandboxDefines sandboxed pages that cannot use extension APIs directly
incognitoBehavior in Incognito mode, values spanning / split / not_allowed
keySpecifies the extension ID for development purposes

Network and data

FieldPurpose
declarative_net_requestStatic rules for declarative network requests, capable of intercepting and modifying network requests
oauth2Configure OAuth 2.0 (client_id and scopes)
storageDeclare a JSON schema for a managed storage area
tts_engineRegister the extension as a text-to-speech engine
mime_types_handlerRegister the extension as a handler for a MIME type (e.g., application/pdf)
requirementsList the technologies required to use the extension

Version and distribution

FieldPurpose
version_nameA human-readable version alias (e.g., "1.0 beta"); if omitted, version is shown
minimum_chrome_versionThe minimum Chrome version required to install the extension; lower versions show it as incompatible, cannot install it, and it does not auto-update
homepage_urlThe extension's homepage URL; defaults to the store page if omitted
update_urlThe update page URL for self-hosted distribution (not listed on the store)
importAllow importing resources into the extension
exportAllow exporting resources from the extension

Internationalization and display

FieldPurpose
default_localeThe default language of a multilingual extension (e.g., en); required for localized extensions, and cannot be used by non-localized extensions
short_nameA short name, ≤12 characters; if omitted, name is truncated for display

Chrome settings

FieldPurpose
chrome_settings_overridesOverride some Chrome settings
cross_origin_embedder_policyConfigure the Cross-Origin-Embedder-Policy request header
cross_origin_opener_policyConfigure the Cross-Origin-Opener-Policy request header

ChromeOS-specific (4 in total)

FieldPurpose
file_browser_handlersAccess ChromeOS file-browser-related APIs
file_handlersSpecify the file types handled by a ChromeOS extension
file_system_provider_capabilitiesLet the extension create a file system usable on ChromeOS
input_componentsUse the Input Method Editor (IME) APIs

Checkpoint ✅ ​

  • manifest.json is in the extension root directory, and there are no comments anywhere in the JSON
  • manifest_version is 3; name is ≤75 characters; version is 1-4 dot-separated integers
  • If publishing to the store: description is ≤132 characters, and every path in icons actually exists
  • The matches of content_scripts is filled in and follows the match pattern syntax
  • permissions only declares APIs that are actually used, with no typos

Pitfall warnings ​

❌ Writing comments in manifest.json ​

json
{
  "manifest_version": 3,  // MV3 写死
  "name": "My Extension"
}

Reason: JSON does not support comments, and the store throws Cannot parse the manifest at upload time. Correct approach: Remove all comments before uploading; if you want to keep notes, put them in the README or a nearby document.

❌ Copying old tutorials and writing manifest_version as 2 ​

Reason: Many tutorials online still use MV2-era syntax, while in MV3 the only supported value for manifest_version is 3, so copying the old syntax is wasted effort. Correct approach: Always use "manifest_version": 3 for new extensions; double-check any fields you are unsure about against the cheat sheet on this page.

❌ Writing version carelessly and having the new upload rejected ​

json
"version": "1.0.0.0.0"   // ❌ 五段,超了
"version": "0.0.0.1"     // ✅ 初版设低值,以后只往上加

Reason: version must be 1-4 dot-separated integers, each segment 0-65536; moreover, the store requires each newly uploaded version number to be greater than the previous one. Correct approach: Set a low value like 0.0.0.1 for the initial release and only increment with each release, so you always have room.

❌ Discovering a typo in name only after uploading ​

Reason: After uploading to the store, the manifest metadata cannot be changed in the developer dashboard — it is not a permissions issue, it is simply the rule. Correct approach: Proofread name and description character by character before uploading; if there really is an error, the only option is to modify the manifest, bump the version, repackage, and upload again.

🔧 Advanced tips (optional) ​

  • Attach the official manifest schema to your editor: Associate the manifest schema in your code editor (json.schemastore.org/chrome-manifest), so wrong fields or missing required items are flagged in red on the spot, instead of finding out only when Chrome refuses to load
  • version_name vs. version division of labor: version is the machine version number Chrome uses for comparison; version_name (e.g., "1.0 beta") is a human-readable alias shown on the extensions management page. If you want "a nice-looking version name + a strictly increasing version number", write both
  • short_name to the rescue: When the name is too long and display space is insufficient, short_name (≤12 characters) steps in; if omitted, Chrome truncates name for display

Lesson Summary ​

✅ In this lesson you nailed:

  • The layered structure of manifest.json: platform-required 3 fields, store-required 2 fields, optional 41 fields (including 4 ChromeOS-specific fields)
  • Building up from a minimal example to the full syntax of action, background, content_scripts, and permissions step by step
  • The format rules for version and the "increment only" discipline, plus two iron rules: no comments in JSON and root directory placement
  • Pre-submission checklist: metadata cannot be changed after upload, so fixing a typo = releasing a new version

Review of key configuration fields:

FieldRecommended value / limitDescription
manifest_version3The only supported value
name≤75 charactersThe extension name shown in three places
versionstart at 0.0.0.1, increment only1-4 dot-separated integers, each segment 0-65536
description≤132 charactersRequired by the store
iconsstart with the two sizes 48 / 128Required by the store; the paths must exist
action.default_popuphello.htmlThe popup page shown when the icon is clicked
background.service_workerservice-worker.jsThe event hub script
content_scripts[].matchesmatch pattern, requiredDetermines which pages get injected
permissionsList only the APIs you usee.g. ["storage"]
host_permissionsAn array of match patternsRequests authorization at install time

Next Lesson Preview ​

Now that the fields are declared, the next lesson C4 Content Scripts and Service Workers: the Brain and Hands of an Extension explains the mechanisms behind these two manifest fields in depth.

You will learn:

  • The official definitions of content scripts and service workers, and what each can do
  • "Isolated worlds": why your scripts and page variables can't see each other
  • How to choose among static declaration, dynamic declaration, and programmatic injection
  • The service worker's on-demand startup and idle-stop temperament, and when to use which