Creating a VSCode Extension Sidebar Panel: Complete Implementation Guide
Your VSCode extension compiles but the sidebar panel doesn't appear - this is one of the most common issues in extension development. Based on extensive research of recent VSCode APIs and working implementations, here's a comprehensive guide to solve your problem and create a fully functional sidebar panel.
The critical missing piece in most implementations
The most frequent cause of invisible panels is missing TreeDataProvider registration in the extension's activate function. Even with perfect package.json configuration, VSCode won't display your panel without proper provider registration.
Complete minimal working example
Here's a fully functional implementation that addresses all your requirements:
1. Package.json configuration
{
"name": "review-sidebar",
"displayName": "Review Sidebar",
"version": "0.0.1",
"engines": {
"vscode": "^1.74.0"
},
"categories": ["Other"],
"activationEvents": [],
"main": "./out/extension.js",
"contributes": {
"views": {
"explorer": [
{
"id": "reviewContent",
"name": "Review Content",
"icon": "$(book)",
"contextualTitle": "Review Panel"
}
]
},
"commands": [
{
"command": "reviewContent.refresh",
"title": "Refresh",
"icon": "$(refresh)"
}
],
"menus": {
"view/title": [
{
"command": "reviewContent.refresh",
"when": "view == reviewContent",
"group": "navigation"
}
]
}
},
"scripts": {
"vscode:prepublish": "npm run compile",
"compile": "tsc -p ./",
"watch": "tsc -watch -p ./"
},
"devDependencies": {
"@types/vscode": "^1.74.0",
"@types/node": "16.x",
"typescript": "^4.9.4"
}
}
Key configuration points:
- For VSCode 1.74.0+, leave
activationEvents
empty - views automatically trigger activation - The
id
in views must exactly match the ID used inregisterTreeDataProvider()
- Adding to
"explorer"
places your panel in the file explorer sidebar
2. TreeDataProvider implementation (src/reviewProvider.ts)
import * as vscode from 'vscode';
export class ReviewItem extends vscode.TreeItem {
constructor(
public readonly label: string,
public readonly collapsibleState: vscode.TreeItemCollapsibleState,
public readonly content?: string,
public readonly children?: ReviewItem[]
) {
super(label, collapsibleState);
// Set tooltip and description
this.tooltip = `${this.label}`;
this.description = this.content ? 'Has content' : '';
// Add click command for items with content
if (this.content) {
this.command = {
command: 'reviewContent.showItem',
title: 'Show Content',
arguments: [this]
};
}
// Use built-in icons
this.iconPath = this.children
? new vscode.ThemeIcon('folder')
: new vscode.ThemeIcon('file-text');
}
}
export class ReviewContentProvider implements vscode.TreeDataProvider<ReviewItem> {
private _onDidChangeTreeData: vscode.EventEmitter<ReviewItem | undefined | null | void> =
new vscode.EventEmitter<ReviewItem | undefined | null | void>();
// This exact property name is required by VSCode
readonly onDidChangeTreeData: vscode.Event<ReviewItem | undefined | null | void> =
this._onDidChangeTreeData.event;
private reviewData: ReviewItem[] = [];
constructor() {
this.loadReviewData();
}
getTreeItem(element: ReviewItem): vscode.TreeItem {
return element;
}
getChildren(element?: ReviewItem): Thenable<ReviewItem[]> {
if (!element) {
// Return root elements
return Promise.resolve(this.reviewData);
}
// Return children of the element
return Promise.resolve(element.children || []);
}
refresh(): void {
this.loadReviewData();
this._onDidChangeTreeData.fire();
}
private loadReviewData(): void {
// Example hierarchical review content
this.reviewData = [
new ReviewItem(
'Chapter 1: Introduction',
vscode.TreeItemCollapsibleState.Expanded,
undefined,
[
new ReviewItem('Overview', vscode.TreeItemCollapsibleState.None, 'Introduction content...'),
new ReviewItem('Key Concepts', vscode.TreeItemCollapsibleState.None, 'Main concepts...'),
]
),
new ReviewItem(
'Chapter 2: Implementation',
vscode.TreeItemCollapsibleState.Collapsed,
undefined,
[
new ReviewItem('Setup', vscode.TreeItemCollapsibleState.None, 'Setup instructions...'),
new ReviewItem('Code Examples', vscode.TreeItemCollapsibleState.None, 'Example code...'),
]
),
new ReviewItem('Summary', vscode.TreeItemCollapsibleState.None, 'Summary content...')
];
}
}
3. Extension activation (src/extension.ts)
import * as vscode from 'vscode';
import { ReviewContentProvider, ReviewItem } from './reviewProvider';
export function activate(context: vscode.ExtensionContext) {
console.log('Review extension is activating...');
// Create provider instance
const reviewProvider = new ReviewContentProvider();
// CRITICAL: Register the tree data provider
const treeView = vscode.window.createTreeView('reviewContent', {
treeDataProvider: reviewProvider,
showCollapseAll: true
});
// Register refresh command
const refreshCommand = vscode.commands.registerCommand('reviewContent.refresh', () => {
reviewProvider.refresh();
vscode.window.showInformationMessage('Review content refreshed');
});
// Register item click handler
const showItemCommand = vscode.commands.registerCommand('reviewContent.showItem', (item: ReviewItem) => {
if (item.content) {
// Option 1: Show in output channel
const outputChannel = vscode.window.createOutputChannel('Review Content');
outputChannel.clear();
outputChannel.appendLine(item.content);
outputChannel.show();
// Option 2: Show in new editor (for markdown content)
// const doc = await vscode.workspace.openTextDocument({
// content: item.content,
// language: 'markdown'
// });
// vscode.window.showTextDocument(doc);
}
});
// Add to subscriptions for proper cleanup
context.subscriptions.push(treeView, refreshCommand, showItemCommand);
console.log('Review extension activated successfully');
}
export function deactivate() {
console.log('Review extension deactivated');
}
Debugging your non-appearing panel
Follow these steps to diagnose why your panel isn't showing:
1. Verify extension activation
Press F5
to launch the Extension Development Host, then check the Debug Console:
// Add to activate function
console.log('Extension activating...', context.extensionPath);
2. Check registration success
const treeView = vscode.window.createTreeView('reviewContent', {
treeDataProvider: reviewProvider
});
console.log('TreeView registered:', treeView.visible);
3. Common issues and solutions
Panel not appearing:
- Ensure view ID matches exactly between package.json and registration code
- Check that TreeDataProvider is registered synchronously in activate()
- Verify
getChildren()
returns data for root elements
Extension not activating:
- For VSCode <1.74.0, add explicit activation:
"activationEvents": ["onView:reviewContent"]
- Check for errors in activate() function using try-catch blocks
- Ensure
main
points to correct compiled output file
Build/bundle issues:
- Verify TypeScript compilation succeeds:
npm run compile
- Check that out/extension.js exists after compilation
- Ensure @types/vscode version matches engines.vscode
4. Developer tools debugging
- In Extension Development Host:
Help → Toggle Developer Tools
- Check Console tab for errors
- Use
Developer: Show Running Extensions
to verify your extension loaded
Alternative approach: Webview for rich content
If you need to display formatted markdown or complex layouts, consider a webview panel instead:
export function createReviewWebview(context: vscode.ExtensionContext) {
const panel = vscode.window.createWebviewPanel(
'reviewWebview',
'Review Content',
vscode.ViewColumn.Beside,
{
enableScripts: true,
retainContextWhenHidden: true
}
);
panel.webview.html = `
<!DOCTYPE html>
<html>
<head>
<style>
body { font-family: var(--vscode-font-family); }
h2 { color: var(--vscode-editor-foreground); }
</style>
</head>
<body>
<h2>Review Content</h2>
<div id="content">
<!-- Your formatted content here -->
</div>
</body>
</html>
`;
return panel;
}
Best practices for sidebar extensions
Performance optimization:
- Bundle your extension using esbuild for faster loading
- Lazy-load data only when tree nodes expand
- Use
TreeItemCollapsibleState.Collapsed
for large datasets
User experience:
- Provide refresh functionality for dynamic content
- Use VSCode's built-in theme icons for consistency
- Add context menus for item-specific actions
Maintainability:
- Separate data models from UI providers
- Use TypeScript interfaces for type safety
- Implement proper disposal in deactivate()
Quick troubleshooting checklist
When your panel doesn't appear, verify:
- ✓ View ID matches exactly in package.json and code
- ✓ TreeDataProvider is registered in activate()
- ✓ getChildren() returns data for root elements
- ✓ Extension compiles without errors
- ✓ Correct activation events for your VSCode version
- ✓ No exceptions thrown in activate() function
This implementation provides a solid foundation for your review content sidebar panel. The hierarchical structure works well for organized content, and you can extend it with features like search, filtering, or integration with external data sources as needed.