Master VS Code Extension Development: Electron Core & Webview Communication

This guide walks you through the fundamentals of VS Code extension development, covering Electron’s core technologies, the architecture of main and renderer processes, command registration, creating Webview panels with React and Webpack, and implementing robust two‑way communication between the Webview and the extension’s Node side.

ELab Team
ELab Team
ELab Team
Master VS Code Extension Development: Electron Core & Webview Communication
Previous context : Some developers find IDL complex compared to Swagger, so they explored VS Code extension development and Flycode architecture, gaining experience to help others get started quickly.

When discussing VS Code, we must mention Electron, whose core technologies are three points:

Chromium: UI built with web technologies, runs on the Chrome engine.

Node.js: Handles file system and network communication.

Native API: Node addons (C++ dynamic libraries) extend Node capabilities to call OS APIs.

Electron also features a multi‑process model; the two most important processes are:

Main process : Only one per application; creates GUI‑related interfaces.

Renderer process : Each web page runs in its own renderer process, created via Chromium API.

In an Electron app, web pages can forward messages to the main process via the renderer process, which then calls native OS APIs, offering more flexible and rich extension capabilities than ordinary web apps.

Understanding VS Code’s underlying design, we now explore VS Code extension development step by step.

Requirement Analysis

When right‑clicking a folder in the VS Code explorer, open a visual interface, configure it, and quickly create a micro‑frontend sub‑application in its sub‑directory.

From this requirement we derive several VS Code‑related functionalities:

Register a command in the VS Code command system and add it to the menu.

Create a web page for configuration.

Close the web page after configuration.

Logic Implementation

Register Command

After initializing a plugin project, the outermost file exports two lifecycle methods, activate and deactivate. Activation events declared in package.json cause VS Code to call activate at the appropriate time, conserving resources.

// package.json
"activationEvents": [
  "onCommand:fly-code.newSubProject",
  ...
],
"commands": [
  {
    "command": "fly-code.newSubProject",
    "title": "新建子项目"
  },
  ...
]

We can register the command when the extension is activated:

import { newProjectCommand } from './commands/new-project';

export function activate(context: vscode.ExtensionContext) {
  vscode.commands.registerCommand('fly-code.newSubProject', (info: any) => {
    newProjectCommand(context, info.path);
  });
}

The command fly-code.newSubProject is bound to newProjectCommand, where the actual implementation resides.

Create Webview

To create a page, use VS Code’s API vscode.window.createWebviewPanel:

export function newProjectCommand(
  context: vscode.ExtensionContext,
  dirPath: string,
) {
  const panel = vscode.window.createWebviewPanel(
    'newPage',               // viewType
    '新建项目',               // title
    vscode.ViewColumn.One,   // show column
    { enableScripts: true, retainContextWhenHidden: true }
  );
  // ...
}

The HTML content is set via the html property, which expects a string. To use React, bundle the React code into a JS file and inject it into the HTML template.

panel.webview.html = getWebviewContent(context, 'project.js');
Render the appropriate component → corresponding JS file.

The helper getWebviewContent builds the full HTML page and references local resources via the vscode-resource: scheme.

function getWebviewContent(context: vscode.ExtensionContext, page: string) {
  const resourcePath = path.join(context.extensionPath, './dist/webview/', page);
  const { getHTMLLinks, getHTMLDependencies } = useWebviewBasic(context);
  return `
    <!DOCTYPE html>
    <html>
      <head>
        <meta charset="UTF-8" />
        <title>fly-code!</title>
        ${getHTMLLinks()}
      </head>
      <style>
        body { background-color: transparent !important; }
      </style>
      <body>
        <div id="root"></div>
        ${getHTMLDependencies()}
        <script src="vscode-resource:${resourcePath}"></script>
      </body>
    </html>
  `;
}
Webviews run in an isolated context and cannot directly access local files; they must use the vscode-resource: scheme.

React and Webpack

The UI can be built like a regular React project.

// web/src/pages/project/index.tsx
const Template: React.FC = () => {
  const [loading, setLoading] = useState(false);
  // ...
  return (
    <Spin spinning={loading} tip={loadingText}>
      <div className="template">
        {/* ... */}
      </div>
    </Spin>
  );
};

ReactDOM.render(<Template />, document.getElementById('root'));

Webpack uses multiple entry points for different commands and externalizes common libraries (React, ReactDOM, Antd) to load them via CDN.

const config = {
  mode: env.production ? 'production' : 'development',
  entry: {
    template: createPageEntry('page-template'),
    layout: createPageEntry('page-layout'),
    view: createPageEntry('view-idl'),
    // ...
  },
  output: {
    filename: '[name].js',
    path: path.resolve(__dirname, '../dist/webview'),
  },
  externals: {
    'react': 'root React',
    'react-dom': 'root ReactDOM',
    'antd': 'antd',
  },
};

Process Communication

After the form page is built, the extension needs to invoke OS APIs via the Node process. Communication between the Webview (HTML/JS) and the extension’s Node side uses the VS Code API acquireVsCodeApi.

Extension sends messages with panel.webview.postMessage.

Webview receives messages via window.addEventListener('message', …).

Webview sends messages using the acquired API’s postMessage method.

Extension receives messages with panel.webview.onDidReceiveMessage.

To manage messages more conveniently, a wrapper can be built.

export function sendMessageToVsCode({ type, data }) {
  const listeners = new Set();
  const message = { type, data, id: getRandomId() };
  vscode.postMessage({ text: JSON.stringify(message) });

  function handleResponse(event) {
    if (event.data.id === message.id) {
      listeners.forEach(l => { try { l(event.data); } catch (e) { console.error(e); } });
    }
  }

  window.addEventListener('message', handleResponse);

  return {
    listen(listener) { listeners.add(listener); },
    dispose() { listeners.clear(); window.removeEventListener('message', handleResponse); }
  };
}

export function sendRequestToVsCode(type, data) {
  return new Promise((resolve, reject) => {
    const handler = sendMessageToVsCode({ type, data });
    const timeout = setTimeout(() => { reject(Error('timeout')); handler.dispose(); }, 10000);
    handler.listen(res => { resolve(res.data); handler.dispose(); clearTimeout(timeout); });
  });
}

On the Node side, messages are parsed and routed based on their type:

panel.webview.onDidReceiveMessage(message => {
  try {
    const { type, id, data } = JSON.parse(message.text);
    switch (type) {
      case MsgTypes.CREATE_PROJECT:
        // ...
        break;
      case MsgTypes.FETCH_TEMPLATE_CONFIG:
        fetchMaterialConfig(data).then(res => {
          panel.webview.postMessage({ id, type: MsgTypes.FETCH_TEMPLATE_CONFIG, data: res });
        });
        break;
      default:
        break;
    }
  } catch (e) {
    outputChannel.error(`newProject: ${e}`);
  }
}, undefined, context.subscriptions);

Styling

VS Code supports light and dark themes. Styles can adapt using CSS variables provided by VS Code (e.g., var(--vscode-sideBar-background)) or by targeting theme‑specific classes ( .vscode-light, .vscode-dark).

With these techniques, you can build a fully functional VS Code extension that leverages Electron, React, Webpack, and robust two‑way communication.

Original Source

Signed-in readers can open the original source through BestHub's protected redirect.

Sign in to view source
Republication Notice

This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactadmin@besthub.devand we will review it promptly.

ReactNode.jsVS CodeExtension Development
ELab Team
Written by

ELab Team

Sharing fresh technical insights

0 followers
Reader feedback

How this landed with the community

Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.