contributesThis chapter demonstrates how to use the contributes
field to extend the user interface of VSCode and declaratively integrate
your extension’s functionality into the editor. The focus is on the most
relevant contribution points for typical extension scenarios.
contributesThe contributes field in package.json is
the central interface between your extension and the VSCode editor.
Here, you declare which UI elements, commands, and functionalities your
extension provides without having to implement them immediately.
VSCode reads these declarations at startup and builds the corresponding UI elements — the actual extension logic is only loaded when needed. This approach ensures fast editor startups and a consistent user interface.
| Contribution Point | Purpose | Typical Use |
|---|---|---|
commands |
Defines executable commands | Command Palette, menu actions |
menus |
Binds commands to UI positions | Context menus, editor toolbar |
configuration |
Enables user settings | Extension-specific settings |
views |
Adds custom UI areas | TreeViews, custom panels |
languages |
Registers file types | New file extensions |
grammars |
Links syntax highlighting | TextMate-based highlighting |
keybindings |
Defines keyboard shortcuts | Direct command execution |
Commands are the fundamental actions of your extension. Each command requires a unique ID and a display title:
{
"contributes": {
"commands": [
{
"command": "textAnalyzer.analyzeDocument",
"title": "Analyze Document",
"category": "Text Analyzer"
},
{
"command": "textAnalyzer.showStatistics",
"title": "Show Word Count",
"icon": "$(graph)"
}
]
}
}The implementation takes place in the activate
function:
export function activate(context: vscode.ExtensionContext) {
const analyzeCommand = vscode.commands.registerCommand(
'textAnalyzer.analyzeDocument',
() => {
const editor = vscode.window.activeTextEditor;
if (editor) {
const wordCount = editor.document.getText().split(/\s+/).length;
vscode.window.showInformationMessage(`Words: ${wordCount}`);
}
}
);
context.subscriptions.push(analyzeCommand);
}Commands are initially only accessible via the Command Palette. Menus bind them to concrete UI positions:
{
"contributes": {
"menus": {
"editor/context": [
{
"command": "textAnalyzer.analyzeDocument",
"when": "editorTextFocus",
"group": "analyze"
}
],
"editor/title": [
{
"command": "textAnalyzer.showStatistics",
"when": "resourceExtname == .txt"
}
]
}
}
}The when condition controls visibility. Common
conditions include editorTextFocus,
resourceExtname, or editorLangId.
Extensions can provide their own configuration options, which users
can adjust in their settings.json:
{
"contributes": {
"configuration": {
"title": "Text Analyzer",
"properties": {
"textAnalyzer.includeWhitespace": {
"type": "boolean",
"default": false,
"description": "Include whitespace in word count"
},
"textAnalyzer.outputFormat": {
"type": "string",
"enum": ["json", "text"],
"default": "text",
"description": "Format for analysis output"
}
}
}
}
}Access in code is done via the workspace configuration:
function getSettings() {
const config = vscode.workspace.getConfiguration('textAnalyzer');
return {
includeWhitespace: config.get<boolean>('includeWhitespace', false),
outputFormat: config.get<string>('outputFormat', 'text')
};
}Views enable persistent UI elements in the sidebar or panel:
{
"contributes": {
"views": {
"explorer": [
{
"id": "textAnalyzer.results",
"name": "Analysis Results",
"when": "textAnalyzer.hasResults"
}
]
}
}
}The simplest implementation shows static content:
class ResultsProvider implements vscode.TreeDataProvider<string> {
private results: string[] = [];
getTreeItem(element: string): vscode.TreeItem {
return new vscode.TreeItem(element);
}
getChildren(): string[] {
return this.results;
}
updateResults(newResults: string[]) {
this.results = newResults;
this._onDidChangeTreeData.fire(undefined);
}
private _onDidChangeTreeData = new vscode.EventEmitter<string | undefined>();
readonly onDidChangeTreeData = this._onDidChangeTreeData.event;
}
// Registration in activate()
const provider = new ResultsProvider();
vscode.window.registerTreeDataProvider('textAnalyzer.results', provider);For new file formats, you can register languages and syntax highlighting:
{
"contributes": {
"languages": [
{
"id": "customlog",
"extensions": [".clog"],
"aliases": ["Custom Log"]
}
],
"keybindings": [
{
"command": "textAnalyzer.analyzeDocument",
"key": "ctrl+shift+a",
"when": "editorTextFocus"
}
]
}
}A typical extension combines multiple contribution points:
A text analysis extension, for example, would define commands for various analyses, make them available via editor menus, offer configuration options for details, and display results in a TreeView.
Frequent issues:
contributes must exactly match the code registrationwhen conditions: Commands
without when clause appear in inappropriate contextscategory names for better discoverabilityBest practices:
extensionName.actionNameThe complete reference for all contribution points can be found in the official VSCode documentation.