umbraco-state-management
Understand and use state management in Umbraco backoffice (foundational concept)
What this skill does
# Umbraco State Management
## What is it?
States in Umbraco are containers for reactive values that enable communication across component instances using the Observable pattern. An Umbraco State is a container for a value that you can create Observables from, which allows multiple observers to subscribe and automatically receive updates when the state changes. This pattern is particularly useful for sharing data between contexts and elements without tight coupling.
## Documentation
Always fetch the latest docs before implementing:
- **Main docs**: https://docs.umbraco.com/umbraco-cms/customizing/foundation/states
- **Foundation**: https://docs.umbraco.com/umbraco-cms/customizing/foundation
- **Context API**: https://docs.umbraco.com/umbraco-cms/customizing/foundation/context-api
## Workflow
1. **Fetch docs** - Use WebFetch on the URLs above
2. **Ask questions** - What type of state? Who observes? Where to provide observable?
3. **Generate code** - Implement state with observables based on latest docs
4. **Explain** - Show what was created and how observation works
## Minimal Examples
### Basic State Usage
```typescript
import { UmbStringState } from '@umbraco-cms/backoffice/observable-api';
// Create a state with initial value
const myState = new UmbStringState('initial value');
// Create an observable from the state
const myObservable = myState.asObservable();
// Observe the state (fires immediately and on changes)
this.observe(myObservable, (value) => {
console.log('Current value:', value);
});
// Update the state (all observers notified)
myState.setValue('updated value');
```
### State in Context Pattern
```typescript
import { UmbContextBase } from '@umbraco-cms/backoffice/class-api';
import { UmbNumberState } from '@umbraco-cms/backoffice/observable-api';
export class MyContext extends UmbContextBase<MyContext> {
// Private state
#counter = new UmbNumberState(0);
// Public observable (readonly)
readonly counter = this.#counter.asObservable();
increment() {
this.#counter.setValue(this.#counter.getValue() + 1);
}
decrement() {
this.#counter.setValue(this.#counter.getValue() - 1);
}
}
```
### Element Observing Context State
```typescript
import { UmbLitElement } from '@umbraco-cms/backoffice/lit-element';
import { MY_CONTEXT } from './my-context.js';
export class MyElement extends UmbLitElement {
@state()
private _count = 0;
constructor() {
super();
this.consumeContext(MY_CONTEXT, (context) => {
// Observe the counter state from context
this.observe(
context.counter,
(count) => {
this._count = count;
},
'_countObserver'
);
});
}
render() {
return html`
<div>Count: ${this._count}</div>
`;
}
}
```
### Different State Types
```typescript
import {
UmbStringState,
UmbNumberState,
UmbBooleanState,
UmbArrayState,
UmbObjectState
} from '@umbraco-cms/backoffice/observable-api';
// String state
const name = new UmbStringState('John');
// Number state
const age = new UmbNumberState(25);
// Boolean state
const isActive = new UmbBooleanState(true);
// Array state
const items = new UmbArrayState(['item1', 'item2']);
// Object state
const user = new UmbObjectState({ name: 'John', age: 25 });
```
### Observable Parts (Derived State)
```typescript
import { UmbArrayState } from '@umbraco-cms/backoffice/observable-api';
const itemsState = new UmbArrayState(['apple', 'banana', 'cherry']);
// Observe just the first item
const firstItem = itemsState.asObservablePart(data => data?.[0]);
// Observe just the count
const itemCount = itemsState.asObservablePart(data => data.length);
// Use in element
this.observe(firstItem, (first) => {
console.log('First item:', first);
});
this.observe(itemCount, (count) => {
console.log('Total items:', count);
});
```
### Array State Operations
```typescript
import { UmbArrayState } from '@umbraco-cms/backoffice/observable-api';
const listState = new UmbArrayState<string>([]);
// Add item
listState.setValue([...listState.getValue(), 'new item']);
// Remove item
listState.setValue(
listState.getValue().filter(item => item !== 'old item')
);
// Clear all
listState.setValue([]);
// Get current value
const current = listState.getValue();
```
### Complete Context Example
```typescript
import { UmbContextBase } from '@umbraco-cms/backoffice/class-api';
import { UmbStringState, UmbArrayState } from '@umbraco-cms/backoffice/observable-api';
export class TodoContext extends UmbContextBase<TodoContext> {
#title = new UmbStringState('My Todo List');
#todos = new UmbArrayState<string>([]);
readonly title = this.#title.asObservable();
readonly todos = this.#todos.asObservable();
readonly todoCount = this.#todos.asObservablePart(data => data.length);
setTitle(value: string) {
this.#title.setValue(value);
}
addTodo(todo: string) {
this.#todos.setValue([...this.#todos.getValue(), todo]);
}
removeTodo(todo: string) {
this.#todos.setValue(
this.#todos.getValue().filter(t => t !== todo)
);
}
}
```
## Key Concepts
**State**: Container for a value (private, mutable)
**Observable**: Subscription hook created from state (public, readonly)
**Observer**: Function that reacts to state changes via `observe()`
**State Types**:
- `UmbStringState` - text values
- `UmbNumberState` - numeric values
- `UmbBooleanState` - boolean flags
- `UmbArrayState` - collections
- `UmbObjectState` - complex objects
- `UmbClassState` - class instances
**Observable Parts**: Derived observables that only update when mapped value changes
**Best Practice**: Keep states private, expose observables publicly
**Use Cases**:
- Sharing data between context and elements
- Reactive UI updates
- Cross-component communication
- Derived/computed values
That's it! Always fetch fresh docs, keep examples minimal, generate complete working code.
Related in General
modeling-omnistudio-epc-catalog
IncludedSalesforce Industries CME EPC product-modeling skill for Product2-based catalog creation. Use when creating EPC products, configuring product attributes, building offer bundles with Product Child Items, or reviewing EPC DataPack JSON metadata for product catalog changes. TRIGGER when: user creates or updates Product2 EPC records, AttributeAssignment payloads, AttributeMetadata/AttributeDefaultValues, Offer bundles, or ProductChildItem relationships. DO NOT TRIGGER when: designing OmniScripts/FlexCards/Integration Procedures (use building-omnistudio-omniscript, building-omnistudio-flexcard, or building-omnistudio-integration-procedure), implementing Apex business logic (use generating-apex), or troubleshooting deployment pipelines (use deploying-metadata).
relationship-science-coach
IncludedUse this skill for direct, practical adult relationship coaching: couples conflict, repair, trust, marriage, dating, flirting, attachment patterns, emotional connection, sex, desire differences, eroticism, kink negotiation, affection, love languages, breakups, and long-term passion. Draw on Gottman, EFT and Hold Me Tight, attachment science, modern sex research, Perel, Nagoski, Kerner, Schnarch, Love and Stosny, and flexible love-language tools. Be concrete and low-hedge. Redirect only for imminent danger, abuse, coercive control, minors, non-consent, self-harm, stalking, or medical/legal/psychiatric decisions.
building-sf-integrations
IncludedSalesforce integration architecture and runtime plumbing with 120-point scoring. Use this skill to set up Named Credentials, External Credentials, External Services, REST/SOAP callout patterns, Platform Events, and Change Data Capture. TRIGGER when: user sets up Named Credentials, External Services, REST/SOAP callouts, Platform Events, CDC, or touches .namedCredential-meta.xml files. DO NOT TRIGGER when: Connected App/OAuth config (use configuring-connected-apps), Apex-only logic (use generating-apex), or data import/export (use handling-sf-data).
venue-templates
IncludedAccess comprehensive LaTeX templates, formatting requirements, and submission guidelines for major scientific publication venues (Nature, Science, PLOS, IEEE, ACM), academic conferences (NeurIPS, ICML, CVPR, CHI), research posters, and grant proposals (NSF, NIH, DOE, DARPA). This skill should be used when preparing manuscripts for journal submission, conference papers, research posters, or grant proposals and need venue-specific formatting requirements and templates.
let-fate-decide
IncludedDraws the 12 Houses of the Zodiac Tarot spread to inject entropy into planning when prompts are vague, ambiguous, or casually delegated. Interprets the spread to guide next steps. Use when the user says 'let fate decide', 'YOLO', 'whatever', 'idk', or other nonchalant phrases, makes Yu-Gi-Oh references, or when you are about to arbitrarily pick between multiple reasonable approaches. Prefer over ask-questions-if-underspecified when the user's tone is casual or playful rather than precision-seeking.
net-ops
IncludedCross-platform network troubleshooting (Windows, macOS, Linux) via local or remote shell. Use for: DNS broken, can't resolve hostnames, nslookup/dig works but apps fail, NRPT, WFP, scutil, /etc/resolver, systemd-resolved, /etc/resolv.conf, NetworkManager, VPN DNS leak residue (ProtonVPN/Mullvad/WireGuard/AnyConnect), AV/firewall blocking DNS or DoH, Tailscale DNS interaction, intermittent connectivity, remote diagnostics over SSH.