LWC Events – Explained Clearly

What is an Event?

Events represent occurrences, such as network activity or user interactions like button clicks, in the form of objects.

In Lightning Web Components (LWC), events are based on DOM Events and can be categorized into two main types of interfaces:

  • CustomEvent Interface
  • EventTarget Interface

We highly recommend using the CustomEvent interface to create events in LWC. This interface offers a consistent experience across various browsers, including Internet Explorer. Additionally, it requires no setup or boilerplate code, and it provides flexibility by allowing you to pass any type of data via the detail property.

It’s crucial to send only primitive data when working with events. JavaScript passes all data types by reference except for primitives. If a component includes an object in its detail property, any listener can modify that object without the component being aware of it. This is undesirable behavior. It’s recommended to either send only primitives or to copy the data into a new object before adding it to the detail property.

Copying the data into a new object guarantees that only the intended data is sent, and it prevents the receiver from altering your data.


Lightning web components leverage the EventTarget interface, enabling them to dispatch, listen for, and handle events.

When using the CustomEvent() constructor, a mandatory parameter is the event type, specified as a string. As a component developer, you define the event type when creating the event. You have the flexibility to choose any string as your event type, but it’s advisable to follow the DOM event standard:

  • Avoid uppercase letters
  • Eliminate spaces
  • Utilize underscores to separate words


Avoid using the prefix “on” in your event names, as inline event handler names are required to start with “on.” For instance, if your event is named “onmessage,” the corresponding markup would be <c-my-component ononmessage={handleMessage}>. This duplication of “onon” can be perplexing and should be avoided.

// Prevents the anchor element from navigating to a URL.
        event.preventDefault();
import { LightningElement, api } from 'lwc';
 
export default class ContactListItem extends LightningElement {
    @api contact;
 
    selectHandler(event) {
        // Prevents the anchor element from navigating to a URL.
        event.preventDefault();
 
        // Creates the event with the contact ID data.
        const selectedEvent = new CustomEvent('selected', { detail: this.contact.Id });
 
        // Dispatches the event.
        this.dispatchEvent(selectedEvent);
    }
}

Event Handling

There are two methods for event listening: declaratively through the component’s HTML template or programmatically using an imperative JavaScript API. It’s recommended to listen from the HTML template as it minimizes the code required. To manage events, you should define methods within the component’s JavaScript class.

Add an Event Listener in a Declarative Manner

Define the listener in the markup within the template of the parent component, in this case, c-parent.

<!-- parent.html -->
<template>
    <c-child onnotification={handleNotification}></c-child>
</template>

Create the handler function, named handleNotification in this case, within the c-parent JavaScript file.

// parent.js
import { LightningElement } from 'lwc';
export default class Parent extends LightningElement {
    handleNotification(){
        // Code runs when event is received
    }
}

Add an Event Listener Using Imperative Programming

Specify both the listener and the handler function within the c-parent JavaScript file.

// parent.js
import { LightningElement } from 'lwc';
export default class Parent extends LightningElement {
  constructor() {
    super();
    this.template.addEventListener('notification', this.handleNotification);
  }
  handleNotification = () => {};
}

There’s no requirement to manually remove the listener. When the same listener is added to the same element multiple times, the browser automatically removes the duplicates.

If you prefer not to remove the event listener, you can also opt to include the handleNotification code directly within the addEventListener() call.

this.template.addEventListener('notification', evt => {
  console.log('Notification event', evt);
});

Avoid using addEventListener(eventName, this.handleNotification.bind(this)) as it is considered an anti-pattern. This is because bind() returns a new function instance, preventing the component from using removeEventListener() with the same function instance. This behavior can lead to a memory leak caused by the listener not being able to use the same function instance.

There are two ways to add an event listener. One method is to add an event listener to an element within a component’s shadow boundary. The other method is to add an event listener to an element that the template doesn’t own, such as an element passed into a slot.

To add an event listener to an element within the shadow boundary, use the template.

	
this.template.addEventListener()

To attach an event listener to an element that isn’t owned by a template, simply use addEventListener directly.

	
this.addEventListener()

To obtain a reference to the object that triggered the event, utilize the Event.target property, which is a component of the DOM API for events.