Ionic components in dynamic html or iframe

I would like to display dynamic HTML content in an ionic (react) app and enrich it with ionic components.

I know, this is highly discouraged. After browsing the forum, there are quite a few questions like this.
And the answer is always: Don’t have an API that returns HTML, have an API that returns data. Then use that data to inflate your components.

Which makes a lot of sense. But I think in this case it’s really justified:
I would like to display an ebook in epub format. That format is basically a collection of static websites, and some special files with metadata.

To display a chapter from the book, I can extract its body and use dangerouslySetInnerHTML to add it to one of my components. Or I can supply the entire html page as srcDoc on an iframe.

This works, my barebones ereader is up and running. But now I would like to enrich the text in the book with some custom components for markup. Here I would be really grateful for some feedback and discussion.

The first thing I realized is that this is near impossible with React. You can use dangerouslySetInnerHTML to embed dom nodes inside of the virtual dom. But then there is no going back, you can’t get components that need to live on the virtual dom in this dangerous inner HTML. That means that I can’t have my react components inside of the ebook markup.

Ok, but what about web components. They live in the dom, surely I can embed those in the ebook. It took me very long to realize that I can’t use the react components, like <IonButton> for this. Instead it’s <ion-button> and an empty import statement import "@ionic/core" to get the bundler to work. But now I can add Ionic components in the dangerous inner HTML. There are ionic styled buttons and badges in the ebook now.
This does not work for iframes, since they don’t share JS and CSS with the parent page. I have not yet figured out how to inject the import "@ionic/core" into the iframe (in bundled code, no CDN).

So it’s possible to get the ionic web components inside of dynamic markup. This is nice, but I need more functionality. The markup in the book needs to display popovers and make callbacks.

I could use jQuery, to wire up the custom logic with these ionic web components. This seems like a bad idea. Have jQuery in a React codebase, mix declarative and imperative programming, ew…

So how do I get this set up properly?
Is this a good usecase for a web component compiler like Stencil? I could mix react components and stencil components in my project. And embed the stencil components in the dynamic html markup, to still get consistent ionic styling.
Is there a good way to wire this up?
I mean, stencil could encapsulate some of the logic in these custom components.
But I still need to find them and hook up callbacks.

At this point I’m very confused and would be really happy for some discussion.
By the way, my processing pipeline looks somewhat like this:

  • load epub and parse manifest data -> list of html pages as text
  • process html page -> still text, but maybe with embedded <custom-tags>
  • reader component is loaded -> node in the virtual dom where custom html needs to be displayed

oh, regarding hooking up those dynamic web components to my app.

Maybe I should use something like redux for it.
What do you think?

How does stencil play with reduc?

Well, redux might be overkill. But I thought that just having stencil web components and communicating over events might be enough.

But from the stenciljs docs:

The largest deficiencies that React currently has when it comes to working with standard HTML Custom Elements is that properties that contain non-scalar data (that is, data that is not a string or number) are not passed properly and custom events are not handled properly. The solution to both of these problems is to wrap the Custom Element in a React component, obtain a ref to the Custom Element, and use the ref in order to set the non-scalar properties and add event listeners via addEventListener .

I can’t do that. There doesn’t seem to be a way to have react components inside of the dynamic html.

Maybe I should switch to preact :see_no_evil: