RFC: Embedding a React application with iframe to Foreman's UI stack

RFC: Embedding a React application with iframe to Foreman’s UI stack

We are posting this to gather community feedback before committing further.

Context and Problem Statement

  • What is the problem being solved?

    • We need to ship an existing React application inside Foreman. That app’s UI stack (newer React + PatternFly + React Router) conflicts with Foreman’s current frontend (different major versions that can not share one runtime cleanly). We prototyped loading those newer apps in an iframe so we can keep one PF6 codebase.
  • How does this affect the user or developer?

    • Users: Pages that use the iframe will show mixed UI elements — Foreman stays on PatternFly 5, while the embedded app is using PatternFly 6. Foreman still owns the header, navigation and overall page shell.
    • Developers: In the short term this avoids maintaining a large parallel set of Foreman-shaped page remotes. In the long term, the goal is to align with Foreman’s UI stack so we are not carrying version conflicts.
  • Background information that helps understand the current state.

    Foreman and several plugins currently run on one frontend stack (roughly React 16, PatternFly 5, React Router v5). Separately, there are React apps that also ship outside of Foreman (for example, hosted console deployments).

    Some of those applications are already used with Foreman in Insights on Premise (IoP). Earlier once were onboarded via module federation, usually as page-level remotes with a small Foreman-side wrapper:

    Foreman owns routing (React Router v5) and loads a federated remote module per page.

    Route params (cveId, systemId, …) are read in Foreman and passed into the remote as props, so the remote does not need its own router.

    The wrapper sets up Foreman-specific context (API base paths, permissions / fake chrome, providers such as React Query) — not a second React runtime.

    At runtime, React is the host singleton (Foreman’s React 16). The remote’s package.json may list React 18 for the other product / local builds; inside Foreman those page modules render in Foreman’s React tree. That works when the IoP UI stays compatible with that host and still targets PatternFly 5.

    So federation here is “mount Foreman-shaped page components into the existing tree,” not “run React 18 + Router v6 next to Foreman.”

    The app we are trying to onboard is substantially more complex than the ones already federated into Foreman: many more views, wizards, and modals, not just a handful of page remotes. Fitting that into the existing model — aligning with Foreman’s dependency versions and wrapping every screen as a Foreman-shaped page module — would be a large parallel UI surface. We also want to avoid PatternFly version hacks (PF6 app forced to behave like PF5, or dual styling workarounds). Those shortcuts become expensive later when we have to maintain per-Foreman release branches, handle CVEs across diverging stacks, etc.

  • Include any technical objectives

    • Show the application in Foreman’s UI (menu pages and, in some cases, host-details tabs)
    • Reuse most of the existing application code (views, wizards, modals).
    • Keep the onboarded app on PatternFly 6 depending of Foreman’s migration.
    • Keep release-branch maintenance manageable (fixed app builds per Foreman release).

Proposal

  • Include all relevant details of the solution
    • Stack isolation — onboarded app keeps PatternFly 6 and its own router; Foreman keeps React 16 / PatternFly 5 / Router v5. No need to align major UI dependencies just to ship the feature.
    • One shared codebase — we reuse the existing app components as they are (including views, wizards, and modals) instead of re-implementing them as Foreman-shaped page remotes.
    • Less version hacking — we avoid PF5/PF6 dual-styling workarounds and other compatibility shims that are hard to maintain.
    • Easier release branches — the onboarded app can move forward on its own. Each Foreman release can use a fixed build of that app. We do not have to keep federated shared dependencies in sync across very different branches.
    • Repeatable host pattern — once the iframe bridge exists (permissions, initial route, limited URL sync), other complex apps in the same situation can follow it.
  • Include any technical and project level impacts
    • Onboarded app shipped as static assets and embedded by the Foreman plugin.
    • Small messaging protocol between host and iframe for permissions and routing sync.
    • Temporary mixed PF5 / PF6 presentation in the product UI.

