
Creating and Dispatching Custom Events in JavaScript

Most new JavaScript developers meet events through built‑in browser interactions such as click, submit, and change. Those are the obvious ones because the browser gives them to you out of the box.
But the event system is not limited to the browser's built‑in list. You can create your own events too.
That becomes useful when one part of your front‑end needs to announce that something meaningful has happened without being tightly coupled to the code that will respond.
In other words, custom events let your UI speak in its own vocabulary rather than only in the browser's native one.
Why Custom Events Exist
The browser already gives us DOM events for low‑level interactions:
- a button was clicked
- a form was submitted
- an input changed
Sometimes, though, your application cares about a higher‑level event:
- the cart was updated
- the modal finished opening
- a product was selected
- a filter changed and new results are ready
You can represent those moments with callbacks or direct function calls, of course. But custom events give you a looser way to signal them across DOM‑driven code.
That can be especially useful when several different pieces of code may want to listen without all being wired together explicitly.
The Basic Pattern
The browser provides the CustomEvent constructor for this.
You create an event:
const event = new CustomEvent('cart:updated');and then dispatch it from an element:
document.dispatchEvent(event);Anything listening for that event can then respond:
document.addEventListener('cart:updated', () => { console.log('Cart updated');});That is the basic pattern:
- create the event
- dispatch it
- listen for it elsewhere
Naming Matters More than People First Think
The event name is just a string, but it does a lot of work.
If the name is vague, the event becomes vague. If it is specific, the event becomes much more useful.
For example:
updatedis weakcart:updatedis clearermodal:openedis clearer thanshow
This is one of the reasons custom events can be useful architecturally. They force you to name the meaningful things your interface is doing.
Passing data with detail
The real usefulness starts once you attach data to the event.
const event = new CustomEvent('product:selected', { detail: { id: 42, name: 'Notebook', },});Now a listener can read that payload:
document.addEventListener('product:selected', (event) => { const customEvent = event as CustomEvent<{ id: number; name: string }>; console.log(customEvent.detail.id); console.log(customEvent.detail.name);});The detail property is what makes custom events far more than just signal flags. It lets you include context that listeners may need.
Dispatching from a Specific Element Can Be Cleaner
You do not have to dispatch every custom event from document.
In many cases it is cleaner to dispatch from the element or component most closely associated with the event.
const panel = document.querySelector<HTMLElement>('.filters');panel?.dispatchEvent(new CustomEvent('filters:changed', { detail: { category: 'books', },}));That can make the event flow feel more local and intentional.
You can still listen on that element directly, or allow the event to bubble if you want ancestors to hear it.
Bubbling is Optional, but Often Useful
By default, a custom event does not automatically bubble unless you ask for it.
const event = new CustomEvent('filters:changed', { bubbles: true, detail: { category: 'books', },});If you dispatch this from a child element, ancestors can listen for it just like other bubbling DOM events.
That is helpful when:
- a small component needs to notify a larger container
- delegated listeners are being used
- you want the event to participate naturally in the DOM event flow
If you leave bubbles out, the event only fires on the target where it was dispatched.
A Practical Example: A Tab Component
Imagine a basic tab interface where clicking a tab should also notify another part of the page.
const tabs = document.querySelector('.tabs');tabs?.addEventListener('click', (event) => { const target = event.target as HTMLElement; const tab = target.closest<HTMLButtonElement>('[data-tab]'); if (!tab) { return; } const tabName = tab.dataset.tab ?? ''; tab.dispatchEvent(new CustomEvent('tab:selected', { bubbles: true, detail: { tab: tabName, }, }));});A parent listener could then react:
document.addEventListener('tab:selected', (event) => { const customEvent = event as CustomEvent<{ tab: string }>; console.log(`Selected tab: ${customEvent.detail.tab}`);});That lets the tab interaction stay locally implemented whilst still broadcasting a meaningful event outward.
Custom Events are Useful, but They are Not Always the Best Choice
This is worth saying because beginners can easily overuse them once they discover them.
Custom events are most useful when:
- you are working in DOM‑heavy code
- components need loose communication
- several listeners may care about the same event
- the event represents something meaningful in the UI
They are less useful when:
- a direct function call would be clearer
- the communication is entirely local
- no real decoupling benefit exists
In other words, custom events are a tool for communication boundaries, not a replacement for all ordinary function calls.
detail should carry useful payloads, not whole applications
The detail object is powerful, but it should still be used sensibly.
Passing a small payload such as an id, name, or selected option is usually fine. Passing huge mutable structures around because you do not want to think about boundaries is less healthy.
Again, this is not a hard technical ban. It is more an engineering judgement point. The cleaner the event contract, the easier the system is to reason about.
You Can Stop or Cancel Custom Events Too
Custom events still participate in the DOM event system, which means familiar concepts such as propagation and cancellation can still matter.
If the event is configured to bubble, listeners can stop propagation. If it is configured as cancelable, listeners can call preventDefault().
That makes custom events feel like proper first‑class events rather than merely ad hoc messages.
Wrapping up
Custom events let you describe meaningful application moments in the same event system the browser already uses for native interactions. By creating a CustomEvent, attaching useful data through detail, and dispatching it deliberately, you can build looser communication between parts of the interface without forcing everything into direct hard‑coded calls.
Key Takeaways
- Custom events let your application announce meaningful UI events beyond the browser's built‑in list.
CustomEventcreates the event, anddispatchEventsends it.- The
detailproperty carries extra data for listeners. - Bubbling is optional and must be enabled if you want ancestors to hear the event.
- Custom events are best used when looser communication genuinely improves the code.
Used sensibly, custom events can make DOM‑heavy front‑end code feel much more modular without becoming overly abstract.
Related Articles

Mastering CSS for Freelance Web Development Projects. 
A Brief Look at JavaScript’s Temporal Dates and Times API. A Brief Look at JavaScript's
TemporalDates and Times API
CSS aspect‑ratio for Responsive Layouts. CSS
aspect‑ratiofor Responsive Layouts
CSS Focus Styles for Keyboard Users Only. CSS Focus Styles for Keyboard Users Only

Disabling Gatsby Telemetry. Disabling Gatsby Telemetry

Fast and Slow Pointers: Solving the 'Linked List Cycle' Problem. Fast and Slow Pointers: Solving the 'Linked List Cycle' Problem

Reduce() in JavaScript. reduce()in JavaScript
Add Two Numbers in TypeScript: Linked Lists Without the Hand‑Waving. Add Two Numbers in TypeScript: Linked Lists Without the Hand‑Waving

Longest Substring Without Repeating Characters in JavaScript. Longest Substring Without Repeating Characters in JavaScript

Creating Progressive Web Apps (PWAs) with Angular. Creating Progressive Web Apps (PWAs) with Angular
What is a Distributed Denial of Service (DDoS) Attack? What is a Distributed Denial of Service (DDoS) Attack?

Understanding the Module Pattern in JavaScript. Understanding the Module Pattern in JavaScript