A modern VSCode extension template for building React-powered webviews with Vite, Tailwind CSS, and shadcn/ui.
English | δΈζ
- π Fast Development - Vite with Hot Module Replacement (HMR)
- βοΈ React 19 - Build modern UIs with the latest React
- π¨ shadcn/ui - Beautiful, accessible components built on Radix UI
- π― Tailwind CSS v4 - Utility-first CSS framework
- π RPC Communication - Type-safe communication between webview and extension host
- π¦ TypeScript - Full type safety across the entire codebase
- π Secure CSP - Content Security Policy for both dev and production
- π οΈ ESLint + Prettier - Code quality and formatting
- πΆ Husky + lint-staged - Git hooks for automated checks
βββ src/
β βββ extension.ts # Extension entry point
β βββ getWebviewContent.ts # Webview HTML generation (dev/prod)
β βββ router.ts # RPC router definitions
β βββ webview/ # React webview source
β βββ main.tsx # React entry point
β βββ App.tsx # Main React component
β βββ index.css # Global styles & Tailwind config
β βββ components/ # React components
β β βββ ui/ # shadcn/ui components
β βββ context/ # React contexts
β βββ lib/ # Utility functions
β βββ assets/ # Static assets
βββ assets/ # Extension assets (icons)
βββ dist/ # Build output
β βββ extension.js # Bundled extension
β βββ webview/ # Built webview
βββ esbuild.config.mjs # Extension bundler config
βββ vite.config.ts # Webview bundler config
βββ tsconfig.json # Extension TypeScript config
βββ tsconfig.webview.json # Webview TypeScript config
βββ components.json # shadcn/ui configuration
βββ package.json # Project manifest
- Node.js v18+
- Visual Studio Code
- Git
# Clone the repository
git clone <repository-url>
cd parsec-react-webview
# Install dependencies
npm install
# Initialize Husky (git hooks)
npx husky init-
Start the Vite dev server:
npm run dev:webview
-
Launch the extension in VSCode:
- Press
F5to open Extension Development Host - Click the π¬ icon in the activity bar
- Press
-
Start coding:
- Edit files in
src/webview/ - Changes will hot-reload automatically
- Edit files in
# Build webview
npm run build:webview
# Build extension
npm run build
# Build both (for packaging)
npm run vscode:prepublish# Package as .vsix
npm run package
# Install in VSCode
code --install-extension parsec-react-webview-*.vsix| Script | Description |
|---|---|
npm run dev:webview |
Start Vite dev server with HMR |
npm run build:webview |
Build webview for production |
npm run build |
Build extension with esbuild |
npm run compile |
TypeScript type checking |
npm run lint |
Run ESLint |
npm run format |
Format code with Prettier |
npm run format:check |
Check code formatting |
npm run package |
Package as .vsix file |
npm run watch |
Watch mode for extension |
src/extension.ts- Registers the webview provider and commandssrc/router.ts- Defines RPC procedures callable from the webviewsrc/getWebviewContent.ts- Generates HTML for dev/prod modes
src/webview/main.tsx- React entry point with providerssrc/webview/App.tsx- Main application componentsrc/webview/wrpc.ts- RPC client configuration
The webview communicates with the extension host via @webview-rpc:
// Webview (src/webview/wrpc.ts)
export const wrpc = withReactQuery<AppRouter>(createWrpcClient<AppRouter>());
// Usage in components
const { data } = wrpc.useQuery('getWorkspaceName');
const mutation = wrpc.useMutation('showAlert');// Extension Host (src/router.ts)
export const appRouter = router({
getWorkspaceName: procedure.resolve(() => {
return vscode.workspace.name || 'No workspace open';
}),
showAlert: procedure.input(z.string()).resolve(({ input }) => {
vscode.window.showInformationMessage(input);
return true;
}),
});Components are configured in components.json:
{
"style": "base-maia",
"rsc": false,
"tsx": true,
"tailwind": {
"css": "src/webview/index.css",
"baseColor": "neutral",
"cssVariables": true
},
"aliases": {
"components": "@/webview/components",
"ui": "@/webview/components/ui",
"lib": "@/webview/lib",
"utils": "@/webview/lib/utils"
}
}Add components:
npx shadcn@latest add button
npx shadcn@latest add cardTheme colors are defined in src/webview/index.css using CSS variables:
:root {
--primary: oklch(0.205 0 0);
--primary-foreground: oklch(0.985 0 0);
/* ... */
}
.dark {
--primary: oklch(0.922 0 0);
--primary-foreground: oklch(0.205 0 0);
/* ... */
}Configured in tsconfig.json and vite.config.ts:
{
"paths": {
"@/*": ["./src/*"]
}
}Usage:
import { Button } from '@/webview/components/ui/button';
import { cn } from '@/webview/lib/utils';# Check for issues
npm run lint
# Format code
npm run formatHusky runs lint-staged on pre-commit:
*.{ts,tsx}β ESLint fix + Prettier format*.{json,css,md}β Prettier format
- Content Security Policy (CSP) configured for both dev and production
- Scripts are nonce-protected in production
- Dev server connections are restricted to localhost