Most state in an Angular application lives locally in a single component: a form's draft values, whether a dropdown is open, the currently selected tab. This lesson covers modeling that state well.
What Is Component State?
Component state is data that only one component (and possibly its direct children, via inputs) needs to know about. It doesn't need to be shared through a service or synchronized across unrelated parts of the app.
Modern Angular models this kind of state naturally with signals: plain class fields for simple cases, signal() for state templates need to react to reactively, and computed() for values derived from other state.
isOpen is purely local, no other component in the app needs to know or care about this accordion's open state.
Modeling Local State
count = 0; // plain field, fine for non-reactive/static values
count = signal(0); // reactive field, template updates automatically
doubled = computed(() => this.count() * 2); // derived from other signals
Plain class fields work for values the template reads once (like configuration) or that don't need automatic reactivity.
signal() is the standard way to model reactive local state that the template should re-render on change.
computed() derives a new reactive value from one or more signals, recalculating only when its dependencies change.
Avoid duplicating derived state as its own signal, if it can be computed, use computed() instead.
Component State Cheatsheet
Common local state patterns in Angular components.
Pattern
Example
Use Case
Plain field
title = 'Dashboard';
Static or rarely-changing values
Signal
count = signal(0);
Reactive state the template renders
Computed
total = computed(() => a() + b());
Derived values from other signals
Effect
effect(() => console.log(count()));
Reacting to signal changes with a side effect
Two-way with a form control
[(ngModel)] / reactive forms
Editable local form state
Deriving State With computed()
Rather than manually keeping a second signal in sync with a first (updating both every time something changes), computed() derives its value automatically and only recalculates when one of its dependencies actually changes.
export class CartSummaryComponent {
items = signal<{ price: number; qty: number }[]>([]);
total = computed(() =>
this.items().reduce((sum, item) => sum + item.price * item.qty, 0)
);
}
total always reflects items correctly, there's no risk of it becoming stale after an update.
Reacting to State Changes With effect()
effect() runs a function automatically whenever any signal it reads changes, useful for side effects like logging, syncing to localStorage, or triggering an animation, rather than for computing new state (use computed() for that instead).
When two sibling components both need access to the same piece of state, the common pattern (borrowed from component-based UI generally) is to "lift" that state up to their shared parent, and pass it down via inputs/outputs, rather than reaching for a full shared service prematurely.
Reaching for a shared service the moment two components need related data, when lifting state to a common parent would be simpler.
Manually recomputing and storing a derived value in a second signal instead of using computed().
Using effect() to compute new state (causing subtle update loops) instead of computed().
Keeping too much state as plain, non-reactive fields, then wondering why the template doesn't update.
Key Takeaways
Component state is data scoped to a single component (and possibly its direct children).
Signals (signal()) model reactive local state; computed() derives values from other signals automatically.
effect() is for side effects reacting to state changes, not for computing new state.
Lift state up to a shared parent before reaching for a full shared service, when siblings need the same data.
Pro Tip
If you find yourself manually keeping two signals in sync with an effect, stop and check whether one of them should actually be a computed() derived from the other, that's almost always the simpler, more correct approach.
You now understand component-level state. Next, learn about Service State for sharing data across multiple, unrelated components.