This content originally appeared on Go Make Things and was authored by Go Make Things
Yesterday, I had an interesting chat with a friend about how I would share state between Web Components, and get them to talk to each other in a web app.
Today, let’s explore what that might look like!
An ecommerce platform
An example I like to use for this kind of thing is an ecommerce platform with a few interconnected components.
You might have a <cart-link>
Web Component that displays a link to the checkout page, with the number of items in the cart as it’s text.
<cart-link>
<a href="/checkout">🛒 2 Items</a>
</cart-link>
You might also have a <product-listing>
Web Component that displays an Add to Cart button if the item isn’t in your text, or a message if it is.
<!-- Not in the cart -->
<product-listing price="39" uid="tshirt-jolly-roger">
<button>Add to Cart - $39</button>
</product-listing>
<!-- Already in the cart -->
<product-listing price="39" uid="tshirt-jolly-roger">
<em>This is item in your cart</em>
</product-listing>
And powering both of these Web Components is a cart
variable—an object ({}
)—that holds the items currently in the cart.
let cart = {};
Whenever an item is added to the cart
, the <cart-link>
element needs to update the text about how many items are in the cart. The <product-listing>
also needs to update itself to show either a <button>
or message depending on whether the item is in the cart.
Let’s take a look at how to wire up all of these pieces so they can talk to each other.
A simple signal
To make this work, we’ll create a Signal
class that will actually hold the cart
data.
Whenever the data is updated, the Signal
will emit a custom event that the other Web Components can listen to. We’ll start by storing whatever value is passed in to the this.value
property.
class Signal {
// Initialize the class
constructor (val) {
this.value = val;
}
}
We’ll create a new Signal()
for the cart
like this…
let cart = new Signal({});
Next, we’ll add an add()
method that accepts a key
and val
as arguments.
The method will add or update the key
in the this.value
object. Then, it will emit a custom cart-updated
event.
Note: In a real production app, I’d either use setter and getter methods or Proxies for this. But this is a useful, simple model for teaching.
class Signal {
// Initialize the class
constructor (val) {
this.value = val;
}
// Add a new item
add (key, val) {
// Update the value
this.value[key] = val;
// Create a new event
let event = new CustomEvent('cart-updated', {
bubbles: true,
detail: {key, val}
});
// Dispatch the event
return document.dispatchEvent(event);
}
}
While we’re here, let’s also add a size()
method that returns the number of items in the this.value
object, and a has()
method that checks if a key
is in the this.value
object or not.
class Signal {
// ...
// Get the number of items in the cart
size () {
return Object.keys(this.value).length;
}
// Check if an item is in the object
has (key) {
return !!this.value[key];
}
}
Creating the <cart-link>
component
For the <cart-link>
component, we’ll create a render()
method that gets the cart.size()
, and displays it as a link inside the custom element.
customElements.define('cart-link', class extends HTMLElement {
// Instantiate the Web Component
constructor () {
// Inherit parent class properties
super();
// Render the initial UI
this.render();
}
// Render the UI
render () {
this.innerHTML = `<a href="/checkout">🛒 ${cart.size()} Items</a>`;
}
});
We’ll add an event listener for the cart-updated
event, and use the handleEvent()
method to run the render()
method whenever the cart
is updated.
customElements.define('cart-link', class extends HTMLElement {
// Instantiate the Web Component
constructor () {
// Inherit parent class properties
super();
// Render the initial UI
this.render();
// Listen for cart events
document.addEventListener('cart-updated', this);
}
// Handle Events
handleEvent (event) {
this.render();
}
// Render the UI
render () {
this.innerHTML = `<a href="/checkout">🛒 ${cart.size()} Items</a>`;
}
});
Now, whenever the cart
is updated, the <cart-link>
gets updated automatically.
🧐 Want to learn more about Web Components? I’m adding all of my guides around building Web Components to my members-only toolkit. Joining is a great way to support my work!
Creating the <product-listing>
component
We’ll do something similar for the <product-listing>
Web Component.
When the Web Component instantiates, we’ll use the Element.getAttribute()
method to get the [uid]
and [price]
attributes, and save them to properties.
Then well run a render()
method to render the UI.
customElements.define('product-listing', class extends HTMLElement {
/**
* Instantiate the Web Component
*/
constructor () {
// Inherit parent class properties
super();
// Set properties
this.uid = this.getAttribute('uid');
this.price = parseFloat(this.getAttribute('price'));
// Render the initial UI
this.render();
}
});
Inside the render()
method, we’ll use the cart.has()
method to check if this.uid
is in the cart
already.
If it is, we’ll show a message. If not, we’ll show a button to add the item to the cart, with this.price
as part of the button text.
// Render the UI
render () {
this.innerHTML = cart.has(this.uid) ? '<em>This is item in your cart</em>' : `<button>Add to Cart - $${this.price}</button>`;
}
Next, we’ll add an event listener for the cart-updated
event. We’ll also listen for click
events inside the custom element.
/**
* Instantiate the Web Component
*/
constructor () {
// Inherit parent class properties
super();
// Set properties
this.uid = this.getAttribute('uid');
this.price = parseFloat(this.getAttribute('price'));
// Render the initial UI
this.render();
// Listen for events
document.addEventListener('cart-updated', this);
this.addEventListener('click', this);
}
Inside the handleEvent()
method, we’ll check if the event.type
is cart-updated
.
If so, we’ll run the render()
method to update the UI, and return
to end the function. If the event.type
is click
, and the event.target
(the clicked element) is or is inside a button
, we’ll run the cart.add()
method to add this.uid
to the cart
object.
// Handle events
handleEvent (event) {
// If it's a cart update event
if (event.type === 'cart-updated') {
this.render();
return;
}
// If it's a click on the button
if (event.type === 'click' && event.target.closest('button')) {
cart.add(this.uid, this.price);
}
}
Adding the item will trigger the cart-updated
event, which will trigger a render of the UI.
Putting it all together
Here’s a demo you can use to play around with this idea. And if you’d prefer, here’s the downloadable source code on GitHub.
Depending on the use case, there are other ways you might handle this kind of thing. But this would probably be my starting point for an app like this.
Like this? A Lean Web Club membership is the best way to support my work and help me create more free content.
This content originally appeared on Go Make Things and was authored by Go Make Things

Go Make Things | Sciencx (2025-01-21T14:30:00+00:00) How would you share state between Web Components?. Retrieved from https://www.scien.cx/2025/01/21/how-would-you-share-state-between-web-components/
Please log in to upload a file.
There are no updates yet.
Click the Upload button above to add an update.