The Magic Behind our Live Preview

Eugenio Lattanzio
Ordergroove Engineering
6 min readJan 30, 2021

My team has been hard at work for the last few months enabling preview functionality for one of our self-service features.

Problem Statement

We have a CMS tool which allows admin users to customize the CSS and HTML layout of content which we inject into their e-commerce site. The existing version of our tool is not able to render an exact preview of this content as the e-commerce website has global CSS styles which override ours. This meant that we needed a way to allow admin users to preview their changes directly on their site.

A little bit of background before we jump to the solution

Ordergroove allows customers to subscribe to products in the merchant’s e-commerce website. We accomplish this by loading a javascript file on the page. Our javascript includes code which is responsible for rendering Ordergroove specific content through the use of native Web Components.

i.e. When the <og-offer/> element is injected on the page, it executes some business rules and renders an offer that allows the customer to subscribe to the product “Inspire Cafe Bella Protein”

This javascript is being built with some custom configuration and styles that the e-commerce admin user can set in our self-service tool.

Admin user navigates to experience management page and enters a product page url of their website where they would like to preview the experience. Then user hits preview and new window is opened with that product page.

In order to enable the admin preview directly in their site, we need some sort of check in our javascript to detect if user browsing the site is an admin or a real customer.

But, how do we detect if the user session is in preview mode?

Our team researched and played around with a couple of ideas on how to enable this functionality. Some things which we considered:

  • 👎 Detect some cookie on the merchant site. Merchant had to set that cookie based on some condition.
  • 👎 Detect some query string parameter such us ?ogPreviewMode=true, so user has to access it with custom url flag. We decided not to do this because some e-commerce stores strip out unknown query string parameters.

Ultimately we landed on and elegant solution which leverages the highly supported and proven postMessage.

The preview protocol

When our javascript code loads in their site we check if window.opener is set. If the opener is our self-service tool we start looking for configuration changes and then re-render our widgets. We need to make sure to only show our preview if their site has been opened by rc3.ordergroove.com.

We thought that we have landed on a good solution but then we were hit with another problem — cross-origin security policies. Since both windows lives in different domain, CORS restriction prevents us from checking if we are the ones who opened the window prohibiting check the opener url.

If the opener is not on the same origin as the current page, functionality of the opener object is limited. For example, variables and functions on the window object are not accessible.

Luckily for us there is already a battle-tested method which was built specifically for this scenario. The good old postMessage which was first introduced a long time ago as part of the HTML5 spec.

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.

The protocol consists of:

  1. Self-service tool opens a preview in new tab and starts listening for the readiness event.
  2. When product page is navigated, our javascript loads and checks if the window.opener is not null.
  3. If opener is set, we subscribe to window.addEventListener(“message”, …) looking for a readiness event from self-service tool.
  4. Our javascript executes postMessage(‘READY’, ‘https://rc3.ordergroove.com’) to its opener. Browser will only send this message to it matches provided origin.
  5. Self-service tool receives a READY message and sends a ready message back.
  6. Our javascript receives a READY message from the tool so the protocol gets established. We are safely in preview mode.

Here is a detailed sequence diagram

Preview protocol sequence

The offer-live-editor.js code looks like this

Reduce the footprint

One of our requirements is that our javascript should be as small as possible in terms of KB. It should load fast.

However, our javascript got bigger when we added the preview feature that was not intended to be used by the customers browsing the site. So we had to find a way have it load only when it was needed.

We added a check in our javascript to see if you are in preview mode and, if so, lazily requests another webpack chunk containing the preview library.

The preview implementation

Once we have solved for the cross domain communication problem and optimized our code, we started working on implementing the actual preview functionality.

All of our preview code is now standalone in client.js that will loads on demand. The client support some types of message and performs the preview re render such as:

  • UPDATE_VARIATION is a message that forces the current offer in the page to refresh with the new styles and content.
  • ADD_NEW_OFFER creates a new offer widget useful for merchants who did not finish their integration yet but want be able to preview the content.

Self-service tool is responsible for sending these messages with the new configuration details.

Handling some edge cases

Finally I would like to mention some edge cases that could happen in this process.

User can open a URL that does not have the javascript on it.

In this case the protocol was not established and there is no way to communicate between two applications. Once the self-service tool launches the window it also starts a timeout of 15 seconds to let the popup load properly. If nothing happens the tool alerts the user that the e-commerce platform is not properly integrated.

User can navigate the e-commerce platform during the preview.

User can enter the landing page of their e-commerce search for products the navigate to product page. In this case the javascript will load few times on their site an since window.opener reference persists across pages everything works as expected.

Conclusion

Different applications can communicate with each other by using postMessage. It could sound a bit complex design a protocol, but later the solution becomes really elegant and secure.

The underlying solution allowed us to provide a smooth user experience for our users. All the users have to do is type in a target url and they are ready to start previewing changes on their own site in real-time. A user doesn’t need enter any code or hash in the url, or even login to their site.

We are able to provide this feature without increasing the overall footprint of our code on the e-commerce site.

--

--