Component Communication — Complete Guide
In this tutorial, you will learn about Component Communication. We cover key concepts, practical examples, and best practices to help you master this topic.
Component communication in Web Components uses attributes, properties, custom events, and shared state for parent-child and sibling component interaction.
What You'll Learn
- Parent to child: attributes and properties
- Child to parent: custom events
- Sibling communication: shared event bus
- Cross-component state management
Why It Matters
Components rarely exist in isolation. A form input must communicate validation status to the parent form. A tab panel must communicate selection changes. Clean communication patterns prevent spaghetti code.
flowchart LR A[Parent] -->|attributes| B[Child] B -->|custom events| A C[Sibling 1] -->|event bus| D[Sibling 2]
Property vs Attribute Communication
class DisplayPanel extends HTMLElement {
constructor() {
super();
this.attachShadow({ mode: 'open' });
}
set data(value) {
// Property setter - used for complex data
this._data = value;
this.render();
}
get data() {
return this._data;
}
render() {
this.shadowRoot.innerHTML = '<pre>' + JSON.stringify(this._data, null, 2) + '</pre>';
}
}
customElements.define('display-panel', DisplayPanel);
// Usage:
// const panel = document.querySelector('display-panel');
// panel.data = { complex: 'object' }; // Property, not attribute
Shared Event Bus
const EventBus = {
_listeners: {},
on(event, callback) {
if (!this._listeners[event]) this._listeners[event] = [];
this._listeners[event].push(callback);
},
off(event, callback) {
if (!this._listeners[event]) return;
this._listeners[event] = this._listeners[event].filter(cb => cb !== callback);
},
emit(event, data) {
(this._listeners[event] || []).forEach(cb => cb(data));
}
};
class FilterPanel extends HTMLElement {
connectedCallback() {
this.shadowRoot.querySelector('input')
.addEventListener('input', (e) => {
EventBus.emit('filter-change', { query: e.target.value });
});
}
}
customElements.define('filter-panel', FilterPanel);
class ResultList extends HTMLElement {
connectedCallback() {
EventBus.on('filter-change', (data) => {
console.log('Filter changed:', data.query);
this.filter(data.query);
});
}
disconnectedCallback() {
// Clean up by tracking event bus listeners
}
}
customElements.define('result-list', ResultList);
Common Mistakes
- Mixing attribute and property APIs inconsistently
- Not cleaning up event bus listeners in disconnectedCallback
- Passing complex objects through attributes (use properties instead)
Practice Questions
- When should you use properties instead of attributes? For complex objects, arrays, or functions.
- How do siblings communicate? Through a shared event bus, a parent Mediator, or a state management pattern.
Mini Project
Build a master-detail view. A list component emits item-selected events. A detail component listens and displays the selected item details. Use a parent mediator component that coordinates the two children.
What's Next
Lesson 16: Styling Hosts
Built by the developers of DodaTech
Doda Browser, DodaZIP & Durga Antivirus Pro