Alternative Designs

  • Include any alternative solutions, and why they are not preferred
    • Continue with federated page remotes (current model for simpler apps)

      Not preferred for this app: too many views / wizards / modals to wrap as Foreman-shaped remotes; would create a large parallel UI and push us toward PatternFly version hacks and painful branch/CVE maintenance.

Decision Outcome

What was the decision that was ultimately made and relevant outcomes with links.

Impacts

Record impacts that the decision has such as limitations, changes to the architecture, affects to workflows, processes or user facing consequences:

  • Architecture: iframe isolation + postMessage bridge.
  • UX: temporary mixed PatternFly 5 and PatternFly 6 in different areas of the UI.
  • Workflows: guest app can release on its own timeline; Foreman releases pin a known static build.
  • Process: Onboarding our app with iframe. And applying iframe solution to other apps that have been onboarded some time ago but still using fed modules solution.

What is the Foreman plugin in this design? Is this foreman_rh_cloud or is this some new plugin explicitly for this purpose? Is this how the assets are provided in production?

I think that element is not clear. Today, the federated modules for iop are laid down on the file system and served by Apache. How are they served in this iframe model?

Can you expand a little on what the small messaging protocol is?

I think one thing that might help when reviewing this would be to see a screenshot or two of the PF6 app embedded in Foreman.

I think this method as a stop-gap to onboard this sooner into the community is a fine approach as long as our goal is to converage to a consistent architecture across plugins + services.

By foreman plugin I meant foreman_rh_cloud. The iframe host lives there as ComplianceIframe.

Same general model — Compliance app is a static build (e.g. index.html + JS/CSS) under /assets/apps/compliance/index.html, packaged and laid down like other IoP frontend assets. And then Apache will serve it.

About small messaging protocol I meant that there are few message types between parent and iframe like:

  1. IOP_READY (Compliance mounted and ready communicate)
  2. IOP_NAVIGATE (helps to sync navigation for Compliance and Foreman)
  3. IOP_INIT (we pass user, permissions, route, etc)

@ehelms Here you have some screenshots of how app looks like with iframe and pf6. You can see Compliance SCAP policies, Report details, Create/Edit policy modals and Inventory host details Compliance tab

A few questions -

  1. Can we get more details on how the messaging between the iframe and Foreman will work? What happens when you refresh the page in the browser? do you lose your place in the compliance app?
  2. Could a savvy user alter the permissions being passed into the iframe using browser dev tools? Is there any way to mitigate this, like having the app verify permissions it receives?
  3. Will Foreman need to do a lot of heavy lifting around content sizing / double scroll bars / visual oddities?
  4. Is this solution intended for the next app as well, or just Compliance?

Thanks for the questions!

  1. For app and foreman apps we use window.postMessage() that allows communication between Foreman and iframe app ( Window: postMessage() method - Web APIs | MDN ). Using this method, we do navigation, permissions sync.
    On reload, Foreman keeps the browser URL for page-level routes and sends the matching route in an init message; Compliance then loads that content. Modal/wizard routes are not in the Foreman URL, so those are not restored after a full refresh.

  2. yes, savvy user can temper with the client-side data. Federated Insights apps (Advisor, Vulnerability, etc) already receive permissions from Foreman on the client (via Scalprum/chrome); iframe does the same via postMessage. In both cases that’s for UI only - the API enforces access on the server.

  3. Foreman doesn’t need anything big. Most of the changes are already here feat(Compliance): onboarding compliance frontend iframe by LightOfHeaven1994 · Pull Request #1225 · theforeman/foreman_rh_cloud · GitHub. Additionally we will need to add Scrolling helper for Compliance in Inventory details which will help to properly calculate page size to avoid second scroll bar.

  4. Other apps could be onboarded with iframe too, but at the same time, when Foreman will be aligned with React/router/PF6 versions with Insights apps - we could get rid of iframe and other tricks that are currently used in Advisor, Vulnerability.