Skip to main content

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 like author, description, and icon.
  • Entry point -- Specifies which JavaScript file to execute via the main field.
  • 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

FieldTypeRequiredDefaultDescription
idstringYes--Unique extension identifier. Must match [a-zA-Z0-9][a-zA-Z0-9_-]* and be at most 128 characters.
namestringYes--Human-readable display name shown in the extension store and settings.
versionstringNo"0.0.0"Semantic version string (e.g., "1.2.3").
descriptionstringNonullShort description of what the extension does.
authorstringNonullAuthor or publisher name.
iconstringNonullRelative path to an icon image file (e.g., "icon.png").
mainstringNonullRelative path to the JavaScript entry point (e.g., "main.js" or "dist/main.js"). Required for extensions that execute code.
categoriesstring[]No[]Categories for store filtering. Known categories: "Themes", "Languages", "Formatters", "Tools", "Utilities".
activationEventsstring[]No[]Events that trigger extension activation. See Activation Events.
engineVersionstringNonullMinimum engine version required (e.g., ">=1" or "2"). See Engine Version Compatibility.
permissionsstring[]No[]Privileged APIs the extension needs. See Permissions.
contributesobjectNo{}Static UI contributions. See Contributes.
filesstring[]No[]Whitelist of files and directories to include in the published bundle. When empty, all files are included (respecting .gitignore).
ignorestring[]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 under dist/)
  • 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.

note

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.

EventDescription
onStartupFinishedActivated 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 valueCurrent engine (0.1.0)Result
">=0.1.0"0.1.0Passes (current version meets requirement)
">=2.0.0"0.1.0Fails (major version mismatch)
"^0.1.0"0.1.0Passes (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.

PermissionString valueDescription
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.

PropertyTypeRequiredDefaultDescription
idstringYes--Unique command ID (e.g., "my-ext.runLinter").
labelstringNoLast segment of idDisplay label in the command palette.
descriptionstringNo""Short description shown alongside the command.
categorystringNo"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.

PropertyTypeRequiredDefaultDescription
idstringYes--Unique theme ID.
labelstringYes--Display name in the theme picker.
typestringYes--Must be "dark" or "light".
appColorsobjectNonullMap of app color token names to hex values.
editorColorsobjectNonullMap of editor color token names to hex values.
tokenColorsobjectNonullMap 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.

PropertyTypeRequiredDefaultDescription
idstringYes--Unique setting ID.
keystringNo"{extensionId}.{id}"Storage key for the setting value.
labelstringNoValue of idDisplay label in the settings UI.
descriptionstringNo""Help text shown below the setting.
typestringNo"string"Data type: "string", "boolean", "number", or "enum".
defaultanyNonullDefault value for the setting.
enumValuesstring[]NonullAllowed 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.

PropertyTypeRequiredDefaultDescription
idstringYes--Unique drawer icon ID.
labelstringYes--Tooltip / label text.
iconstringYes--Material icon name (e.g., "science", "terminal").
commandIdstringNonullCommand to execute when tapped.
htmlstringNonullHTML content to render in a webview panel.
urlstringNonullURL to load in a webview panel.
priorityintNo100Sort 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.

PropertyTypeRequiredDefaultDescription
idstringYes--Unique status bar item ID.
labelstringYes--Text displayed in the status bar. Can be updated at runtime via the API.
iconstringNonullMaterial icon name.
commandIdstringYes--Command to execute when the item is tapped.
alignmentstringNo"left"Position in the status bar: "left" or "right".
priorityintNo100Sort 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.

PropertyTypeRequiredDefaultDescription
idstringYes--Unique tools item ID.
labelstringYes--Display label.
iconstringYes--Material icon name.
commandIdstringYes--Command to execute when tapped.
priorityintNo100Sort 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.

PropertyTypeRequiredDefaultDescription
idstringYes--Unique template ID.
labelstringYes--Template display name.
iconstringYes--Material icon name.
commandIdstringYes--Command to execute to create the project.
priorityintNo100Sort 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.

PropertyTypeRequiredDefaultDescription
idstringYes--Unique action ID.
labelstringYes--Menu item label.
iconstringNonullMaterial icon name.
commandIdstringYes--Command to execute. The file path is passed as an argument.
fileExtensionsstring[]NonullFile extensions this action applies to (e.g., [".js", ".ts"]). null means all files.
appliesToFoldersboolNofalseWhether this action appears on folders.
priorityintNo100Sort 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.

PropertyTypeRequiredDefaultDescription
idstringYes--Unique bottom sheet ID.
titlestringYes--Title displayed at the top of the sheet.
htmlstringNonullHTML content to render.
urlstringNonullURL to load in the sheet's WebView.
showTitleboolNotrueWhether to show the title bar.
showCloseButtonboolNotrueWhether to show the close button.
showOpenInTabboolNotrueWhether 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 jsCode that returns a CM6 Extension or Extension[].
  • File-based mode -- Point to a bundled JavaScript file via file. Optional params are forwarded to the factory function exported by the bundle.
PropertyTypeRequiredDefaultDescription
idstringYes--Unique extension ID.
jsCodestringNo""Inline JavaScript code (inline mode).
filestringNonullRelative path to a JS file in the extension bundle (file-based mode).
paramsobjectNonullParameters passed to the file's factory function (file-based mode).
fileExtensionsstring[]NonullFile extensions this applies to. null means all files.
descriptionstringNonullHuman-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.

PropertyTypeRequiredDefaultDescription
idstringYes--Unique formatter ID.
labelstringYes--Display name (e.g., "Prettier", "Black").
commandIdstringYes--Command to execute when formatting is requested.
languagesstring[]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.

PropertyTypeRequiredDefaultDescription
idstringYes--Unique custom editor ID.
labelstringYes--Display name shown in "Open With" menu.
fileExtensionsstring[]Yes[]File extensions this editor handles (e.g. [".pdf", ".png"]).
commandIdstringYes--Command to execute when opening a file. Receives { uri, name } as args.
isDefaultbooleanNofalseIf true, files matching fileExtensions open with this editor by default.
prioritynumberNo100Sort 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"
}
]
}
}

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:

  1. id is required -- Must be a non-empty string. A validation error is thrown if missing or empty.
  2. name is required -- Must be a non-empty string. A validation error is thrown if missing or empty.
  3. id format -- Must match the pattern [a-zA-Z0-9][a-zA-Z0-9_-]*. Invalid characters cause a validation error.
  4. id length -- Must not exceed 128 characters. Longer IDs cause a validation error.
  5. Theme type -- Must be exactly "dark" or "light". Any other value causes a validation error.
  6. 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 is 0.1.0.
  7. contributes parsing -- Non-object contributes values are silently ignored. Within each contribution type, non-object array elements are skipped.