Skip to content

Component Communication — Complete Guide

DodaTech Updated 2026-06-28 2 min read

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

  1. Mixing attribute and property APIs inconsistently
  2. Not cleaning up event bus listeners in disconnectedCallback
  3. Passing complex objects through attributes (use properties instead)

Practice Questions

  1. When should you use properties instead of attributes? For complex objects, arrays, or functions.
  2. 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