Views
A React surface the host mounts.
import { function defineMod<const TMod extends ModDefinition>(definition: TMod & ValidateModHooks<TMod>): ModThe entry point of the SDK. A mod's module default-exports one `defineMod` call and nothing else. The returned value is data. It registers nothing, starts nothing, and touches no host state. The host folds it twice. At build time it runs the module in a throwaway sandbox and reads the value to derive the serializable manifest. At runtime it folds the same value again to wire your handlers into their lanes: ext-host for commands, tools, effects, and hooks; the sandboxed renderer for views and widgets. That double fold is why the module must be pure. It is re-executed to produce the manifest, so anything at the top level, a file read, a network call, a timer, a `console` line, runs during a build with no host attached. Put behavior inside handlers: `run`, `activate`, `handle`. The `ValidateModHooks` constraint makes the call fail to typecheck when a `hook.beforeAction` targets an action that is neither a known host action nor `command:` plus one of this mod's own command ids.defineMod, function view(input: ComponentViewInput): ComponentViewDeclare a panel that docks into a workbench container. You hand the host a module reference, not a mount callback. The host owns the React root, mounts your component in a sandboxed renderer webview, and re-renders it on live edits. That webview has no Node access, so it reaches the ext-host half of the same mod through `ctx.action(...)` and shared state, never a shared object. `editorExtension` and `canvasElement` are the renderer-native alternative: they run in the host's own renderer and share its `lexical` and `react` instances. Use `view` when you are drawing the panel yourself. Use {@link treeView } when the content is a hierarchy of rows and you want the host's native tree with selection and inline actions. Use `widget` for a surface that floats or lives in its own window instead of docking.view } from '@modular/sdk'; export defaultdefineMod<{ readonly metadata: { readonly displayName: "Panels"; readonly description: "Contributed surfaces"; }; readonly views: readonly [ComponentView]; }>(definition: { readonly metadata: { readonly displayName: "Panels"; readonly description: "Contributed surfaces"; }; readonly views: readonly [ComponentView]; }): ModThe entry point of the SDK. A mod's module default-exports one `defineMod` call and nothing else. The returned value is data. It registers nothing, starts nothing, and touches no host state. The host folds it twice. At build time it runs the module in a throwaway sandbox and reads the value to derive the serializable manifest. At runtime it folds the same value again to wire your handlers into their lanes: ext-host for commands, tools, effects, and hooks; the sandboxed renderer for views and widgets. That double fold is why the module must be pure. It is re-executed to produce the manifest, so anything at the top level, a file read, a network call, a timer, a `console` line, runs during a build with no host attached. Put behavior inside handlers: `run`, `activate`, `handle`. The `ValidateModHooks` constraint makes the call fail to typecheck when a `hook.beforeAction` targets an action that is neither a known host action nor `command:` plus one of this mod's own command ids.defineMod({metadata: { readonly displayName: "Panels"; readonly description: "Contributed surfaces"; }metadata: { displayName: "Panels"displayName: 'Panels', description: "Contributed surfaces"description: 'Contributed surfaces' }, views: readonly [ComponentView]views: [ function view(input: ComponentViewInput): ComponentViewDeclare a panel that docks into a workbench container. You hand the host a module reference, not a mount callback. The host owns the React root, mounts your component in a sandboxed renderer webview, and re-renders it on live edits. That webview has no Node access, so it reaches the ext-host half of the same mod through `ctx.action(...)` and shared state, never a shared object. `editorExtension` and `canvasElement` are the renderer-native alternative: they run in the host's own renderer and share its `lexical` and `react` instances. Use `view` when you are drawing the panel yourself. Use {@link treeView } when the content is a hierarchy of rows and you want the host's native tree with selection and inline actions. Use `widget` for a surface that floats or lives in its own window instead of docking.view({ id: stringid: 'panels.outline', title: stringtitle: 'Outline', container?: "right" | "left" | "bottom" | "anywhere" | undefinedcontainer: 'right', icon?: string | undefinedThe view's nav icon — shown on its activity-bar / panel entry. A design system icon name (the icon's export name), e.g. `'IconSparkle2Outline24'`, `'IconStarOutline24'`, or a codicon like `'CodiconAccount'`. Rendered monochrome, tinted to the active foreground — distinct from the mod's full-color `metadata.icon`. Omit and the view falls back to a placeholder.icon: 'CodiconListTree', component: ViewComponentReferencecomponent: { ViewComponentReference.module: stringmodule: './views/outline', ViewComponentReference.exportName?: string | undefinedexportName: 'Outline' }, }), ], });
component is a module reference, not a mount callback. The host owns the
React root, so it can re-render your component when you edit it.
view produces a
View — a type alias, because a view's props depend
on what it declares.
Import from the view entry point
A view's React code imports from @modular/sdk/view, not from the root entry
point:
import type { ViewContext } from '@modular/sdk/view'; export functionfunction Outline({ ctx }: { readonly ctx: ViewContext; }): `${string}.${string}` & $brand<"ModId">Outline({ ctx: ViewContextctx }: { readonly ctx: ViewContextctx: ViewContext }) { return ctx: ViewContextctx.ContextBase.modId: `${string}.${string}` & $brand<"ModId">The id of the mod this handler belongs to.modId; }
That subpath is a shared live instance. The host injects React and the
@modular/sdk/ui component library into it. If your mod bundles
its own copy of either, hooks and context break because two Reacts in one tree
do not share state.
This is the single most common way a working view stops working.
Use Components for the shared controls, UI hooks, icons, and material surfaces available inside that module.
The context
Views receive a ViewContext, wider
than the base context.
Styling
Mod webviews see Modular's own design tokens. They do not get the
--vscode-* variable set, so a var(--vscode-foreground) in your CSS silently
resolves to its fallback rather than to a real colour.