Skip to content

Angular, React, and Vue in One Product

Angular, React, and Vue can run side by side in one product. This is only sensible if they remain technically separate applications and the organizational benefit justifies the permanent cost of a polyglot frontend platform.

The frameworks do not need to work with one another. They need to run cleanly alongside one another.

That means more than merely loading different JavaScript bundles into the same page. Each framework needs its own DOM area, its own state, and its own lifecycle. The host may know how an application is started and stopped. It should not need to know how that application renders internally, manages dependencies, or organizes its state.

Framework autonomy is therefore not a free benefit. It is a platform decision that must be funded permanently.

This applies not only to runtimes, build systems, and expertise. It applies to the interface as well. The frameworks may differ. The product must still not feel like three separate applications to the user.

A polyglot frontend is easiest to understand when Angular, React, and Vue are treated not as interchangeable component models, but as independent applications inside one shared product.

Angular Host
├── Angular Area
│ └── Angular Root
├── React Area
│ └── React Root
└── Vue Area
└── Vue Root

Each area has its own runtime, component tree, rendering mechanism, and local state. It is bootstrapped separately, has its own failure behavior, and must be cleaned up reliably when removed.

Angular manages its components according to Angular’s rules. React owns a root and controls the DOM area beneath it. Vue likewise brings its own component and reactivity model. For the integration, it does not matter how similar or different these models are in detail.

What matters is who owns each area.

Polyglot frontends do not work through one shared framework abstraction. They work through clear technical ownership boundaries. Trying to unify the internal models of several frameworks does not remove their differences. It merely distributes them across an integration layer that is difficult to understand.

The host provides a container to a remote. Once the remote has been mounted there, the area beneath that container belongs entirely to the corresponding framework.

Angular Host
├── Angular Root
│ └── only Angular manages this area
├── React Mount Point
│ └── only React manages this area
└── Vue Mount Point
└── only Vue manages this area

An Angular host may create a container for a React application and remove that container later. It should not modify DOM elements inside the React root. Likewise, a React component should neither access the Angular injector nor control Angular component instances.

State models do not belong across that boundary either. An Angular service does not become a React context. A React store is not modified directly from an Angular component. A Vue store does not become the shared state of all participating applications.

Such connections often look pragmatic at first. The required object already exists, both applications run in the same browser anyway, and a direct reference appears to avoid another contract. In reality, this creates a technical dependency on internal framework and application details.

The problem becomes visible at the latest during updates, tests, and failures. A team can no longer change its internal implementation without considering the other application. Lifecycles become interdependent. Cleanup becomes ambiguous. Tests have to bootstrap several frameworks at once even though they are supposed to verify only one area.

One framework may mount and remove another framework’s DOM area. It must not manage the internals of that area.

The Integration Contract Defines Start and End

Section titled “The Integration Contract Defines Start and End”

The integration does not require a shared component standard. A small, framework-neutral lifecycle contract is often sufficient.

interface RemoteApplication {
mount(container: HTMLElement, context: PlatformContext): void;
unmount(): void;
}

The host knows the container, the entry point, and the required cleanup. It can also pass a small platform context and present loading or error states.

It does not know React components, hooks, or context objects. It does not know Angular injectors or Vue component instances. Nor does it need to know how the remote stores its state or when it rerenders internally.

The host knows how the application is started and stopped. It does not know how the application works internally.

Depending on the platform, the contract may be asynchronous, return an error state, or expose additional lifecycle operations. These technical variations do not change the underlying principle: the boundary describes the integration of an application, not the remote control of its components.

The smaller this contract remains, the more independently the respective framework can evolve inside its own area.

In one real-world project, a React application was integrated into an Angular host by registering a small contract on the global window object.

window.reactRemote.mount(container, context);
window.reactRemote.unmount();

This solution is technically crude. The global object requires a unique name. The loading sequence must be controlled. Bootstrap failures must not go unnoticed. The contract needs versioning, and cleanup must not be forgotten. Tests also have to prepare and remove the global registration deliberately.

An uncontrolled, ever-growing window object should therefore not serve as a general production API.

Architecturally, however, such a contract can be surprisingly honest. Angular knows no React components. React knows no Angular services. Both frameworks have separate lifecycles. Their shared boundary consists only of a DOM container, a context, mount, and unmount.

A rustic but small framework-neutral contract can be cleaner than an elegant entanglement of two framework runtimes.

