1- # react-vscode-webview-ipc
1+ # React + VSCode Webview = IPC
2+
3+ [ ![ npm version] ( https://badge.fury.io/js/react-vscode-webview-ipc.svg )] ( https://www.npmjs.com/package/react-vscode-webview-ipc )
4+ [ ![ Ask DeepWiki] ( https://deepwiki.com/badge.svg )] ( https://deepwiki.com/hbmartin/react-vscode-webview-ipc )
5+ [ ![ CI] ( https://github.com/hbmartin/react-vscode-webview-ipc/actions/workflows/ci.yml/badge.svg )] ( https://github.com/hbmartin/react-vscode-webview-ipc/actions/workflows/ci.yml )
6+ [ ![ NPM License] ( https://img.shields.io/npm/l/react-vscode-webview-ipc?color=blue )] ( https://github.com/hbmartin/react-vscode-webview-ipc/blob/main/LICENSE.txt )
27
38A small library to make two-way communication between a VS Code extension host and a React webview simple and type‑safe.
49
510Two complementary paradigms are supported (you can use one or both):
11+
612- UDF reducer IPC: Dispatch actions from the webview; the host computes a patch; the webview applies it via a reducer. (unidirectional dataflow)
713- RPC promises IPC: Call host functions from the webview and await typed results; the host can also push typed events to all connected webviews.
814
915This README explains how to implement both and shows how they can coexist.
1016
11-
1217## Install
1318
1419- Install the package in your VS Code extension project.
@@ -21,7 +26,6 @@ npm i react-vscode-webview-ipc
2126 - ` react-vscode-webview-ipc/host ` for your extension host code
2227 - ` react-vscode-webview-ipc/client ` for your React webview code
2328
24-
2529## Concepts Overview
2630
2731- WebviewKey: a branded string identifying your view instance. Use a stable id (often your view type).
@@ -30,12 +34,12 @@ npm i react-vscode-webview-ipc
3034 - RPC IPC: ` { type: 'request' } ` from webview → host; ` { type: 'response'|'error' } ` from host → webview; ` { type: 'event' } ` from host → webview broadcast.
3135- Logging: webview logs are forwarded to the host’s Output channel.
3236
33-
3437## UDF Reducer IPC (Action → Patch → Reduce)
3538
3639Use this when your webview wants unidirectional state updates managed via a reducer.
3740
3841### Types and Building Blocks
42+
3943- On the webview:
4044 - ` useVscodeState<S, A>(vscode, providerId, postReducer, initialState) ` returns ` [state, actor] ` .
4145 - ` state: S ` – your current state
@@ -51,6 +55,7 @@ Use this when your webview wants unidirectional state updates managed via a redu
5155### Minimal Example
5256
5357Host (extension):
58+
5459``` ts
5560// src/extension/MyViewProvider.ts
5661import * as vscode from ' vscode' ;
@@ -109,6 +114,7 @@ export class MyViewProvider extends BaseWebviewViewProvider<MyActions> {
109114```
110115
111116Register the provider in your extension activation:
117+
112118``` ts
113119// src/extension/activate.ts
114120import * as vscode from ' vscode' ;
@@ -117,25 +123,31 @@ import { MyViewProvider } from './MyViewProvider';
117123export function activate(context : vscode .ExtensionContext ) {
118124 const viewType = ' myExtension.myView' as unknown as WebviewKey ; // brand to WebviewKey
119125 const provider = new MyViewProvider (viewType , context );
120- context .subscriptions .push (
121- vscode .window .registerWebviewViewProvider (viewType , provider )
122- );
126+ context .subscriptions .push (vscode .window .registerWebviewViewProvider (viewType , provider ));
123127}
124128```
125129
126130Webview (React):
131+
127132``` tsx
128133// src/webview/App.tsx
129134import { useMemo } from ' react' ;
130- import { useVscodeState , type StateReducer , type WebviewKey } from ' react-vscode-webview-ipc/client' ;
135+ import {
136+ useVscodeState ,
137+ type StateReducer ,
138+ type WebviewKey ,
139+ } from ' react-vscode-webview-ipc/client' ;
131140
132141declare function acquireVsCodeApi(): {
133142 postMessage(message : unknown ): Thenable <boolean >;
134143 getState(): unknown ;
135144 setState(state : unknown ): void ;
136145};
137146
138- interface State { count: number ; message: string }
147+ interface State {
148+ count: number ;
149+ message: string ;
150+ }
139151interface MyActions {
140152 increment: (by : number ) => number ;
141153 setMessage: (msg : string ) => { message: string };
@@ -165,6 +177,7 @@ export default function App() {
165177```
166178
167179### UDF Flow (Sequence)
180+
168181``` mermaid
169182sequenceDiagram
170183 participant W as Webview (React)
@@ -178,12 +191,12 @@ sequenceDiagram
178191 W->>W: postReducer[key](prev, patch) => newState
179192```
180193
181-
182194## RPC Promises IPC (Typed Requests/Responses + Events)
183195
184196Use this when your webview needs to call host functions and await results. The host can also broadcast typed events back to all connected webviews.
185197
186198### Types and Building Blocks
199+
187200- On the webview:
188201 - Wrap your app in ` <WebviewProvider viewType contextKey> ` .
189202 - Use ` createCtxKey<T>() ` to create a unique key tying the context to your API type ` T ` .
@@ -198,6 +211,7 @@ Use this when your webview needs to call host functions and await results. The h
198211### Minimal Example
199212
200213Shared types:
214+
201215``` ts
202216// Host receives these requests from the webview (must return promises)
203217import type { ClientCalls } from ' react-vscode-webview-ipc/client' ;
@@ -215,6 +229,7 @@ export interface MyHostEvents extends HostCalls {
215229```
216230
217231Host (extension):
232+
218233``` ts
219234import * as vscode from ' vscode' ;
220235import {
@@ -253,7 +268,9 @@ export class MyRpcViewProvider extends BaseWebviewViewProvider<{}> {
253268 const [name] = message .params ;
254269 const value = ` Hello, ${name }! ` ;
255270 const response: ViewApiResponse <MyClientApi , ' fetchGreeting' > = {
256- type: ' response' , id: message .id , value ,
271+ type: ' response' ,
272+ id: message .id ,
273+ value ,
257274 };
258275 await webview .postMessage (response );
259276 return ;
@@ -262,19 +279,24 @@ export class MyRpcViewProvider extends BaseWebviewViewProvider<{}> {
262279 const [count] = message .params ;
263280 // persist count...
264281 const response: ViewApiResponse <MyClientApi , ' saveCount' > = {
265- type: ' response' , id: message .id ,
282+ type: ' response' ,
283+ id: message .id ,
266284 };
267285 await webview .postMessage (response );
268286 return ;
269287 }
270288 }
271289 const error: ViewApiError = {
272- type: ' error' , id: message .id , value: ` Unknown method: ${String (message .key )} ` ,
290+ type: ' error' ,
291+ id: message .id ,
292+ value: ` Unknown method: ${String (message .key )} ` ,
273293 };
274294 await webview .postMessage (error );
275295 } catch (e ) {
276296 const error: ViewApiError = {
277- type: ' error' , id: message .id , value: e instanceof Error ? e .message : String (e ),
297+ type: ' error' ,
298+ id: message .id ,
299+ value: e instanceof Error ? e .message : String (e ),
278300 };
279301 await webview .postMessage (error );
280302 }
@@ -287,6 +309,7 @@ export class MyRpcViewProvider extends BaseWebviewViewProvider<{}> {
287309```
288310
289311Webview (React):
312+
290313``` tsx
291314import React , { useEffect } from ' react' ;
292315import {
@@ -316,7 +339,7 @@ function Inner() {
316339 })();
317340 }, [api ]);
318341
319- return <div />
342+ return <div />;
320343}
321344
322345export default function App() {
@@ -329,6 +352,7 @@ export default function App() {
329352```
330353
331354### RPC Flow (Sequence)
355+
332356``` mermaid
333357sequenceDiagram
334358 participant W as Webview (React)
@@ -347,7 +371,6 @@ sequenceDiagram
347371 VS-->>W: Invoke registered listeners for key
348372```
349373
350-
351374## Using Both Paradigms Together
352375
353376- They are designed to coexist. The webview can dispatch reducer actions for state, and call RPC methods for imperative operations.
@@ -356,20 +379,19 @@ sequenceDiagram
356379 - ` WebviewProvider ` listens for ` { type: 'response'|'error'|'event' } ` messages and ignores messages with ` providerId ` present.
357380- In your host provider, ` resolveWebviewView ` (from the base class) handles reducer ` act/patch ` automatically; implement ` handleMessage ` for RPC requests.
358381
359-
360382## Logging
361383
362384- Webview: ` useLogger(tag, vscode) ` returns a logger that posts to the host output channel.
363385- Host: ` getLogger(tag) ` returns an Output channel logger; ` BaseWebviewViewProvider ` automatically routes webview log messages to it.
364386
365387Webview example:
388+
366389``` ts
367390import { useLogger } from ' react-vscode-webview-ipc/client' ;
368391const logger = useLogger (' MyView' , acquireVsCodeApi ());
369392logger .info (' hello' );
370393```
371394
372-
373395## Tips & Troubleshooting
374396
375397- Brand your view type to ` WebviewKey ` at the edges to keep types happy: ` const id = 'ext.view' as unknown as WebviewKey ` .
@@ -378,23 +400,23 @@ logger.info('hello');
378400- When posting RPC responses/errors from the host, always echo the same ` id ` you received.
379401- If you use both paradigms, keep your reducer patches focused on state updates and use RPC for IO or long‑running tasks.
380402
381-
382403## API Surface (Quick Reference)
383404
384405Host exports (` react-vscode-webview-ipc/host ` ):
406+
385407- ` BaseWebviewViewProvider<A> `
386408- ` WebviewApiProvider<T extends HostCalls> `
387409- ` isViewApiRequest(message) `
388410- ` Logger ` , ` getLogger ` , ` disallowedLogKeys `
389411
390412Client exports (` react-vscode-webview-ipc/client ` ):
413+
391414- ` WebviewProvider<T extends ClientCalls> `
392415- ` useWebviewApi(ctxKey) ` and ` createCtxKey<T>() `
393416- ` useVscodeState<S, A>(vscode, providerId, postReducer, initial) `
394417- ` useLogger(tag, vscode) `
395418- Types: ` ClientCalls ` , ` HostCalls ` , ` CtxKey ` , ` WebviewKey ` , ` StateReducer `
396419
397-
398420## License
399421
400422Apache-2.0
0 commit comments