Protocol for Message Passing Between Webview and VS Code Extension Host in Secure Design

The Secure Design extension uses a simple JSON-based command protocol where the webview calls vscode.postMessage() to send commands to the extension host, which processes requests via onDidReceiveMessage and responds through webview.postMessage().

The hbmartin/secure-design repository implements a bidirectional communication layer between its React-based webview UI (rendering the Chat sidebar and Canvas) and the TypeScript extension host backend. This protocol enables the UI to request file operations, trigger LLM prompts, and synchronize state while keeping the webview sandboxed according to VS Code security architecture.

How the Message Protocol Works

Webview to Extension Host (Outbound)

The webview obtains a reference to the VS Code API by calling const vscode = acquireVsCodeApi(); at initialization. When the UI requires backend action—such as loading design files or submitting a prompt—it constructs a plain JavaScript object containing a command property and posts it via vscode.postMessage(message).

In src/webview/components/CanvasView.tsx, eight distinct postMessage calls dispatch commands including:

  • loadDesignFiles – Requests the host to read workspace design files and stream them back
  • selectDesign – Notifies the host that the user selected a specific design element via { command: 'selectDesign', designId: string }
  • clearContext – Signals the host to reset stored LLM context
  • prompt – Transmits user-typed text to the LLM service via { command: 'prompt', text: string }
  • setTheme – Persists theme configuration with { command: 'setTheme', theme: ThemeData }

Extension Host to Webview (Inbound)

The extension host creates a WebviewPanel in src/SuperdesignCanvasPanel.ts and registers an inbound message handler:

this._panel.webview.onDidReceiveMessage(async message => {
  switch (message.command) {
    case 'loadDesignFiles':
      const designs = await this._designService.getAllDesigns();
      this._panel.webview.postMessage({
        type: 'designData',
        designs,
      });
      break;
    // … additional command handlers …
  }
});

After processing, the host responds using this._panel.webview.postMessage(response), where the response object contains a type field identifying the payload category.

Complete Message Flow

  1. User interaction triggers a React component to build a command object and call vscode.postMessage.
  2. Extension host receives it via onDidReceiveMessage, switches on message.command, and executes the requested work (file I/O, LLM calls).
  3. Host responds with webview.postMessage containing a typed payload.
  4. Webview receives the response via window.addEventListener('message', …) in src/webview/index.tsx and updates React state accordingly.

Key Commands and Message Types

Direction Identifier Payload Structure Purpose
Webview → Host loadDesignFiles { command: 'loadDesignFiles' } Request workspace design file enumeration
Webview → Host selectDesign { command: 'selectDesign', designId: string } Notify selection of specific design element
Webview → Host clearContext { command: 'clearContext' } Reset LLM conversation context
Webview → Host prompt { command: 'prompt', text: string } Submit user prompt to LLM service
Webview → Host setTheme { command: 'setTheme', theme: ThemeData } Persist theme configuration
Host → Webview designData { type: 'designData', designs: DesignFile[] } Deliver design file list to render
Host → Webview status { type: 'status', message: string } Display temporary info banner
Host → Webview error { type: 'error', error: string } Propagate exception for user feedback
Host → Webview contextUpdate { type: 'contextUpdate', context: string } Synchronize LLM context after tool execution

Implementation Examples

Posting a Message from the Webview

In src/webview/components/CanvasView.tsx, the UI requests design files:

const vscode = acquireVsCodeApi();
const loadMessage = { command: 'loadDesignFiles' };
vscode.postMessage(loadMessage);

Handling Messages in the Extension Host

From src/SuperdesignCanvasPanel.ts, the host processes the request and replies:

this._panel.webview.onDidReceiveMessage(async message => {
  switch (message.command) {
    case 'loadDesignFiles':
      const designs = await this._designService.getAllDesigns();
      this._panel.webview.postMessage({
        type: 'designData',
        designs,
      });
      break;
    // … other commands …
  }
});

Receiving Responses in the Webview

In src/webview/index.tsx, the React application listens for host responses:

useEffect(() => {
  const listener = (event: MessageEvent) => {
    const msg = event.data;
    if (msg.type === 'designData') {
      setDesigns(msg.designs);
    } else if (msg.type === 'error') {
      showError(msg.error);
    }
    // … handle other types …
  };
  window.addEventListener('message', listener);
  return () => window.removeEventListener('message', listener);
}, []);

Source Files and Architecture

File Role GitHub Link
src/webview/components/CanvasView.tsx React component dispatching commands via vscode.postMessage and rendering design data. https://github.com/hbmartin/secure-design/blob/main/src/webview/components/CanvasView.tsx
src/SuperdesignCanvasPanel.ts Extension host class creating the WebviewPanel, registering onDidReceiveMessage, and posting responses. https://github.com/hbmartin/secure-design/blob/main/src/SuperdesignCanvasPanel.ts
src/webview/index.tsx Webview entry point setting up the global message event listener to bridge VS Code API events into React state. https://github.com/hbmartin/secure-design/blob/main/src/webview/index.tsx
src/webview/components/DesignPanel.tsx Secondary UI component also utilizing vscode.postMessage for design‑specific interactions. https://github.com/hbmartin/secure-design/blob/main/src/webview/components/DesignPanel.tsx

Summary

  • The Secure Design extension implements a JSON command protocol for webview-extension host communication.
  • Outbound messages use acquireVsCodeApi().postMessage() with a command property to trigger host actions like loadDesignFiles or prompt.
  • Inbound messages are captured by onDidReceiveMessage in SuperdesignCanvasPanel.ts, processed, and returned via webview.postMessage() with a type field such as designData or error.
  • The webview listens for responses via window.addEventListener('message', …) in src/webview/index.tsx to update React application state.

Frequently Asked Questions

How does the webview acquire the VS Code API?

The webview calls const vscode = acquireVsCodeApi(); at initialization. This function is injected into the webview global scope by VS Code and provides the postMessage method used to transmit commands to the extension host.

What happens if a message command is not recognized by the extension host?

The extension host’s onDidReceiveMessage handler in SuperdesignCanvasPanel.ts uses a switch statement on message.command. If the command does not match any defined case, the message is silently ignored; no error response is automatically generated unless explicitly implemented in a default case.

Can the protocol handle binary data or file transfers?

The current protocol transmits plain JSON objects. Binary data or large file contents are not sent as raw binary; instead, the extension host reads files using Node.js APIs and transmits the serialized content (e.g., base64 strings or JSON‑serializable objects) within the payload or designs fields of the message structure.

How are errors propagated back to the webview UI?

When the extension host encounters an exception during command processing, it catches the error and responds with a message object containing type: 'error' and an error string property. The webview’s global message listener in src/webview/index.tsx detects this type and routes it to the error display logic, presenting feedback to the user without reloading the webview.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →