Skip to main content

A shell is the product frame: navigation, layout, runtime/device state, and where surfaces appear. It should place surfaces, not become every feature.

Your app has a sidebar, a main panel, and a details panel. The shell decides those regions exist. Package surfaces decide what product body appears inside them.
Shell

Navigation, route, account state, runtime/device state, and layout regions.

Descriptor

Which product surface belongs in which shell region.

Surface

Reads named data, renders UI states, and sends named action requests.

Shell ownership

Shell can ownShell should not own
NavigationCustomer archive logic
Region layoutPackage record parsing
Active-device bannersTarget payload interpretation
Account/key promptsProduct-specific empty states
Surface placementPackage-owned file contents

Placeable surface rule

The shell should be able to place a surface from a descriptor without importing the feature’s private screens, stores, actions, or file layout.
{
  "surface": "customers.list",
  "region": "main",
  "reads": ["customers.list"],
  "targets": ["customers.archive"]
}
The shell decides where a surface goes. The surface still owns the customer-specific UI inside that place.

What this prevents

Bad shapeWhat happens by month six
Shell imports every feature component and action helperNavigation becomes the product’s largest dependency graph
Surface placement is hardcoded inside feature logicReusing the same feature in another shell becomes painful
Account/device state is copied into every surfaceEvery screen handles activation differently

Verify

You have the right boundary when you can move a surface from main to details without editing the target implementation or named data read contract.

Next

Use Placeable surfaces for the descriptor mental model and Device and deployment shape for account/device rollout.
Last modified on May 11, 2026