The same contract can be supplied through a controlled registry, a loader, or another integration mechanism. Its technical delivery may improve. The conceptual boundary should not become more permeable in the process.

An Angular host integrates separate Angular, React, and Vue roots through a small framework-neutral contract.

The Platform Shares Contracts, Not Framework Internals

Section titled “The Platform Shares Contracts, Not Framework Internals”

The applications usually need shared platform capabilities. Authentication, navigation, language, theme, telemetry, feature configuration, and session information cannot be treated as entirely isolated concerns for every remote.

These capabilities should still not be published as Angular services, React contexts, or Vue stores.

Platform Contract
├── Authentication
├── Navigation
├── Locale
├── Theme
├── Telemetry
├── Feature Configuration
└── Session Context

Such a contract remains small, stable, and framework-neutral. It transports neither component instances nor domain state owned by other remotes. It assumes no specific rendering cycle and does not control the internal lifecycles of the applications.

Each framework can then translate that contract into its own world.

Framework-Neutral Platform Contract
├── Angular Adapter
├── React Adapter
└── Vue Adapter

The Angular adapter can expose the data through Angular-specific mechanisms. The React adapter can provide a context or suitable hooks. The Vue adapter translates the same contract into Vue-specific structures. These adapters remain inside their respective applications.

This allows every team to work idiomatically with its framework without binding the platform to any one of them.

A polyglot frontend platform does not eliminate standardization. It moves standardization from the frameworks to the integration contracts.

Framework Separation Is Not a Browser Sandbox

Section titled “Framework Separation Is Not a Browser Sandbox”

Separate roots create technical ownership boundaries. They do not create complete isolation.

All applications still live in the same window and the same document. They share the browser history and the page’s main thread. Network, memory, and CPU resources are still consumed inside the same browser environment.

That leaves several potential conflict surfaces:

  • global styles and CSS custom properties
  • routers and browser history
  • overlays or portals under body
  • global event listeners and error handlers
  • storage keys
  • telemetry
  • custom-element registrations
  • network and main-thread load

A computationally expensive React area can still block the Angular host. A global stylesheet from a Vue remote can affect selectors outside its intended area. A dialog or tooltip can render through a portal at a point in the DOM that no longer lies inside the intended root.

Framework separation is not a browser sandbox.

This limitation is not an argument against separate roots. It merely describes which responsibilities remain with the shared platform despite clean framework boundaries.

Shadow DOM Encapsulates Only Part of the Problem

Section titled “Shadow DOM Encapsulates Only Part of the Problem”

Shadow DOM can be used as an additional integration boundary independently of the framework.

Framework Mount Point
└── optional Shadow Root
└── Framework Root

It can make the local CSS cascade easier to control. Global selectors do not reach the encapsulated area without further measures, DOM ownership becomes more visible, and some naming and style conflicts can be avoided.

React does not inherently use Shadow DOM. Angular and Vue applications are not automatically moved into a shadow root merely because they exist either.

Most importantly, Shadow DOM does not create a separate JavaScript realm. The main thread, memory, network, window, storage, and framework runtimes remain shared. The organizational maintenance cost does not disappear either.

New integration questions arise at the same time. Portals and overlays must account for the boundary. Focus behavior, forms, accessibility, and event boundaries require special attention. Global fonts, CSS custom properties, and design tokens must be defined deliberately at the boundary and checked for inheritance. Not every testing tool understands the additional encapsulation without further configuration.

Shadow DOM can encapsulate DOM and styles more strongly. It does not turn a framework area into an independent application or a separate browser context.

Three Frameworks Create Permanent Platform Costs

Section titled “Three Frameworks Create Permanent Platform Costs”

Technical feasibility is rarely the real problem. Angular, React, and Vue can all be started. Running them reliably for years is harder.

Every additional framework brings its own runtime, ecosystem, and development model.

Angular
├── Runtime
├── Updates
├── Build
├── Testing
├── Debugging
└── Expertise
React
├── Runtime
├── Updates
├── Build
├── Testing
├── Debugging
└── Expertise
Vue
├── Runtime
├── Updates
├── Build
├── Testing
├── Debugging
└── Expertise

At runtime, this may create additional downloads, repeated bootstrap work, further memory usage, and more main-thread activity. The size of these costs depends on the application and its delivery model. Additional runtimes are not free regardless.

The long-term platform costs are often larger. The organization needs expertise in every supported framework. It must plan updates and security fixes across several ecosystems. Build, testing, and debugging problems differ. Monitoring and failure analysis must cover all applications even though they behave differently at a technical level.

