name: home-assistant-lazy-context
description: 'Home Assistant frontend lazy-context, memoization, and hass removal guidance. Use when migrating Lit components from hass!: HomeAssistant, .hass=${...}, or broad hass access to context slices.'
Home Assistant Lazy Context
Use this skill when reviewing or editing Home Assistant frontend context, memoization, or hass removal work.
Default Migration Rule
When a component adopts context, remove its hass property and replace each this.hass.* access with the narrowest context or value that supplies that data. Also remove .hass=${...} passthroughs to migrated child components; if a child still needs data, either migrate the child to context or pass a narrow prop such as localize, states, or Pick<HomeAssistant, "callWS">.
Do not keep hass!: HomeAssistant just to preserve the old public API unless the component is intentionally reused outside the app context tree or the user explicitly asks for compatibility.
Context Architecture (src/data/context/index.ts)
Three tiers of Lit contexts provided by context-mixin.ts:
Core Contexts (always subscribed)
| Context | Type | Contents |
|---|---|---|
registriesContext |
HomeAssistantRegistries |
entities, devices, areas, floors registries |
statesContext |
HomeAssistant["states"] |
Live entity state map |
servicesContext |
HomeAssistant["services"] |
Services map |
internationalizationContext |
HomeAssistantInternationalization |
localize, locale, language, loaders |
apiContext |
HomeAssistantApi |
callService, callWS, fetchWithAuth, hassUrl |
connectionContext |
HomeAssistantConnection |
connection, connected, debugConnection |
uiContext |
HomeAssistantUI |
themes, panels, sidebar, kiosk |
configContext |
HomeAssistantConfig |
auth, config, user, userData, systemData |
formattersContext |
HomeAssistantFormatters |
formatEntityState, formatEntityName, etc. |
entitiesContext |
HomeAssistant["entities"] |
Entity registry map |
devicesContext |
HomeAssistant["devices"] |
Device registry map |
areasContext |
HomeAssistant["areas"] |
Area registry map |
floorsContext |
HomeAssistant["floors"] |
Floor registry map |
Lazy Contexts (subscribed only when consumed)
Managed by LazyContextProvider -- WS subscription defers until first consumer, tears down after 5s idle when all consumers disconnect.
| Context | Type |
|---|---|
labelsContext |
LabelRegistryEntry[] |
fullEntitiesContext |
EntityRegistryEntry[] |
configEntriesContext |
ConfigEntry[] |
manifestsContext |
DomainManifestLookup |
Deprecated Contexts
Do not use. Each has a @deprecated comment naming the replacement:
connectionSingleContext→connectionContextlocalizeContext,localeContext→internationalizationContextconfigSingleContext,userContext,userDataContext,authContext→configContextthemesContext,selectedThemeContext,panelsContext→uiContext
Entity Decorators (src/common/decorators/consume-context-entry.ts)
@consumeEntityState({ entityIdPath })— resolves entity ID from host config path, subscribes tostatesContext, returnsHassEntity@consumeEntityStates({ entityIdPath })— same for array of entity IDs →HassEntity[]@consumeEntityRegistryEntry({ entityIdPath })— subscribes toentitiesContext, returnsEntityRegistryDisplayEntry@consumeLocalize()— subscribes tointernationalizationContextand narrows it toLocalizeFunc
Use @consumeLocalize() when a component only needs localize. Use internationalizationContext directly when it also needs locale, language, translationMetadata, loadBackendTranslation, or loadFragmentTranslation.
Migration: Removing hass
When a component adopts context, the hass property is removed. This requires revising all sub-components and helpers it passes hass to.
Decision Tree for Sub-Components
Component can consume context directly (it's rendered in the app tree, not reused across unrelated trees):
- Remove
hassproperty, add@consumedecorators for each needed slice. - Use
ContextType<typeof fooContext>for typing.
- Remove
Component is a shared utility (reused widely, context conversion would touch too many callers):
- Pass the individual item as a prop when scope is small (e.g.
localize: LocalizeFunc,states: HassEntities). - Pass
Pick<HomeAssistant, "callWS" | "localize">when the component needs a few unrelated slices and context is not viable. - Never pass the full
HomeAssistantobject.
- Pass the individual item as a prop when scope is small (e.g.
Helper/utility functions (pure data functions):
- Narrow the parameter from
HomeAssistanttoPick<HomeAssistant, "callWS">(or the single value needed). - This lets the consuming component pass its context slice directly (e.g.
this._api) without reconstructinghass. - Prefer narrowing the helper over changing every caller — keeps the scope of the change small.
- Narrow the parameter from
Narrowing Helpers to Avoid Scope Explosion
When a component adopts context, its helpers still need data. Rather than passing a reconstructed hass-like object or converting every caller, narrow the helper signature so the context slice satisfies it directly:
// BEFORE: helper accepts full hass
export const fetchDeviceTriggers = (hass: HomeAssistant, deviceId: string) =>
hass.callWS<DeviceTrigger[]>({...});
// AFTER: helper accepts only what it uses
export const fetchDeviceTriggers = (
hass: Pick<HomeAssistant, "callWS">,
deviceId: string
) => hass.callWS<DeviceTrigger[]>({...});
The component then passes its context slice directly:
// Component consumes apiContext (which satisfies Pick<HomeAssistant, "callWS">)
@state()
@consume({ context: apiContext, subscribe: true })
private _api!: ContextType<typeof apiContext>;
// Passes directly — no reconstruction needed
const triggers = await fetchDeviceTriggers(this._api, deviceId);
This keeps the change focused: the dialog/panel adopts context, the helpers get narrowed types, and all other callers of those helpers still work unchanged (since HomeAssistant satisfies Pick<HomeAssistant, "callWS">).
When to Use Pick vs Individual Values
Pick<HomeAssistant, "callWS">— when the helper uses methods from a single context group and the existing param namehassstays readable. Keeps changes minimal.- Individual value (e.g.
states: HassEntities) — when passing a plain data map that the helper iterates/reads. More explicit, avoids thehass.prefix. Pickwith multiple keys — avoid unless strictly necessary. If a helper needs"callWS" | "localize", consider splitting it or accepting the grouped context type directly.
Consumption Pattern
@state()
@consume({ context: apiContext, subscribe: true })
private _api!: ContextType<typeof apiContext>;
@state()
@consume({ context: internationalizationContext, subscribe: true })
private _i18n!: ContextType<typeof internationalizationContext>;
@state()
@consume({ context: statesContext, subscribe: true })
private _states!: ContextType<typeof statesContext>;
Usage: this._api.callWS(...), this._i18n.localize(...), this._states[entityId].
For localize-only components, prefer the helper:
@consumeLocalize()
private _localize!: LocalizeFunc;
Rules
General
- Preserve runtime behavior and public API shape except for the
hassAPI intentionally removed by context migrations. - Use the narrowest correct data source; prefer contexts, lazy contexts, and entity decorators over broad
hassaccess. - Do not use deprecated contexts; use the replacement named in
@deprecatedcomments. - Do not replicate
hassor build local objects that mirror large parts ofHomeAssistant. - Do not add provider fallbacks when an existing app-level context/lazy context is already correct.
- Do not duplicate
LazyContextProviderlifecycle behavior in feature components. - Do not create wrapper helpers that only forward existing utility calls.
Typing
- Use
ContextType<typeof context>for consumed context typing. - Use required (
!) when lifecycle guarantees availability; use optional (?) only when value can be genuinely absent. - Avoid excessive defensive guards around required consumed contexts.
- For sub-components that can't use context, type props as narrow as possible: prefer
LocalizeFuncoverHomeAssistantInternationalization, preferPick<HomeAssistant, "callWS">overHomeAssistant.
Memoization
- Pass explicit narrow inputs only (actual state slice, config, primitive, small arrays).
- Do not pass broad objects (
hass,this, large mutable maps) intomemoizeOne. - Do not widen signatures with pass-through
localize,language,locale, or config values when class/context access already exists. - Do not add passthrough fields (
_language,_locale,_config) only to route values into helpers. - For rarely changed values like locale/config, read from existing class/context access at point of use.
Style
- Do not add unnecessary comments or abstractions.