Skip to content
5.0 Upwork, 100+ reviews Adobe Certified since 2021 New: become your own Magento developer Learn more →
Magento glossary

What is a Magento Observer?

Short answer A Magento 2 observer is a class that runs when Magento dispatches an event, such as an order being placed or a customer being saved. That is different from a plugin, which wraps a method call. You register observers in etc/events.xml (all areas), etc/frontend/events.xml or etc/adminhtml/events.xml, and the class implements Magento\Framework\Event\ObserverInterface with a single execute(Observer $observer) method. Observers run synchronously, inside the same request. So when an observer has to call an ERP or another slow API, I publish a message to a queue and let a consumer do the work.

How it works
  1. Identify the event you care about 01
  2. Create the observer class 02
  3. Register the observer in events.xml 03
  4. Clear the config cache 04
  5. Event fires at runtime: execute() runs synchronously 05
Each step is explained below

Most common mistake Doing slow work in a synchronous observer

How it works

Five steps from event name to a live observer in production

Observers are wired up in XML, not found automatically. You find the event, write the class, register it in events.xml and clear the config cache. This is the sequence I follow.

  1. 01

    Identify the event you care about

    Core code dispatches a lot of named events. The ones I use most are customer_save_after, customer_login, sales_order_place_after, catalog_product_save_after, cms_page_save_after, checkout_cart_product_add_after, controller_action_predispatch and admin_user_authenticate_after. Adobe has an event reference, but I trust a grep more: search vendor/magento for ->dispatch(' and you see every event your installed version actually fires, along with the data it passes.

  2. 02

    Create the observer class

    The class goes in Vendor\Module\Observer\WhateverName and implements \Magento\Framework\Event\ObserverInterface, which has one method: execute(\Magento\Framework\Event\Observer $observer): void. Read the event data with $observer->getEvent()->getData('customer'), or the magic getter $observer->getCustomer(), which reads the same key. Anything else you need (a logger, a repository, a queue publisher) comes in through the constructor like any other Magento class.

  3. 03

    Register the observer in events.xml

    Choose the scope on purpose. etc/events.xml applies everywhere, including cron, CLI and the APIs. etc/frontend/events.xml applies only to storefront requests and etc/adminhtml/events.xml only to the admin. The entry looks like <event name="customer_save_after"><observer name="my_unique_name" instance="Vendor\Module\Observer\CustomerSaveAfter"/></event>. Keep the observer name unique for that event. If another module uses the same name, the later one replaces the earlier one without any error.

  4. 04

    Clear the config cache

    Magento merges every events.xml into the config cache, so a new observer does nothing until that cache is cleared. Run bin/magento cache:clean config after any events.xml change, in every mode, including developer mode if the config cache is enabled. In production mode the class also needs setup:di:compile. When someone tells me their observer never fires, a stale config cache is the first thing I check.

  5. 05

    Event fires at runtime: execute() runs synchronously

    When core code calls $eventManager->dispatch('customer_save_after', ['customer' => $customer]), Magento’s event manager looks up the event name, walks the registered observer list in events.xml load order (no explicit sortOrder), and invokes each execute() synchronously. An exception in one observer stops the chain unless caught, so be defensive: wrap risky work in try/catch and log rather than re-throw if the failure is non-critical to the dispatching request.

When to use

Four scenarios where an observer is the right tool

Observers are not the answer to every change. Plugins, preferences and di.xml arguments all have their place. These are the four cases where I pick an observer.

  • Side-effect-on-state-change patterns

    “When a customer registers, post to Slack.” “When an order is placed, send the lines to the ERP.” “When a product is saved, purge that URL on the CDN.” Anything shaped like “this happened, now do that” is what observers are for. If the follow-up talks to another system over HTTP, I keep the observer tiny: it publishes a message to a queue (declared in communication.xml and queue_publisher.xml) and a consumer does the slow part outside the customer’s request.

  • Loose-coupling between modules

    An observer lets your module react to core behaviour without calling or extending core classes. Magento fires the event, your code runs, and the core class has no idea your module exists. Event names change less often between versions than the internals of the methods a plugin wraps. So when an event and a plugin would both do the job, I usually go with the event.

  • Replacing legacy Magento 1 hooks

    Magento 1 used Mage::dispatchEvent('name', $params) with observers declared in config.xml. Magento 2 events are the direct equivalent, same dispatch-and-react pattern, slightly different wiring (events.xml instead of config.xml, ObserverInterface instead of Mage_Core_Model_Observer). Most Magento 1 observer logic ports cleanly to Magento 2 observers; if you’re mid-migration, observers are the migration path of least resistance for event-reactive code.

  • Subscribing to your OWN dispatched events

    Your module dispatches vendor_module_action_after from inside a service class, and a different module observes it, clean separation of concerns. This is also how you make your module extensible by third parties without forcing them to use plugins on your code. Dispatch your own events at meaningful state transitions in your module (panth_subscription_renewed_after, panth_widget_rendered_after) and downstream modules can hook in without touching your code.

Common mistakes

Three observer mistakes that slow your store or silently break it

Every Magento observer-related ticket I get called in on is one of these three. Check your code against this list before shipping.

  • Doing slow work in a synchronous observer

    Observers fire synchronously inside the dispatching request, an HTTP call to a CRM from inside customer_save_after blocks the admin customer-save by the duration of the CRM round-trip. A 2-second CRM call turns a 200ms admin save into a 2.2s admin save. Always queue async via Magento_AsynchronousOperations (or PublisherInterface) for any I/O over ~50ms. The synchronous observer publishes a tiny message; a consumer process handles the slow work out-of-band.

  • Forgetting events.xml is per-area

    Registering an observer in etc/events.xml when you only need it on the frontend doubles its boot footprint, the observer wires up in adminhtml too, gets invoked on every matching admin event, and adds DI overhead to every admin page load. Pick the scoped variant: etc/frontend/events.xml for storefront-only, etc/adminhtml/events.xml for admin-only, plain etc/events.xml only when you genuinely need both. Same applies to webapi_rest / webapi_soap scopes for REST/SOAP-only observers.

  • Mutating $observer data and expecting core to see it

    Observers receive a copy of the event payload wrapped in the Observer object, modifying values inside the observer does not propagate back to the caller. If you change $observer->getOrder()->setStatus('custom') the change may or may not persist depending on whether the dispatched object is the same reference Magento later saves. For deterministic mutation use a plugin (around / before / after) on the specific method, not an observer, observers are for reactions, plugins are for interception.

FAQ

Magento observers, frequently asked questions

  • Observer vs plugin, when to use which?
    Use an observer when you want to react to a dispatched event with a side effect that does not need to influence the caller’s return value, logging, queue publishing, sending an email, invalidating a cache. Use a plugin when you want to wrap a specific method call to inspect or modify its arguments, return value, or exception handling, that’s the interceptor pattern, and observers cannot do it. Rule of thumb: if you need to change what the caller sees, you need a plugin; if you just need to do something on the side, an observer is cleaner and survives upgrades better. They are not mutually exclusive, many modules use both for different concerns.
  • Why isn’t my observer firing?
    Three causes account for almost every case. First, the events.xml isn’t loaded, run bin/magento cache:flush config full_page after every events.xml change. Second, the observer is in the wrong area scope, an observer in etc/adminhtml/events.xml will not fire on frontend requests, and vice versa. Check whether your event is dispatched in frontend code, admin code, or both, and place the events.xml accordingly. Third, the event name is typo’d, Magento does not throw an error for an observer registered against a non-existent event, it just silently never invokes. Grep vendor/magento for the exact event string to confirm it’s dispatched.
  • Can multiple observers listen to one event?
    Yes, unlimited, each registered with a unique name attribute on the <observer> element. They fire in events.xml load order across modules (which roughly follows module dependency order), with no explicit sortOrder attribute available. If you absolutely need ordering, the workaround is to make one observer depend on the other’s output via a shared registry or a database flag, but that’s a code-smell. If you find yourself needing strict ordering between observers, reconsider whether a plugin (which does support sortOrder) is the better fit.
  • How do I make an observer async?
    Two patterns. The lightweight option: from inside the synchronous observer, publish a message via Magento\Framework\MessageQueue\PublisherInterface::publish('topic.name', $payload), declare the topic + consumer in etc/communication.xml + etc/queue_consumer.xml + etc/queue_topology.xml, and run a consumer process to handle it. The richer option: use the Magento_AsynchronousOperations module which gives you bulk operations, status tracking via the admin UI, and per-operation retry semantics. Either way the synchronous observer stays sub-50ms (it just publishes the message) and the slow work runs out-of-band on a consumer process.
  • What’s the cost of a no-op observer?
    Roughly 5-15 microseconds per dispatch, class autoload (cached after the first call), DI container resolution, and the empty execute() method call. The cost adds up because Magento ships hundreds of dispatched events per request: controller_action_predispatch fires for every controller, core_layout_block_create_after fires for every block, view_block_abstract_to_html_before fires for every render. If you register an observer on a high-frequency event, the cumulative cost can become measurable. Profile with Blackfire or XHProf before registering observers on the high-fire events.
  • Are there any global events for “any database write”?
    No, Magento dispatches per-entity-type events, not a single “model_save_after” superset. You get customer_save_after, catalog_product_save_after, sales_order_place_after, cms_page_save_after, and so on per entity. There used to be a generic model_save_after in Magento 1, but Magento 2 removed it because almost every observer registered against it ended up needing to check the model type anyway, which made the abstraction worse than just listening to the specific entity event. If you genuinely need to react to every save across types, register the same observer class on each event you care about, that’s the idiomatic Magento 2 approach.
Observer audit

Need a Magento observer built or audited?

Send your event + side-effect requirement, I will design the observer, place it in the correct area scope, wire async via Magento_AsynchronousOperations where the work is slow, and ship a tested module with a fixed-price quote. 24-business-hour written reply.

Kishan Savaliya Online 10 AM to 7 PM IST Chat on WhatsApp