Platform adapters, documentation, onboarding, and shared quality rules add further work. During an incident, someone must understand not only the domain area but also its framework and toolchain.

The decisive question is therefore not whether a team can use the third framework productively today.

The decisive question is: Who will operate the third framework in five years?

The original developers may have left the organization by then. The ecosystem may have changed. The area may be functionally stable and receive only occasional maintenance. Even then, someone must still perform security updates, repair builds, and analyze failures.

An additional framework is not a local project decision when it becomes a permanent part of the same product and platform.

Framework autonomy is often understood as a way to reduce technical constraints. Each team can use established tools from its ecosystem and make independent decisions within its own area.

Users do not see those technical boundaries. They move through one shared product.

Colors, typography, and spacing must not shift visibly whenever the current remote changes. Input fields should behave comparably. Validation, focus states, keyboard interaction, dialogs, loading indicators, and error messages must follow one shared logic.

The same applies to responsive rules, tables, overlays, empty states, and risky or irreversible actions. Accessibility must not depend on which framework happens to render an area.

What is saved through local framework flexibility can return as permanent UI and UX harmonization work.

The frameworks may differ technically. To users, moving between them must not feel like moving between independent products.

Angular Material and Material UI illustrate this problem particularly well.

Angular
└── Angular Material
React
└── Material UI

Both libraries follow a similar design language. That does not mean their components will automatically look or behave identically inside the same product.

They are independent libraries with their own APIs, defaults, theming systems, component structures, and release cycles. Accessibility decisions and interaction details are likewise made within each library.

Differences may begin with the height and density of input fields. They appear in spacing, typography, hover and focus states, disabled states, or animations. Label behavior, validation messages, dialogs, tables, menus, and autocomplete components can differ in small but visible ways.

On separate demo pages, each library appears internally consistent. Side by side in one product, deviations become visible much faster.

This is not a quality comparison. Neither library needs to reproduce the other exactly. The problem begins with the assumption that a similar design heritage can replace a shared product design.

“Both use Material” is not a shared design system.

The Design System Sits Above the Frameworks

Section titled “The Design System Sits Above the Frameworks”

An Angular host cannot simply declare its Angular Material theme to be the product contract. React or Vue applications cannot use Angular-specific theme objects, Sass structures, or component APIs.

The shared model must sit above the frameworks.

Product Design System
├── Colors
├── Typography
├── Spacing
├── Radii
├── Shadows
├── Density
├── Breakpoints
├── Motion
├── Focus Rules
├── Validation Rules
└── Interaction Patterns

This product model is then translated into framework-specific implementations.

Global Design Tokens and UX Rules
├── Angular Material Theme
├── Material UI Theme
└── Vue Theme

The shared source may contain design tokens, CSS custom properties, documentation, and binding UX rules. The technical format matters less than the direction of the dependency.

The product defines the rules. The frameworks translate them into their respective component systems.

The product owns a design system. The frameworks merely own adapters for it.

Those adapters are not a one-time setup. They must evolve when component libraries are updated. New components require classification. Divergent defaults must be identified and adjusted where necessary. Visual regressions and accessibility remain shared responsibilities.

A set of global variables can align important foundations.

--color-primary: ...;
--surface-default: ...;
--spacing-medium: ...;
--border-radius: ...;

They can make colors, spacing, and radii consistent across several frameworks. They still do not create one shared product experience.

Tokens do not answer how tall a standard input should be or when validation errors should become visible. They do not define how required fields are marked or where focus returns after a dialog closes.

They do not specify how a combobox is operated by keyboard, which errors block an action, or what loading states look like. Nor do they determine table density, tooltip placement, or how a risky or irreversible action is confirmed.

Visual consistency grows from shared foundations. UX consistency additionally requires shared behavioral rules.

A shared theme is therefore not a stylesheet. It is a product contract that must be implemented and maintained permanently for every framework.

Angular, React, and Vue create their own technical costs while the shared product platform must harmonize UI, UX, and integration contracts through several adapters.

Do Not Build a Separate Component World for Every Framework

Section titled “Do Not Build a Separate Component World for Every Framework”

The visible differences between several component libraries could theoretically be removed through custom UI kits.

custom Angular UI Kit
+
custom React UI Kit
+
custom Vue UI Kit

The organization would then be operating not one shared component library, but several custom component platforms.

