diff --git a/docs/docs/advanced/event-dispatcher.md b/docs/docs/advanced/event-dispatcher.md
new file mode 100644
index 000000000..f37df5c41
--- /dev/null
+++ b/docs/docs/advanced/event-dispatcher.md
@@ -0,0 +1,122 @@
+---
+title: Event Dispatcher System
+description: How the webforJ event dispatcher system works and how to use it for event-driven programming.
+sidebar_position: 50
+sidebar_class_name: new-content
+---
+
+
+
+
+The webforJ event dispatcher system provides a flexible and type-safe way to handle events in your app. It allows you to register listeners for specific event types and dispatch events to those listeners, enabling event-driven programming across components.
+
+:::tip Element and component events
+The `EventDispatcher` is for custom event management, see the [Events](/docs/building-ui/events) articles to learn how to handle standard element and component events.
+:::
+
+## Creating and registering listeners
+
+To respond to events in your app, you first need to register one or more listeners with the `EventDispatcher`. A listener is simply a function or lambda that will be called whenever an event of the specified type is dispatched. This mechanism allows you to decouple event producers from consumers. You can register listeners for standard Java events or for your own custom event classes.
+
+
+```java
+import com.webforj.dispatcher.EventDispatcher;
+import com.webforj.dispatcher.EventListener;
+import com.webforj.dispatcher.ListenerRegistration;
+import java.util.EventObject;
+
+// Create the dispatcher
+EventDispatcher dispatcher = new EventDispatcher();
+
+// Register a listener
+ListenerRegistration reg = dispatcher.addListener(EventObject.class, event -> {
+ console().log("Event received: " + event);
+});
+
+// Remove a listener
+reg.remove();
+// or
+dispatcher.removeListener(EventObject.class, reg.getListener());
+
+// Dispatch an event
+dispatcher.dispatchEvent(new EventObject(this));
+```
+
+
+
+## Creating and registering custom events
+
+Create a class that extends `EventObject` (or another suitable base event class). Add any custom fields or methods you need:
+
+```java
+public static class MyCustomEvent extends EventObject {
+ private final String message;
+ public MyCustomEvent(String message) {
+ super(message); // The source can be any object, e.g., the dispatcher or sender
+ this.message = message;
+ }
+ public String getMessage() {
+ return message;
+ }
+}
+```
+
+Then register a listener listening for your custom event.
+
+```java
+ListenerRegistration reg = dispatcher.addListener(MyCustomEvent.class, event -> {
+ // Handle the event (access custom fields)
+ console().log("Custom event received: " + event.getMessage());
+});
+```
+
+Whenever needed, dispatch the event and the listener will be triggered.
+
+```java
+dispatcher.dispatchEvent(new MyCustomEvent("Hello from custom event!"));
+```
+
+
+
+### Removing listeners and avoiding memory leaks
+
+When to remove listeners
+
+- Remove listeners when the consumer object is disposed or no longer reachable from the app UI (for example: closing a dialog, navigating away from a view, or when a long-lived service no longer needs callbacks).
+- Remove listeners when the event handling is tied to a transient lifecycle (short-lived tasks, temporary subscriptions, or one-time flows).
+
+How to remove listeners
+
+Use the returned `ListenerRegistration` to unregister a listener explicitly:
+
+```java
+ListenerRegistration reg = dispatcher.addListener(MyCustomEvent.class, ev -> handle(ev));
+// Later, when no longer needed
+reg.remove();
+```
+
+Or remove by passing the listener to the dispatcher:
+
+```java
+dispatcher.removeListener(MyCustomEvent.class, myListener);
+```
+
+If you registered multiple listeners for the same class and want to remove all of them at once:
+
+```java
+dispatcher.removeAllListeners(MyCustomEvent.class);
+```
+
+Listeners are objects or lambdas that often reference surrounding objects (for example, a view or its model). The EventDispatcher keeps those listener objects in internal collections; while stored, neither the listener nor the objects it references can be garbage-collected. In apps that create many short-lived views or components, forgotten listeners accumulate and keep those views alive, causing memory that can't be freed.
+
+- A dialog registers a listener that references the dialog's model. If the dialog is closed but the listener isn't removed, the dispatcher still references the listener, which in turn references the dialog and its model. The dialog can't be garbage-collected.
+- Lambdas or inner classes implicitly capture `this` or local variables; the captured references become part of the listener object retained by the dispatcher.
+
+Removing the listener explicitly breaks the chain of strong references: the dispatcher no longer references the listener, so the listener and the objects it captured become eligible for garbage collection if there are no other live references to them.
+
+Prefer storing the returned `ListenerRegistration` where you can remove it during cleanup (for example, in a component's `dispose` or a view's `onDetach`), and avoid anonymous registrations that you can't later unhook.
+
diff --git a/src/main/java/com/webforj/samples/views/advanced/EventDispatcherCustomEventView.java b/src/main/java/com/webforj/samples/views/advanced/EventDispatcherCustomEventView.java
new file mode 100644
index 000000000..b4810423b
--- /dev/null
+++ b/src/main/java/com/webforj/samples/views/advanced/EventDispatcherCustomEventView.java
@@ -0,0 +1,77 @@
+package com.webforj.samples.views.advanced;
+
+import com.webforj.dispatcher.EventDispatcher;
+import com.webforj.router.annotation.FrameTitle;
+import com.webforj.router.annotation.Route;
+import com.webforj.component.button.Button;
+import com.webforj.annotation.StyleSheet;
+import com.webforj.component.Composite;
+import com.webforj.component.html.elements.Div;
+import com.webforj.component.layout.flexlayout.FlexJustifyContent;
+import com.webforj.component.layout.flexlayout.FlexLayout;
+import com.webforj.component.layout.flexlayout.FlexLayoutBuilder;
+
+import java.util.EventObject;
+
+@Route
+@FrameTitle("Event Dispatcher")
+@StyleSheet("ws://css/advanced/eventDispatcherCustomEvent.css")
+public class EventDispatcherCustomEventView extends Composite {
+ private final EventDispatcher dispatcher = new EventDispatcher();
+
+ /**
+ * A custom event that carries a message string.
+ */
+ public static class CustomMessageEvent extends EventObject {
+ private final String message;
+
+ public CustomMessageEvent(Object source, String message) {
+ super(source);
+ this.message = message;
+ }
+
+ public String getMessage() {
+ return message;
+ }
+ }
+
+ Button button = new Button("Fire custom event");
+ Button button2 = new Button("Reset");
+ Div statusText = new Div("Waiting for custom event");
+ FlexLayoutBuilder layoutBuilder = new FlexLayoutBuilder(button, button2, statusText);
+
+ public EventDispatcherCustomEventView() {
+
+ button.setWidth("fit-content");
+ button2.setWidth("fit-content");
+
+ statusText.addClassName("statusText");
+
+ FlexLayout layout = layoutBuilder.vertical()
+ .justify().center()
+ .align().center()
+ .build()
+ .setHeight("100vh")
+ .setSpacing("var(--dwc-space-xl)");
+
+ FlexLayout root = this.getBoundComponent();
+ root.setJustifyContent(FlexJustifyContent.CENTER)
+ .add(layout);
+
+ // Register a listener for the custom event
+ dispatcher.addListener(CustomMessageEvent.class, e -> {
+ statusText.setText("Received custom event");
+ button.setEnabled(false);
+ });
+
+ // Fire the custom event with a message when the button is clicked
+ button.onClick(e -> {
+ dispatcher.dispatchEvent(new CustomMessageEvent(this, "Hello from custom event!"));
+ });
+
+ button2.onClick(e -> {
+ statusText.setText("Waiting for custom event");
+ button.setEnabled(true);
+ });
+ }
+}
diff --git a/src/main/resources/static/css/advanced/eventDispatcherCustomEvent.css b/src/main/resources/static/css/advanced/eventDispatcherCustomEvent.css
new file mode 100644
index 000000000..0750485d8
--- /dev/null
+++ b/src/main/resources/static/css/advanced/eventDispatcherCustomEvent.css
@@ -0,0 +1,7 @@
+.statusText {
+border: 2px solid #333;
+border-radius: var(--dwc-border-radius-s);
+padding: 8px 24px;
+font-size: var(--dwc-font-size-l);
+background: var(--dwc-surface-3);
+}