Content Management and Capture

 View Only

Embedding UI components with different technologies

By Kai Zhang posted Fri June 10, 2022 10:41 AM

In the last blog I discussed the approach to incrementally migrating the UI from a legacy framework to modern technologies. The approach worked well in Content Analyzer 1.5 when we were migrating the UI from the legacy AngularJS 1.7 to React. However, we also saw the limit of the approach that the migration has to be at the page level. In some circumstances, you have to load the incompatible components and run time on the same page with the new code. This is a tricky situation and we should try to avoid it as much as possible, but there are a few legitimate use cases:

  1. You have migrated the page to the new technology, but there are existing customers running custom UI components in that page. You need to make sure those custom UI components continue to work in the new page.
  2. You have developed a complex component using the new technology, and you want to reuse it in the legacy page that will won't be migrated for a while. (that's actually what we ran into)
We solve this problem by creating an adapter app that wraps the non-compatible UI component into a web app and embeds it in the parent page using iframe. Note that iframe introduces extra complexity such as difficulties in sharing data and challenges on layout, thus this approach should be used as a supplemental when absolutely necessary.

Embedding the incompatible component

It's easy to create a Webpack entry point using the approach mentioned in my last blog for the adapter app. Technically, the entry point is an isolated web app. You can do whatever is needed to load the component in it. For example, there may be some legacy registry mechanisms for loading the custom UI that doesn't work with the new technologies on the parent page. You can put them in the isolated adapter app. Assuming the path to the adapter app is /adapter-app/index.html, you just need to embed the adapter app in an <iframe> like this:
<iframe src="/adapter-app/index.html"></iframe>
As the <iframe> shares the same domain as the parent page, the code inside the iframe has access to the cookie and thus inherits the login session automatically.

Data exchange

In most of the use cases, you'll need to deal with data exchange between the parent page and the code running inside the iframe. If you just need to pass in simple data like an ID to the iframe and let it load the data from the server, you can simply use query parameters in the URL being assigned to the iframe. However, if you need to pass complex data back and forth, the best way is to use message event. If you want to implement bi-directional data exchange, you'll need to listen to the "message" event in both the parent page and the adapter app inside the iframe like this:
window.addEventListener('message', event => {
    // parse the and take the action accordingly
const data = JSON.parse(;
When you want to send data from the parent page to the adapter app inside the iframe, you can do it like:
const serializedData = JSON.stringify(data);
document.getElementById('my-iframe-id').contentWindow.postMessage(serializedData, window.location.origin);
When you want to send data from the app inside the iframe to the parent page, you can do it like:
const serializedData = JSON.stringify(data);
window.parent.postMessage(serializedData, window.location.origin);


This blog provides a supplemental to the migration approach discussed in my last blog when we have to run both the new code and the old code on the same page. As they don't work together natively, we leverage the multiple entry points mechanism to wrap the incompatible components into an app, and we use <iframe> to embed it into the parent page. Code in the parent page and inside the <iframe> can communicate with each other by the message event. While the iframe allows the incompatible code to work together on the same page, it should not be used extensively due to the extra complexity and limitations.