Observer pattern defines a one-to-many relationship between objects: when one object — the subject — changes state, every object that has subscribed to it — the observers — gets notified automatically. Neither side needs deep knowledge of the other. The subject just maintains a list of observers and calls a notify method on each one; the observer just implements whatever that notify method expects. This is the pattern underneath event listeners, reactive UI state, and most publish-subscribe systems, whether or not the code calls it "observer pattern" by name.
What changed in 2026
- Reactive state libraries made the pattern implicit. Frameworks like React, Vue, and Svelte handle subscription and notification internally through signals or reactive state, so most application code no longer implements observer pattern by hand — it just uses
useState or a signal and gets the behavior for free.
- Fine-grained reactivity reduced over-notification. Older observer implementations often notified every observer on any change; current reactive systems track exactly which observers depend on which piece of state and only notify the ones that actually need to re-run.
- Memory leak tooling improved. Browser devtools and framework linters got better at flagging observers that subscribe but never unsubscribe, which used to be a silent, slow leak.
How it works
class Subject {
private observers: Observer[] = [];
subscribe(o: Observer) { this.observers.push(o); }
unsubscribe(o: Observer) {
this.observers = this.observers.filter(x => x !== o);
}
notify(data: unknown) {
this.observers.forEach(o => o.update(data));
}
}
Any object implementing an update method can subscribe. The subject does not need to know what any given observer does with the notification — logging it, updating a UI, triggering another process — that logic lives entirely in the observer.
Push vs pull notification
In the push model, the subject sends the full changed data along with the notification, and observers use it directly. In the pull model, the subject just signals that something changed, and each observer queries the subject for the specific data it needs. Push is simpler and faster for small payloads; pull scales better when different observers care about different slices of a large state object and do not want the overhead of receiving all of it on every change.
Observer pattern variations compared
| Variant |
Coupling |
Notification style |
Common in |
| Classic observer |
Subject holds observer references directly |
Push or pull |
Hand-rolled event systems |
| Pub/sub |
Decoupled via a message broker/event bus |
Usually push |
Distributed systems, microservices |
| Reactive signals |
Framework tracks dependencies automatically |
Fine-grained push |
React, Vue, Svelte, SolidJS |
| DOM event listeners |
Browser manages the subscriber list |
Push, with event object |
Any client-side JavaScript |
Common pitfalls
- Forgetting to unsubscribe. An observer that stays registered after it should be gone keeps getting notified and keeps the subject holding a reference to it, which is a textbook memory leak in long-running applications.
- Notification storms. A subject that notifies on every tiny change, with observers that trigger further changes in response, can create cascading update loops that are hard to debug.
- Overusing observer where a direct function call would do. If there is exactly one listener and it will always be exactly one, the indirection of subscribe/notify buys nothing over calling a function directly — similar to the caution against unnecessary indirection covered for the factory pattern.
FAQ
Is observer pattern the same as pub/sub?
They are closely related. Classic observer usually has the subject hold direct references to observers; pub/sub typically routes messages through an intermediary (a broker or event bus), which further decouples publishers from subscribers, who often do not know about each other at all.
Do React hooks use observer pattern internally?
Effectively yes. State updates in React and similar frameworks rely on a subscription model where components register interest in a piece of state and re-render when it changes — the mechanics differ, but the shape is the same subscribe-and-notify idea.
How do I prevent memory leaks from observers?
Always pair a subscribe call with a corresponding unsubscribe, typically in a cleanup function tied to the observer's own lifecycle — a component unmounting, for example — rather than relying on it happening automatically.
When should I avoid observer pattern?
When there is a single, fixed relationship between two objects with no need for multiple listeners or future extensibility — a direct method call is simpler and easier to trace than a subscription.
Where to go next