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 in registerTreeDataProvider()
  • 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

  1. In Extension Development Host: Help → Toggle Developer Tools
  2. Check Console tab for errors
  3. 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:

  1. ✓ View ID matches exactly in package.json and code
  2. ✓ TreeDataProvider is registered in activate()
  3. ✓ getChildren() returns data for root elements
  4. ✓ Extension compiles without errors
  5. ✓ Correct activation events for your VSCode version
  6. ✓ 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.