Search code examples
javascriptreactjsiframeweb-componentshadow-dom

What is the best way to encapsulate React app?


I am trying to encapsulate a React application in a Web component's Shadow Root or an iFrame to build a widget for a Chatbot messenger like Intercom widget.

I´m using:

React 16.8.6
Typescript 3.5.3
Redux 4.0.4 
Styled-components 4.3.2 
Material-UI 4.3.3

I based my Web component choice on this React official doc (that didint mention any of my issue).

First, I encountered some style issue, with both styled-components and Material-UI, because they both put their styles in the light DOM header. But, I figured out how to deal with both issues (I can help anyone with the same problems). [SOLVED]

Then my components are rendered without a problem, but events are not working, After some research I found that when you render a react component inside Shadow DOM events will not be dispatched to react. I.e. when a user clicks on your react component nothing happens. so i used this repo to fix this issue and retarget and dispatch events to React. [PARTIALLY-SOLVED]

But unfortunately, this is not enough, because some events like onChange don't get fixed, and some depth like Material-UI add their own events, The problem is in React it self check this issue and they are not so motivated to fix it in a near date.[ISSUE]

So I decided to move to another encapsulation alternative, the iFrame. I didn't find a helpful tuto, repo or thread yet and all existing npm modules aren't updated neither optimized for Typescript. I need a clear way to encapsulate my app in the #document of the iFrame.

Please, propose a repo. or give me any idea of implementation, any idea could be very helpful.


Solution

  • Finally, I did encapsulate my app in the #document of the iframe successfully.

    // Create iframe.
    const iFrame: any = document.createElement("iframe")
    document.body.appendChild(iFrame)
    // Create react entry point.
    const mountPoint = document.createElement("div")
    iFrame.contentWindow.document.body.appendChild(mountPoint)
    ReactDOM.render(<App />, mountPoint)
    

    I just forgot to point on the x.contentWindow.document.body element, instead, I pointed directly on the iframe.