Skip to content

Composition: the provider surface

This is a guide, not the contract. What the platform guarantees is specified under openspec/specs/. For this page: platform-composition · host-services. Where this page and a specification disagree, the specification is right, and that is a defect in this page: change the behaviour there, then explain it here.

There is no single god-provider, on purpose: each decision has its own provider, so the same decision never has two doors. This page is the whole surface indexed by what you want; each row points at the guide section that tells the story.

What the product is

I want to …provider
set the name, logo and taglineprovideProductIdentity (Branding)
decide which regions exist and whereprovideLayout (Layout)
recolour the whole appthe design tokens (tokens)
change sizes, radii, densityyour own CSS on the class contracts (tokens)
replace a built-in iconprovideIcons (Icons)
reword the shell itself (“Folder” instead of “View”)provideTranslationOverrides (Rewording)
ship my own translationsprovideTranslationNamespaces (i18n)

What users are allowed to do

I want to …provider
take a gesture away (splitting, pinning, pop-out, shortcuts …)provideShellFeatures (Switching capabilities off)
change a switch while the app runs, or read itFeatureSwitches (Switches)
offer a pane, workspace, sidebar or reset action from my own controlthe services on these pages (Panes, Workspaces, Sidebars, Resetting the application)
drop a built-in command, item, settings row, menu entry or routeprovideShell({ omit }) (Recomposing host chrome)
hand out layouts the product definesprovideWorkspaces (Developer-defined workspaces)
let users put the arrangement backnothing: shell.app.reset ships (Resetting the arrangement)

What ships inside

I want to …provider
compose my weaverprovidePlugins + provideCapabilityGrants (Capabilities)
keep a plugin from being switched offprovideRequiredPlugins (A plugin your application cannot run without)
run an isolated pluginprovideFramePlugins (Frame plugins)
offer a plugin catalogueprovidePluginCatalog (Plugin store)
add chrome of my ownprovideBarItems, provideRailItems, provideViews (Do it below)
put a search entry in a barprovideCommandPaletteEntry, provideQuickOpenEntry (Command palette entry)

What it talks to

I want to …provider
feed the signed-in user inprovideAuthSource (Auth integration)
send gated routes to my loginprovideUnauthorizedRedirect (Redirect)
store settings in my backendprovideSettingsStore (Persistence stores)
store working state in my backendprovideWorkingStateStore (Persistence stores)
keep two users in one browser apartprovideIdentityScopedStores (Identity-scoped stores)
compute a following tab’s address myselfprovideTabAddressResolver (Following tabs)
route the content areaprovideShellRouter (Content-area routing)
ship without a service workerprovideShell({ serviceWorker: false }) (PWA)
keep hidden surfaces alive by defaultprovideShell({ retention }) (Surface retention)

Do it

A distribution does not need a plugin to add chrome. Three providers contribute the same shapes a weaver contributes, statically at composition time:

// src/app/app.config.ts — in the providers array
...provideViews({ id: 'acme.inspector', title: 'acme.inspector.title', region: 'left-panel', component: Inspector }),
...provideBarItems({ id: 'acme.status', bar: 'status-bar', slot: 'end', component: BuildStatus }),
...provideRailItems({ id: 'acme.help', rail: 'primary', icon: 'help', title: 'acme.help', anchor: 'bottom',
command: 'acme.openHelp' }),

Read it

ContributionRegistry is the registry underneath the providers. Three of its signals answer questions about your own composition, and are what loomweaver.report() reads:

SignalHolds
omittedthe ids your omit list names, exactly as you wrote them, prefixes and all
registeredIdsevery id registered so far, of any kind, including the ones omit hides
registeredCommandsevery command with the plugin the host stamped on it as its owner (RegisteredCommand); the shell’s own commands carry no owner, which is what tells one plugin’s commands from another’s

What asks about unsaved work

Nothing on this page asks; a provider runs once at composition time, before any surface exists.

Switched off

No switch governs the providers. provideShellFeatures is where the switches themselves are declared; Switches reads and changes them while the app runs.

In depth

Ids address regions. region, bar and rail must name a region id declared in your provideLayout; the ids above match the getting-started layout. A contribution addressing an id no region declares renders nowhere, and a view logs a dev-mode warning.

Ids replace. Because ids are the addressing scheme everywhere, using an existing id replaces that contribution, which is how the demo moves the update badge into a sidebar footer. provideShell({ omit: [...] }) removes one.

At runtime. Injecting ContributionRegistry lets you add and remove contributions while the app runs; addRailItem returns a disposer. Prefer the providers when the answer is known at composition time.

Reading registeredIds. It is the only way to tell an omit that hid something from one that hit nothing at all, because an omitted contribution is by construction absent from every other signal. Reach for the report first: it already phrases the answer, including which prefix you probably meant.

Where the story is told