Each implementation creates responsibility for accessibility, tests, documentation, bug fixes, and updates. Complex components such as comboboxes, tables, date pickers, or dialogs must be developed several times and kept comparable for years. Every new product requirement creates parity work across the frameworks.

A custom component system may make sense for elements that are particularly important to the brand or the domain. It should not become the automatic response to every small difference between established libraries.

It is usually more pragmatic to use an established component library for each framework, define a framework-neutral design system above them, and configure the libraries as far as is sensible. Small differences may be accepted deliberately as long as the product remains coherent and predictable.

The goal is not absolute pixel identity. The goal is consistent behavior and one shared product language.

Pixel-perfect parity between different component libraries can become more expensive than the framework freedom that caused the need for it.

The additional cost does not mean that polyglot frontends are inherently wrong.

An existing product may bring a different stack and be integrated gradually into a new platform. An acquired system may be more economical to continue operating than to rebuild completely. A functionally independent capability may already exist with an established team and long-term available expertise.

Special technical requirements can also justify a different stack. A clearly bounded experiment may likewise be sensible if its objective, owner, and evaluation are defined. In each case, the platform must be explicitly prepared for the additional stack.

Strong and stable organizational boundaries make this decision easier. An independent area with long-term ownership can carry responsibility for its stack. Frequently changing team structures and unclear responsibilities argue against it.

Not every local preference is an economic reason.

One team may prefer React to Angular. Another may want to try Vue. A new framework may seem popular or attractive for recruiting. A small feature may appear faster to implement with another stack.

Such arguments focus mainly on the first use case. They do not answer who will maintain the integration, apply security updates, adapt the design system, and operate the area several years later.

Framework autonomy is sensible when it solves a real organizational, domain, or economic problem—not when it merely institutionalizes local preferences.

The benefit of an additional framework often appears locally.

A team works with familiar technology, can use existing expertise, and can choose its own tooling. Development in that area may therefore become faster or more reliable.

The additional cost, however, appears in several places.

The platform maintains integration contracts, framework adapters, build and test systems, observability, security updates, and documentation. It must harmonize UI and UX across several component systems and provide support during incidents.

The product carries additional runtimes, downloads, and potential inconsistencies. Users experience divergent interaction details or varying accessibility quality when harmonization is not maintained reliably.

A decision can therefore be clearly positive for the local team and still be negative for the product as a whole.

A team’s local advantage becomes an overall advantage only when the additional platform and product costs remain smaller.

That trade-off cannot be made solely within the team that wants to use the new stack. The team benefits immediately from the decision but does not necessarily bear all of its long-term consequences.

For new areas, one standard framework should remain the normal case. A shared standard does not eliminate every technical difficulty, but it limits the number of ecosystems that must be operated permanently.

Additional frameworks require a concrete domain, organizational, or economic reason. That reason must be stronger than personal preference or short-term productivity.

When another framework is admitted, it needs clear boundaries. It remains entirely inside its own DOM area, manages its own state, and has an independent lifecycle. Framework internals do not cross the boundary.

The host knows only small, framework-neutral lifecycle and platform contracts. Authentication, navigation, language, theme, and telemetry are provided as stable platform capabilities and adapted inside each framework.

The design system sits above the frameworks. Every supported component system receives a maintained theme and UX adapter. Established libraries remain the default. Custom components are developed where their product value justifies their long-term cost.

Most importantly, every framework needs permanent ownership. Its update strategy, security responsibility, support model, and expertise must be clear before a local experiment becomes part of the product platform.

Framework autonomy needs a high bar. Anyone introducing another framework must explain not only its first use case, but also how it will be operated over the long term.

Angular, React, and Vue can run in the same product because each framework manages its own part of the interface. They do not need to understand one another. This separation works only while framework internals do not cross the boundary. What is shared are not components, stores, or rendering cycles, but small, stable platform contracts.

Separate roots create clear ownership boundaries, but not a complete browser sandbox. At the same time, the organization must operate several runtimes, toolchains, and bodies of expertise permanently. The effort does not end after the first successful bootstrap.

The design system must also be translated into several component systems. Similar libraries such as Angular Material and Material UI do not create identical UI or UX. Visual tokens require binding behavioral rules and maintained adapters so that technical diversity does not turn the product into a visibly inconsistent collection of applications.

Framework autonomy is therefore sensible only when its concrete benefit justifies these permanent platform costs.

Multiple frameworks are not a technical playground for individual teams. They are a permanent investment by the entire platform.