react-webview-architecture
Defines architectural conventions and patterns for developing React-based webviews in the documentdb extension.
Install
mkdir -p .claude/skills/react-webview-architecture && curl -L -o skill.zip "https://agentskills.codes/api/skills/download/10783" && unzip -o skill.zip -d .claude/skills/react-webview-architecture && rm skill.zipInstalls to .claude/skills/react-webview-architecture
Activation
This is the description your AI agent reads to decide when to run this skill — the better it matches your request, the more reliably it fires.
Architecture patterns for React-based webviews in the vscode-documentdb extension. Use when creating new webview components, modifying existing views (CollectionView, DocumentView), working with state management (Context API), integrating Fluent UI components, handling Monaco Editor or SlickGrid, solving stale closure bugs with refs, or debugging webview rendering issues. Does NOT cover tRPC messaging (see webview-trpc-messaging skill) or accessibility/ARIA (see accessibility-aria-expert skill).Key capabilities
- →Create webview components
- →Manage state with Context API
- →Debug webview rendering
How it works
It enforces rendering pipelines, context-based state management, and specific patterns for third-party component integration.
Inputs & outputs
When to use react-webview-architecture
- →Create new webview component
- →Manage state with Context API
- →Debug webview rendering
About this skill
React Webview Architecture
Patterns and conventions for React webviews in vscode-documentdb.
Related skills (do not duplicate):
- webview-trpc-messaging — tRPC routers, procedures, telemetry, AbortSignal, subscriptions, WebviewController
- accessibility-aria-expert — ARIA labels, Announcer, focus management, screen reader patterns
Full reference: See references/REACT_ARCHITECTURE_GUIDELINES.md
When to Use
- Creating or modifying a webview (DocumentView, CollectionView)
- Adding new components inside
src/webviews/ - Working with CollectionView context or state management
- Integrating Monaco Editor or SlickGrid
- Debugging stale closure issues in event handlers
Rendering Pipeline
Every webview boots through src/webviews/index.tsx:
root.render(
<DynamicThemeProvider useAdaptive={true}>
<WithWebviewContext vscodeApi={vscodeApi}>
<Component />
</WithWebviewContext>
</DynamicThemeProvider>,
);
DynamicThemeProvider— adapts Fluent UI theming to VS Code's active color themeWithWebviewContext— providesvscodeApi(postMessage) via React ContextWebviewRegistry— maps webview names → React components (in_integration/WebviewRegistry)
Configuration from the extension host is read via useConfiguration<T>().
File Organization
viewName/
├── ViewName.tsx # Main component
├── viewName.scss # Styles
├── viewNameContext.ts # Context + state types (if complex)
├── viewNameController.ts # WebviewController subclass (extension-side)
├── viewNameRouter.ts # tRPC router (extension-side, see webview-trpc-messaging skill)
├── constants.ts
├── components/ # Sub-components
├── hooks/ # Custom React hooks
├── types/ # TypeScript types
└── utils/ # Helpers
Component Hierarchy
DocumentView (simpler, good reference pattern):
DocumentView
├── ProgressBar (conditional: isLoading)
├── ToolbarDocuments
└── MonacoEditor
CollectionView (complex, multi-tab):
CollectionView
├── ProgressBar (conditional)
├── ToolbarMainView
├── QueryEditor
│ └── MonacoAutoHeight (multiple: filter, project, sort)
├── TabList (Results | Query Insights [PREVIEW])
├── Results Tab:
│ ├── ToolbarViewNavigation + ToolbarDocumentManipulation + ViewSwitcher
│ ├── DataViewPanelTable / DataViewPanelTree / DataViewPanelJSON
│ └── ToolbarTableNavigation (Table View only)
└── Query Insights Tab:
└── QueryInsightsMain (3-stage progressive loading)
State Management
Simple views (DocumentView): local useState + props
Complex views (CollectionView): React Context with [state, setState] tuple
export const CollectionViewContext = createContext<
[CollectionViewContextType, React.Dispatch<React.SetStateAction<CollectionViewContextType>>]
>([DefaultCollectionViewContext, () => {}]);
// Provider in parent
const [currentContext, setCurrentContext] = useState(DefaultCollectionViewContext);
<CollectionViewContext.Provider value={[currentContext, setCurrentContext]}>
// Consumer in child
const [currentContext, setCurrentContext] = useContext(CollectionViewContext);
Always use functional updates when state depends on previous value:
setCurrentContext((prev) => ({
...prev,
isLoading: true,
activeQuery: { ...prev.activeQuery, pageNumber: 1 },
}));
Stale Closure Pattern (CRITICAL)
Third-party components (SlickGrid) bind event handlers at initialization — they don't update when state changes. Always use refs to access current data in those handlers:
const dataRef = useRef(data);
useEffect(() => {
dataRef.current = data;
}, [data]);
const onCellDblClick = useCallback((event) => {
const item = dataRef.current[event.detail.args.row]; // ✅ always current
// NOT: data[event.detail.args.row]; ❌ stale closure
}, []); // stable deps only
Why: SlickGrid binds handlers once at init time. Without refs, handlers see the data from initialization, not the latest state. This caused multiple hard-to-debug issues.
Monaco Editor
Required patterns:
- Manual layout — Monaco doesn't auto-resize:
useEffect(() => {
const handler = debounce(() => editorRef.current?.layout(), 200);
window.addEventListener('resize', handler);
handleResize(); // initial layout
return () => window.removeEventListener('resize', handler);
}, []);
- Dispose on unmount:
return () => {
editorRef.current?.dispose();
};
- MonacoAutoHeight — self-sizing editor for query fields:
<MonacoAutoHeight
adaptiveHeight={{ enabled: true, maxLines: 10, minLines: 1, lineHeight: 19 }}
onExecuteRequest={() => onExecuteRequest()}
onMount={(editor, monaco) => handleEditorDidMount(editor, monaco)}
/>
- JSON Schema delay — Monaco's JSON worker may not be ready immediately after mount. An AbortController-guarded delay is used (see QueryEditor for the pattern).
Fluent UI Integration
Use @fluentui/react-components (v9), themed via DynamicThemeProvider:
| Component | Usage |
|---|---|
ProgressBar | Loading states |
Button, ToggleButton | Toolbar actions |
Tab, TabList | View switching |
Dropdown, Option | Selection (ViewSwitcher) |
Badge | Status/preview indicators |
MessageBar | Info/warning messages |
Skeleton, SkeletonItem | Loading placeholders |
Animations: Collapse from @fluentui/react-motion-components-preview
Styling
- Each component gets its own
.scssfile, imported directly - Shared styles in
sharedStyles.scss, applied via@extend - Consistent spacing unit:
10pxwith flexboxrow-gap/column-gap - No inline styles — move to SCSS files
- Avoid negative margins — fix layout with proper flexbox
.documentView {
display: flex;
flex-direction: column;
height: 100vh;
row-gap: 10px;
}
Custom Hooks
| Hook | Purpose |
|---|---|
useSelectiveContextMenuPrevention() | Prevents browser context menu everywhere except Monaco editors. Call once in top-level view. |
useHideScrollbarsDuringResize() | Returns a function that temporarily hides scrollbars during layout transitions (500ms). |
Conditional Rendering Patterns
Object-based switch:
{{
'Table View': <DataViewPanelTable {...props} />,
'Tree View': <DataViewPanelTree {...props} />,
'JSON View': <DataViewPanelJSON {...props} />,
}[currentContext.currentView]}
Loading State
const [isLoading, setIsLoading] = useState(false);
setIsLoading(true);
try {
await op();
} finally {
setIsLoading(false);
}
// In render:
{
isLoading && <ProgressBar thickness="large" shape="square" className="progressBar" />;
}
Common Pitfalls
- Forgetting refs with third-party components → stale data in event handlers
- Not cleaning up event listeners, Monaco instances, AbortControllers in
useEffectreturn - Not calling
editor.layout()after resize → blank Monaco panels - Using
any→ use proper types orunknownwith type guards - Missing
l10n.t()on user-facing strings - Inline styles instead of SCSS files
- Negative margins to fix spacing — restructure layout with flexbox gap instead
When not to use it
- →tRPC messaging
- →Accessibility/ARIA implementation
Limitations
- →Does not cover tRPC messaging or accessibility
How it compares
It provides standardized architectural patterns for webviews instead of ad-hoc implementation.
Compared to similar skills
react-webview-architecture side by side with the closest alternatives in the catalog.
| Skill | Installs | Updated | Safety | Difficulty |
|---|---|---|---|---|
| react-webview-architecture (this skill) | 0 | 2mo | No flags | Advanced |
| nextjs-best-practices | 31 | 6mo | No flags | Intermediate |
| web-component-design | 5 | 5mo | No flags | Intermediate |
| react-component-patterns | 3 | 1mo | No flags | Intermediate |
Try saying
Example prompts that trigger this skill in your AI assistant.
More by microsoft
View all by microsoft →You might also like
nextjs-best-practices
davila7
Next.js App Router principles. Server Components, data fetching, routing patterns.
web-component-design
wshobson
Master React, Vue, and Svelte component patterns including CSS-in-JS, composition strategies, and reusable component architecture. Use when building UI component libraries, designing component APIs, or implementing frontend design systems.
react-component-patterns
HoangNguyen0403
Modern React component architecture and composition patterns.
vercel-composition-patterns
vercel-labs
React composition patterns that scale. Use when refactoring components with boolean prop proliferation, building flexible component libraries, or designing reusable APIs. Triggers on tasks involving compound components, render props, context providers, or component architecture. Includes React 19 API changes.
javascript-typescript-typescript-scaffold
sickn33
You are a TypeScript project architecture expert specializing in scaffolding production-ready Node.js and frontend applications. Generate complete project structures with modern tooling (pnpm, Vite, N
server-components
davepoon
This skill should be used when the user asks about "Server Components", "Client Components", "'use client' directive", "when to use server vs client", "RSC patterns", "component composition", "data fetching in components", or needs guidance on React Server Components architecture in Next.js.