In recent weeks I have had the opportunity to work on some projects that implement a MicroFrontEnd architecture and we used the postMessage method to enable the communication between the appliations.
So I thought....
why not use this approach also in Next applications to put two Component Clients in communication?
In our site we have an ADD TO BOOKMARK button at the bottom of each lesson and blog post, just like this one.
This is a dynamic route created through a page.tsx file, while the context panel on the right side - "Topics | Bookmarks | Courses" - contains all the course's bookmarks and its instance is created in the upper NextJS layout, layout.tsx.
The Goal
Our goal was to update the bookmark list every time the button is clicked.
As you can see from the animation below, the list is automatically refreshed when the bookmark icon is clicked or unclicked:
Obviously we could have implemented some server side solutions (for example using sockets) but it would be overkill and would have introduced an additional architectural complexity that we wanted to avoid.
As an alternative:
We could use React Context to share a state across components and layouts.
We could have used a micro state manager as Zustand , I love it for its simplicity and performance.
Both solutions have their PROS and CONS.
However this time I decided to try using a JavaScript native solution: postMessage .
Why postMessage?
The main reason is that we did not need to share a state but rather notify events between components.
I realize that is not the most appropriate use for this API, usually used to communicate different IFRAMEs, or applications in a MicroFronteEnd but it seemed to me a simple, fast and a native solution.
Infact, the MDN documentation says:
The window.postMessage() method safely enables cross-origin communication between Window objects; e.g., between a page and a pop-up that it spawned, or between a page and an iframe embedded within it.
A NextJS / React Client component can emit an event, for instance when a button is clicked.
In the example below you can see how the button emits an updateCounter event passing the increment value as parameter:
src/SenderComponent.tsx
Receiver Component
The receiving component can simply listen this event, read the value and carry out the necessary operations.
In this case, for example, we update a local counter displaying its value in the template:
We use the same mechanism to handle another scenario of this site.
In the course pages of a course we have a listener on the page scroll event.
When the page is scrolled by at least 75%, we set the lesson as read and we send a specific lessonRead event with the id of the lesson as parameter.
The Drawer component (the side panel on the left) listens for this event, read the value (the id) and selects the correct checkbox, highlighting the lesson the user has just read.
In the following animation you can see how the first lesson is highlighted at the end of the page scroll:
On this site we handle other 2 or 3 scenarios in this way and I must say it seems to work well.