Extension Manifest (manifest.json)
Every extension requires a manifest.json file at the root of the extension project. This file declares the extension's identity, capabilities, permissions, and UI contributions. The app reads it at install time and at startup to determine how and when to load the extension.
Overview
The manifest serves several purposes:
- Identity -- Uniquely identifies the extension with an
id,name,version, and optional metadata likeauthor,description, andicon. - Entry point -- Specifies which JavaScript file to execute via the
mainfield. - Activation strategy -- Controls when the extension is loaded (eagerly at startup, lazily on a command, or when a specific file type is opened).
- Permissions -- Declares which privileged APIs the extension needs (terminal access, filesystem access outside the project, project creation).
- Contributions -- Registers static UI elements such as commands, themes, status bar items, drawer icons, and more.
- Bundling -- Controls which files are included in or excluded from the published package.
Complete Schema
| Field | Type | Required | Default | Description |
|---|---|---|---|---|
id | string | Yes | -- | Unique extension identifier. Must match [a-zA-Z0-9][a-zA-Z0-9_-]* and be at most 128 characters. |
name | string | Yes | -- | Human-readable display name shown in the extension store and settings. |
version | string | No | "0.0.0" | Semantic version string (e.g., "1.2.3"). |
description | string | No | null | Short description of what the extension does. |
author | string | No | null | Author or publisher name. |
icon | string | No | null | Relative path to an icon image file (e.g., "icon.png"). |
main | string | No | null | Relative path to the JavaScript entry point (e.g., "main.js" or "dist/main.js"). Required for extensions that execute code. |
categories | string[] | No | [] | Categories for store filtering. Known categories: "Themes", "Languages", "Formatters", "Tools", "Utilities". |
activationEvents | string[] | No | [] | Events that trigger extension activation. See Activation Events. |
engineVersion | string | No | null | Minimum engine version required (e.g., ">=1" or "2"). See Engine Version Compatibility. |
permissions | string[] | No | [] | Privileged APIs the extension needs. See Permissions. |
contributes | object | No | {} | Static UI contributions. See Contributes. |
files | string[] | No | [] | Whitelist of files and directories to include in the published bundle. When empty, all files are included (respecting .gitignore). |
ignore | string[] | No | [] | Glob patterns for files to exclude from the bundle, applied on top of .gitignore and the files whitelist. |
Field Details
id
The extension ID uniquely identifies your extension across the store and the editor. It must:
- Start with an alphanumeric character (
a-z,A-Z,0-9). - Contain only alphanumeric characters, hyphens (
-), and underscores (_). - Be no longer than 128 characters.
The pattern enforced is:
^[a-zA-Z0-9][a-zA-Z0-9_-]*$
Invalid IDs will be rejected and the extension will not load.
Good IDs: my-extension, dev-tools-linter, python_support, theme-dracula
Bad IDs: -starts-with-dash, .starts-with-dot, has spaces, has@special!chars
name
The display name shown to users in the extension store, the installed extensions list, and permission dialogs. It must be a non-empty string.
version
A semantic version string. If omitted, it defaults to "0.0.0". The store uses this to determine whether an update is available.
main
The relative path to the JavaScript file that the extension host will execute. This is the entry point for all extension logic. If main is null, the extension can still contribute static UI elements (themes, settings, etc.) but cannot run any JavaScript code.
icon
A relative path to an image file within the extension bundle. Used as the extension's icon in the store and settings UI.
categories
An array of category strings used for filtering in the extension store. The store UI currently supports these categories:
"Themes"-- Visual themes for the app and editor."Languages"-- Language support and tooling."Formatters"-- Code formatters."Tools"-- Developer tools and utilities."Utilities"-- General-purpose utilities.
You may use custom category strings, but they will not appear as filter chips in the store unless the app is updated to recognize them.
files
When non-empty, acts as a whitelist -- only files matching the listed patterns are included in the published bundle. Supports:
- Exact filenames:
"manifest.json" - Directory prefixes:
"dist/"(includes everything underdist/) - Glob patterns:
"src/**/*.js"
When empty (the default), all files are included except those excluded by .gitignore. In both cases, the ignore patterns are applied afterwards as an additional exclusion layer, and large dependency directories (e.g., node_modules) are always excluded.
When specific patterns are listed in files, matching files are included in the bundle even if they appear in .gitignore. Only the default behavior (empty files) respects .gitignore exclusions.
ignore
Glob patterns to exclude from the bundle. These patterns use gitignore-style syntax and are applied after the files whitelist (or after the default .gitignore-based inclusion).
"ignore": ["src/", "test/", "*.test.js", ".github/"]
Activation Events
The activationEvents array controls when the extension host loads and executes the extension's main script. Using targeted activation events instead of eager startup improves app launch performance.
| Event | Description |
|---|---|
onStartupFinished | Activated after the app finishes initialization. The extension loads on every app start. |
* | Wildcard. Behaves identically to onStartupFinished. |
onCommand:{commandId} | Activated the first time the specified command is invoked. The extension stays dormant until that command fires. |
onFileOpen:*.{ext} | Activated when a file matching the glob pattern is opened. Supports * and ** wildcards. |
How lazy activation works
For onCommand: events, the app registers a placeholder command at startup. When the user triggers the command, the app activates the extension and re-dispatches the command to the now-loaded extension handler.
For onFileOpen: events, the app checks pending activations whenever a file is opened. If the filename matches the glob pattern, the extension is activated.
If an extension has main set but no recognized activation events, it will not be activated and a warning is logged.
Examples
// Activate on startup -- always loaded
"activationEvents": ["onStartupFinished"]
// Activate only when the user runs a specific command
"activationEvents": ["onCommand:my-ext.runLinter"]
// Activate when Python files are opened
"activationEvents": ["onFileOpen:*.py"]
// Multiple events -- activate on either condition
"activationEvents": [
"onCommand:my-ext.formatCode",
"onFileOpen:*.rs",
"onFileOpen:*.toml"
]
Engine Version Compatibility
The engineVersion field specifies the minimum extension engine version the extension requires. The current engine version is 0.1.0.
The app performs semver comparison with a major-match rule: the major version must match exactly, and within the same major version the current engine must be >= the required version. A bare version (no operator) is treated as >=.
engineVersion value | Current engine (0.1.0) | Result |
|---|---|---|
">=0.1.0" | 0.1.0 | Passes (current version meets requirement) |
">=2.0.0" | 0.1.0 | Fails (major version mismatch) |
"^0.1.0" | 0.1.0 | Passes (compatible within major) |
When activation is blocked, the log shows:
Extension "my-ext" requires engine version 0.2.0 but current is 0.1.0 — skipping activation
Permissions
Extensions run in a sandboxed environment. Certain APIs require explicit user consent. Declare needed permissions so the app can prompt the user at runtime.
| Permission | String value | Description |
|---|---|---|
| Terminal | "terminal" | Run shell commands and open terminal sessions on the device. |
| File System | "fileSystem" | Read and write files outside the current project directory. |
| Project Create | "projectCreate" | Create new projects on the device. |
Users are prompted when the extension first tries to use a permission-gated API. They can grant access at three scopes:
- One-time -- Valid for this single request only.
- Session -- Valid until the app restarts (in-memory only).
- Permanent -- Persisted across app restarts.
Granular grants are supported. For example, a user can grant terminal access for git commands only, or filesystem access to a specific directory only.
If an extension calls a permission-gated API without declaring the permission in its manifest, the call is rejected with a PERMISSION_DENIED error. Always declare permissions upfront so users know what to expect before installing.
The Contributes Section
The contributes object declares static UI elements that are registered when the extension is installed. Each key maps to an array of contribution objects.
commands
Register commands that appear in the command palette.
| Property | Type | Required | Default | Description |
|---|---|---|---|---|
id | string | Yes | -- | Unique command ID (e.g., "my-ext.runLinter"). |
label | string | No | Last segment of id | Display label in the command palette. |
description | string | No | "" | Short description shown alongside the command. |
category | string | No | "Extensions" | Grouping category in the palette. |
"commands": [
{
"id": "my-ext.runLinter",
"label": "Run Linter",
"description": "Lint the current file",
"category": "Linting"
}
]
See Commands API for full details on registration, execution, and hooks.
themes
Register app and editor color themes.
| Property | Type | Required | Default | Description |
|---|---|---|---|---|
id | string | Yes | -- | Unique theme ID. |
label | string | Yes | -- | Display name in the theme picker. |
type | string | Yes | -- | Must be "dark" or "light". |
appColors | object | No | null | Map of app color token names to hex values. |
editorColors | object | No | null | Map of editor color token names to hex values. |
tokenColors | object | No | null | Map of syntax token names to hex values. |
"themes": [
{
"id": "my-ext.nightOwl",
"label": "Night Owl",
"type": "dark",
"appColors": {
"background": "#011627",
"text": "#d6deeb"
},
"editorColors": {
"background": "#011627",
"lineHighlight": "#01162788"
},
"tokenColors": {
"keyword": "#c792ea",
"string": "#ecc48d",
"comment": "#637777"
}
}
]
See Themes for the complete color schema and examples.
settings
Register extension configuration options that appear in the extension settings page.
| Property | Type | Required | Default | Description |
|---|---|---|---|---|
id | string | Yes | -- | Unique setting ID. |
key | string | No | "{extensionId}.{id}" | Storage key for the setting value. |
label | string | No | Value of id | Display label in the settings UI. |
description | string | No | "" | Help text shown below the setting. |
type | string | No | "string" | Data type: "string", "boolean", "number", or "enum". |
default | any | No | null | Default value for the setting. |
enumValues | string[] | No | null | Allowed values when type is "enum". |
"settings": [
{
"id": "theme",
"label": "Color Theme",
"description": "Choose the extension's color scheme",
"type": "enum",
"default": "auto",
"enumValues": ["auto", "light", "dark"]
},
{
"id": "maxResults",
"label": "Max Results",
"description": "Maximum number of search results to display",
"type": "number",
"default": 50
},
{
"id": "enabled",
"type": "boolean",
"default": true
}
]
See Contribution Points for details on all contribution types.
drawerIcons
Add icons to the activity bar (side drawer). Tapping the icon can execute a command, show an HTML panel, or open a URL.
| Property | Type | Required | Default | Description |
|---|---|---|---|---|
id | string | Yes | -- | Unique drawer icon ID. |
label | string | Yes | -- | Tooltip / label text. |
icon | string | Yes | -- | Material icon name (e.g., "science", "terminal"). |
commandId | string | No | null | Command to execute when tapped. |
html | string | No | null | HTML content to render in a webview panel. |
url | string | No | null | URL to load in a webview panel. |
priority | int | No | 100 | Sort order (lower values appear first). |
Provide exactly one of commandId, html, or url.
"drawerIcons": [
{
"id": "my-ext.panel",
"label": "My Panel",
"icon": "science",
"commandId": "my-ext.openPanel",
"priority": 50
}
]
statusBarItems
The status bar is the thin bar at the bottom of the editor that shows contextual information like cursor position and file type.
Add items to the editor status bar.
| Property | Type | Required | Default | Description |
|---|---|---|---|---|
id | string | Yes | -- | Unique status bar item ID. |
label | string | Yes | -- | Text displayed in the status bar. Can be updated at runtime via the API. |
icon | string | No | null | Material icon name. |
commandId | string | Yes | -- | Command to execute when the item is tapped. |
alignment | string | No | "left" | Position in the status bar: "left" or "right". |
priority | int | No | 100 | Sort order within the alignment group (lower values appear first). |
"statusBarItems": [
{
"id": "my-ext.wordCount",
"label": "Words: 0",
"icon": "text_fields",
"commandId": "my-ext.showWordCount",
"alignment": "right",
"priority": 200
}
]
See Contribution Points for more details on status bar items.
toolsItems
Add items to the tools menu on the home screen.
| Property | Type | Required | Default | Description |
|---|---|---|---|---|
id | string | Yes | -- | Unique tools item ID. |
label | string | Yes | -- | Display label. |
icon | string | Yes | -- | Material icon name. |
commandId | string | Yes | -- | Command to execute when tapped. |
priority | int | No | 100 | Sort order (lower values appear first). |
"toolsItems": [
{
"id": "my-ext.colorPicker",
"label": "Color Picker",
"icon": "palette",
"commandId": "my-ext.openColorPicker",
"priority": 50
}
]
projectTemplates
Add templates to the "new project" creation flow.
| Property | Type | Required | Default | Description |
|---|---|---|---|---|
id | string | Yes | -- | Unique template ID. |
label | string | Yes | -- | Template display name. |
icon | string | Yes | -- | Material icon name. |
commandId | string | Yes | -- | Command to execute to create the project. |
priority | int | No | 100 | Sort order (lower values appear first). |
"projectTemplates": [
{
"id": "my-ext.flutterApp",
"label": "Flutter App",
"icon": "phone_android",
"commandId": "my-ext.createFlutterProject",
"priority": 10
}
]
fileContextActions
Add items to the context menu that appears on files and folders in the file tree.
| Property | Type | Required | Default | Description |
|---|---|---|---|---|
id | string | Yes | -- | Unique action ID. |
label | string | Yes | -- | Menu item label. |
icon | string | No | null | Material icon name. |
commandId | string | Yes | -- | Command to execute. The file path is passed as an argument. |
fileExtensions | string[] | No | null | File extensions this action applies to (e.g., [".js", ".ts"]). null means all files. |
appliesToFolders | bool | No | false | Whether this action appears on folders. |
priority | int | No | 100 | Sort order (lower values appear first). |
"fileContextActions": [
{
"id": "my-ext.minify",
"label": "Minify File",
"icon": "compress",
"commandId": "my-ext.minifyFile",
"fileExtensions": [".js", ".css", ".html"],
"appliesToFolders": false,
"priority": 90
}
]
bottomSheets
Register bottom sheet panels with WebView content.
| Property | Type | Required | Default | Description |
|---|---|---|---|---|
id | string | Yes | -- | Unique bottom sheet ID. |
title | string | Yes | -- | Title displayed at the top of the sheet. |
html | string | No | null | HTML content to render. |
url | string | No | null | URL to load in the sheet's WebView. |
showTitle | bool | No | true | Whether to show the title bar. |
showCloseButton | bool | No | true | Whether to show the close button. |
showOpenInTab | bool | No | true | Whether to show the "open in tab" button. |
Provide either html or url.
"bottomSheets": [
{
"id": "my-ext.preview",
"title": "Live Preview",
"url": "https://localhost:3000",
"showTitle": true,
"showCloseButton": true,
"showOpenInTab": true
}
]
codemirrorExtensions
Register CodeMirror 6 editor extensions. Supports two modes:
- Inline mode -- Provide JavaScript code directly via
jsCodethat returns a CM6ExtensionorExtension[]. - File-based mode -- Point to a bundled JavaScript file via
file. Optionalparamsare forwarded to the factory function exported by the bundle.
| Property | Type | Required | Default | Description |
|---|---|---|---|---|
id | string | Yes | -- | Unique extension ID. |
jsCode | string | No | "" | Inline JavaScript code (inline mode). |
file | string | No | null | Relative path to a JS file in the extension bundle (file-based mode). |
params | object | No | null | Parameters passed to the file's factory function (file-based mode). |
fileExtensions | string[] | No | null | File extensions this applies to. null means all files. |
description | string | No | null | Human-readable description. |
"codemirrorExtensions": [
{
"id": "my-ext.bracketHighlighter",
"jsCode": "return bracketMatching()",
"fileExtensions": [".js", ".ts"],
"description": "Highlight matching brackets"
},
{
"id": "my-ext.customHighlighting",
"file": "dist/cm-highlight.js",
"params": { "theme": "dark" },
"fileExtensions": [".mylang"],
"description": "Custom syntax highlighting for .mylang files"
}
]
See CodeMirror Extensions for inline and file-based modes.
formatters
Register code formatters for specific languages.
| Property | Type | Required | Default | Description |
|---|---|---|---|---|
id | string | Yes | -- | Unique formatter ID. |
label | string | Yes | -- | Display name (e.g., "Prettier", "Black"). |
commandId | string | Yes | -- | Command to execute when formatting is requested. |
languages | string[] | Yes | [] | Language IDs this formatter supports. Empty array means all languages. |
"formatters": [
{
"id": "my-ext.prettier",
"label": "Prettier",
"commandId": "my-ext.formatWithPrettier",
"languages": ["javascript", "typescript", "css", "html", "json"]
}
]
See Formatters for the execution flow and language matching.
customEditors
Register a custom editor that opens specific file types in an extension-provided webview instead of the built-in CodeMirror editor.
| Property | Type | Required | Default | Description |
|---|---|---|---|---|
id | string | Yes | -- | Unique custom editor ID. |
label | string | Yes | -- | Display name shown in "Open With" menu. |
fileExtensions | string[] | Yes | [] | File extensions this editor handles (e.g. [".pdf", ".png"]). |
commandId | string | Yes | -- | Command to execute when opening a file. Receives { uri, name } as args. |
isDefault | boolean | No | false | If true, files matching fileExtensions open with this editor by default. |
priority | number | No | 100 | Sort priority (lower = higher priority). |
"customEditors": [
{
"id": "my-ext.pdfViewer",
"label": "PDF Viewer",
"fileExtensions": [".pdf"],
"commandId": "my-ext.openPdf",
"isDefault": true
}
]
The command receives { uri: string, name: string } as arguments. The extension can then read the file content via workspace.fs.read(uri) for text files, or use the ext-file:// URL scheme in a webview for binary files:
// In the command handler:
editor.commands.registerCommand("my-ext.openImage", async (args) => {
// Encode the original URI (file:// or content://) as a query parameter
const src = `ext-file://load?uri=${encodeURIComponent(args.uri)}`;
const html = `<html><body style="margin:0;display:flex;justify-content:center;align-items:center;height:100vh;background:#1e1e1e"><img src="${src}" style="max-width:100%;max-height:100%;object-fit:contain"></body></html>`;
await editor.window.openWebviewTab({ html, title: args.name });
});
The ext-file:// scheme works with both file:// and content:// (SAF) URIs. The app reads the file and serves it with the correct MIME type. Use it for binary content like images, PDFs, and fonts.
URL format: ext-file://load?uri=<encodeURIComponent(originalUri)>
Examples
Minimal Manifest
The absolute minimum required to register an extension:
{
"id": "hello-world",
"name": "Hello World"
}
This registers an extension with no entry point, no contributions, and no activation events. It does nothing, but it is a valid manifest.
Minimal Runnable Extension
A minimal extension that actually executes code:
{
"id": "hello-world",
"name": "Hello World",
"version": "0.1.0",
"main": "main.js",
"activationEvents": ["onStartupFinished"],
"contributes": {
"commands": [
{
"id": "hello-world.sayHello",
"label": "Say Hello"
}
]
}
}
Full-Featured Manifest
A comprehensive example using most available fields:
{
"id": "python-tools",
"name": "Python Tools",
"version": "2.1.0",
"description": "Python development tools -- linting, formatting, and project scaffolding",
"author": "Jane Developer",
"icon": "icon.png",
"main": "dist/main.js",
"categories": ["Formatters", "Tools"],
"activationEvents": [
"onFileOpen:*.py",
"onFileOpen:*.pyw",
"onCommand:python-tools.createVenv"
],
"engineVersion": ">=0.1.0",
"permissions": ["terminal", "fileSystem"],
"files": ["dist/", "icon.png", "manifest.json"],
"ignore": ["src/", "test/", "node_modules/", "*.map"],
"contributes": {
"commands": [
{
"id": "python-tools.lint",
"label": "Lint File",
"description": "Run pylint on the current file",
"category": "Python"
},
{
"id": "python-tools.format",
"label": "Format with Black",
"description": "Format the current file using Black",
"category": "Python"
},
{
"id": "python-tools.createVenv",
"label": "Create Virtual Environment",
"category": "Python"
}
],
"formatters": [
{
"id": "python-tools.black",
"label": "Black",
"commandId": "python-tools.format",
"languages": ["python"]
}
],
"settings": [
{
"id": "lintOnSave",
"label": "Lint on Save",
"description": "Automatically run the linter when a file is saved",
"type": "boolean",
"default": true
},
{
"id": "formatter",
"label": "Formatter",
"description": "Which formatter to use",
"type": "enum",
"default": "black",
"enumValues": ["black", "autopep8", "yapf"]
},
{
"id": "lineLength",
"label": "Max Line Length",
"description": "Maximum line length for formatting",
"type": "number",
"default": 88
}
],
"statusBarItems": [
{
"id": "python-tools.venvStatus",
"label": "No venv",
"icon": "terminal",
"commandId": "python-tools.createVenv",
"alignment": "left",
"priority": 50
}
],
"toolsItems": [
{
"id": "python-tools.pipManager",
"label": "Pip Package Manager",
"icon": "inventory_2",
"commandId": "python-tools.openPipManager",
"priority": 60
}
],
"projectTemplates": [
{
"id": "python-tools.flaskApp",
"label": "Flask Web App",
"icon": "web",
"commandId": "python-tools.createFlaskProject",
"priority": 30
}
],
"fileContextActions": [
{
"id": "python-tools.runFile",
"label": "Run Python File",
"icon": "play_arrow",
"commandId": "python-tools.run",
"fileExtensions": [".py"],
"appliesToFolders": false,
"priority": 10
}
],
"drawerIcons": [
{
"id": "python-tools.panel",
"label": "Python",
"icon": "code",
"commandId": "python-tools.openPanel",
"priority": 80
}
],
"themes": [
{
"id": "python-tools.darkTheme",
"label": "Python Dark",
"type": "dark",
"editorColors": {
"background": "#1e1e2e",
"lineHighlight": "#2a2a3e"
},
"tokenColors": {
"keyword": "#cba6f7",
"string": "#a6e3a1",
"comment": "#6c7086"
}
}
],
"codemirrorExtensions": [
{
"id": "python-tools.indentGuides",
"file": "dist/cm-indent-guides.js",
"fileExtensions": [".py"],
"description": "Python-aware indentation guides"
}
],
"bottomSheets": [
{
"id": "python-tools.docs",
"title": "Python Docs",
"url": "https://docs.python.org/3/",
"showTitle": true,
"showCloseButton": true,
"showOpenInTab": true
}
]
}
}
Validation Rules Summary
The following validations are enforced when a manifest is parsed or an extension is registered:
idis required -- Must be a non-empty string. Avalidation erroris thrown if missing or empty.nameis required -- Must be a non-empty string. Avalidation erroris thrown if missing or empty.idformat -- Must match the pattern[a-zA-Z0-9][a-zA-Z0-9_-]*. Invalid characters cause avalidation error.idlength -- Must not exceed 128 characters. Longer IDs cause avalidation error.- Theme
type-- Must be exactly"dark"or"light". Any other value causes avalidation error. - Engine version -- The major version must match exactly, and within the same major the current engine must be
>=the required version. The current engine version is0.1.0. contributesparsing -- Non-objectcontributesvalues are silently ignored. Within each contribution type, non-object array elements are skipped.