paint-brush
How to Integrate React and AngularJS via Webpack Module Federationby@rusanovn
3,727 reads
3,727 reads

How to Integrate React and AngularJS via Webpack Module Federation

by Nikita RusanovFebruary 20th, 2024
Read on Terminal Reader
Read this story w/o Javascript
tldt arrow

Too Long; Didn't Read

This guide outlines a method for integrating React components into AngularJS applications, utilizing Webpack for React and Gulp for AngularJS builds. It highlights the use of Module Federation to load React components dynamically and discusses the setup for seamless integration, including handling props, managing component lifecycles, and configuring routing. Aimed at developers looking to modernize AngularJS apps or combine these technologies in micro-frontend architectures, it provides practical steps for efficient development and deployment.
featured image - How to Integrate React and AngularJS via Webpack Module Federation
Nikita Rusanov HackerNoon profile picture

Imagine finding yourself in a situation where you join a project tasked with developing a massive legacy application created five years ago using the first version of Angular. This application resembles a forgotten treasure, the functioning of which no one in the company knows anymore. Hidden within its depths is an authentication system and numerous modules managing business logic, all intertwined into a single structure with the help of an outdated build tool – Gulp. As if that wasn't enough, the application's dependencies still require loading through Bower, adding another layer of outdated technologies. In this context, your task acquires not only a technical but almost an archaeological aspect – you're faced with restoring the functionality of this digital relic and updating it without losing the value of the knowledge and experience encoded within.


Now, consider the parallel reality of this project, where alongside the legacy Angular system, the development team has embarked on creating a new application, this time choosing the modern and popular React. The project is built using Webpack, which is the de facto standard for modern web applications. The new application implements its internal routing mechanism and integrates modern libraries, significantly differentiating its technological base from the old Angular application.


However, despite the external attractiveness and modernity of the React application, a serious problem of duplicating business logic between the two systems arises. Such key interface elements as the main sidebar, despite having an identical appearance, must be rewritten from scratch for the new system. This increases the workload and leads to the need to maintain and update code in two different codebases whenever business requirements change. The lack of code reuse between the two applications becomes a significant drawback, increasing labor costs and the likelihood of errors when synchronizing changes.


Having two applications = twice the work


The ideal solution in such a situation would be gradually integrating new technologies into the legacy application, with the possibility of selectively replacing its parts with modern alternatives. Imagine how the scenario would change if we could, step by step, replace outdated pages with new ones developed using React while preserving and reusing the common business logic and utilities, such as the authentication system. This would reduce code duplication and facilitate the transition to modern technologies, minimizing the risks and costs associated with a complete overhaul of the application.


Throughout my professional career, I have encountered situations twice where a company had an outdated AngularJS application and, alongside it, was developing a new application on React. Finding ways to integrate and create a symbiosis between the old and new code becomes a critically important task. There are various methods to create the illusion of a single whole from two different applications, including using an iframe or setting up routing through Nginx.


Today, I want to share not so much a step-by-step guide but an introduction to an interesting idea that could help developers with the task of integrating Angular and React applications. My focus today is on using Module Federation to address this issue. While it may not be considered an perfect solution, it nonetheless works and could be the very impetus someone is looking for in their quest to unite these two worlds.


We will touch on micro-frontends, explore the peculiarities of building such applications, and discuss how to organize interaction between Angular and React code. This idea is intended to show how modern approaches can be used to solve highly relevant tasks in development and offer a path to pursue in search of solutions.


Seamless interaction between React and AngularJS components

Let's consider the stack:

  • Webpack 5 for building React 18 + Module Federation plugin;
  • Gulp 4 for building AngularJS 1.8;
  • Docker


Micro-frontends

How micro-frontends solve the problems of "big" projects

In traditional frontend development, work is usually carried out within a single project, which contains a single package.json file, uses one bundler, and has a set of modules that can freely import each other to exchange functionality and data. In this approach, all project elements are closely integrated and work in a unified context.


However, "micro-frontends" implies a significant change in this paradigm. The essence of the micro-frontend approach is that several frontend projects (or modules) are launched within a single application, which are loosely coupled with each other. This means that each of these micro-frontends can be developed, tested, built, and deployed independently of the others while together, they form a single application.


The micro-frontend architecture offers an interesting and innovative approach to web application development; however, like any technology, it has pros and cons.


Pros:

  • Increased Code Maintainability: The independence of modules significantly simplifies code maintenance, as it reduces the amount of code and the number of interdependencies between components. This leads to a lower likelihood of problems arising. Additionally, different modules can be under the responsibility of different teams, which facilitates project management.
  • Technological Flexibility: Micro-frontends allow for the assembly of websites using various technologies within a single project. This is particularly valuable for projects aiming to combine new and legacy technologies, such as React and AngularJS, which is the subject of this article.

Cons:

  • Complexity of Architecture: Managing micro-frontends leads to a more complex architecture with many interaction points, increasing the risk of errors and the cost of testing.
  • High Entry Threshold: Developing and maintaining a micro-frontend architecture requires a deep understanding of specific technologies and approaches and finding specialists willing and able to work in such an environment.
  • Performance Issues: Integrating many independent applications can lead to an increase in the total amount of code loaded and, consequently, a decrease in performance, especially if the same libraries (e.g., Lodash) are used in different applications. However, using mechanisms such as Module Federation can help solve this problem by allowing dependencies to be shared between micro-frontends, thereby reducing the total volume of code loaded.


Micro-frontends represent a powerful tool for developing complex web applications, but they require careful planning and a considered approach to their implementation and maintenance.


If you already have two independently functioning applications, transitioning to a micro-frontend architecture may be a relatively simple, especially if a unified system like Webpack is used for project assembly.


However, it's worth noting that many AngularJS projects are traditionally built using Gulp, which may become an obstacle to integration, given the differences between Gulp and Webpack in project assembly approaches. Gulp is often used for automating tasks, such as file minification and compiling SCSS to CSS, whereas Webpack provides more comprehensive capabilities for modular application assembly.


In this article, we will explore overcoming this limitation and integrating a React application using Webpack with an AngularJS application built using Gulp.


Comparing Approaches


Developers decide which approach is best

As we mentioned earlier, there are several approaches to creating a project where micro-frontends on React and Angular can coexist successfully:

  1. Using iframe: In the Angular application, you can embed an iframe pointing to a separate React application. The main drawback of this method is the difficulty of interaction between applications through postMessage, which requires the development of a special mechanism for message exchange.
  2. Clever Routing Through Reverse Proxy: This approach displays different applications on different URLs. For example, the Angular application is accessible at http://site.com/angular/…, and the React application at http://site.com/react/…. Among the cons:
    • Difficulties with State Sharing Between Applications: Possibly through localStorage or URL parameters. This challenge involves managing and synchronizing the state across different applications, which can become complex and error-prone.
    • Inability to Reuse Components Between Two Separate Applications: Sidebar or Topbar. This limitation results in duplicated efforts and inconsistencies in the user interface, as developers must recreate similar components for each application.
    • Degradation of User Experience Due to Page Reloads: When navigating between applications. This can lead to a disjointed and slow user experience, as each application reload may reset the state and require the browser to load resources again, disrupting the seamless interaction users expect from modern web applications.
  3. Module Federation: This is a Webpack feature that allows the creation of applications that can act as dependencies for each other and share third-party libraries. The main downside is the need to use a unified build system (Webpack), but within this article, we will explore how to overcome this limitation.
  4. Single SPA: A framework for creating micro-frontends using an in-browser build approach through modern ES modules. Suitable for projects with frontends on different technologies and provides a "shell" that unites various applications through JavaScript. In this article, we will focus on a simpler solution; however, this approach is worth exploring in more detail in the future.


Share in the comments if you know other effective methods for implementing a micro-frontend architecture!


A Bird's Eye View

It's great when the entire approach is clear at a glance

In our project, we organize two types of builds: dev and production. The Angular application uses Gulp, while the React application is built through Webpack.


For production builds: We use Docker to run the build processes in parallel, after which we serve the results — static files for Angular and React — via Nginx. The React application is built using the ModuleFederation plugin.


For dev builds: A webpack-dev-server is run on the local machine, serving Angular as static files and React in normal mode. In this article, we will not delve into the details of this build, as it represents a fairly standard configuration without the use of Module Federation.


Angular is the foundation for our application, into which we integrate React components. This allows us to retain most of the logic within the Angular application and gradually transition to React, adding new fragments as needed. This approach allows smooth integration of the new frontend without radical changes in the existing codebase.


In the article, we will refer to the approximate structure of the project, indicating which files and sections are affected in the process of presenting the material:

- Dockerfile
- package.json
- packages // microfrontends
	- react-app
		- package.json
    - src
	    - angularIntegration.ts // entry to react app for angular app
		- angularContext.tsx // pass global props from angular
		- index.tsx
  - angular-app
    - package.json
    - app
      - index.html
			- scripts
        - react-integration.js // load react API and add that to `window`
			- routes.js
        - components
	        - sidebar
		        - sidebar-d.js // example of component


Build Process

Frontend developers invent a new project builder

Monorepo

Let's start with the concept of a monorepository. One of the advantages of micro-frontend architecture is the ability to separate large applications from each other effectively. However, when it comes to development, it is especially convenient when two or more applications are located in the same repository. This applies to both dev and production builds.


The main advantage of a monorepo is that if an error occurs in one of the projects, it becomes obvious to the entire team, and further deployment to production is blocked. This significantly simplifies tool management, especially when you have not two but many small applications: it's easier to manage their dependencies and the build process. Moreover, a monorepo allows quick changes to be made simultaneously to Angular and React parts of the project, ensuring rapid task switching and feedback.


To focus on the key points and not complicate the article with details of managing a monorepo, we will not delve into the tools for working with monorepos. Instead, let's assume that we already have separate scripts set up for running production and dev builds directly from the root package.json: react-app:build, react-app:dev, angular-app:build, angular-app:dev.


Production Build

Building micro-frontends in React and Angular in Docker

For the production build, we adopt an approach where the Angular and React applications are built separately in Docker. After the build process is completed, the results — the static files of both applications — are placed in the app/www directory for further distribution via Nginx. One of this process's key features is using the ModuleFederation plugin in the React application build, which generates a remoteEntry.js file.


This remoteEntry.js file plays a central role in the Module Federation mechanism, allowing different frontend applications to dynamically load and use each other's code as dependencies at runtime. This provides flexibility and modularity, allowing, for example, an Angular application to integrate and use React components without the need to statically include the entire application in the build. Thus, both builds can remain independent, while ModuleFederation facilitates efficient interaction at runtime.


In the context of AngularJS, built through Gulp, and React, using Webpack and Module Federation, we directly interact with remoteEntry.js from Angular. This file, created for React, automatically manages the loading of the necessary parts of the application. By connecting remoteEntry.js to Angular, we gain access to React components and functions, easing integration between different parts of the project without complex configurations.


Docker

The Docker configuration for our project is organized into several stages. In the first stage, package.json both applications are copied, and their dependencies are installed. Then, the process continues with the parallel building of the production versions of the React and Angular applications. The final step includes copying Nginx settings and the necessary static files.


An approximate configuration looks like this:

#
# Stage 1.1 - APP: Install modules
#
FROM node:20.3.0 AS app_modules_installer
WORKDIR /app
COPY package.json yarn.lock /app/
COPY packages/angular-app/package.json /app/packages/angular-app/
COPY packages/react-app/package.json /app/packages/react-app/
RUN yarn install --frozen-lockfile
#
# Stage 2.1 - APP: Build Angular app
#
FROM app_modules_installer AS app_angular_builder
WORKDIR /app
COPY packages/angular-app /app/packages/angular-app
RUN yarn angular-app:build
#
# Stage 2.2 - APP: Build React app
#
FROM app_modules_installer AS app_react_builder
WORKDIR /app
COPY packages/react-app /app/packages/react-app
RUN yarn react-app:build
#
# Stage 3.1 - NGINX: Build
#
FROM nginx:1.25-alpine3.18 AS nginx_builder
COPY deploy/nginx.conf /etc/nginx/nginx.conf
#
# Stage 3.2 - NGINX: Add assets
#
FROM nginx_builder
COPY --from=app_angular_builder /app/packages/angular-app/www /app/www
COPY --from=app_react_builder /app/packages/react-app/build /app/www/react-app

Module Federation Plugin

The project incorporates the Module Federation plugin, which you can learn more about on the official Webpack website. The essence is that our application is identified as react-app, integration is done using the remoteEntry.js file, and we export a module named angularIntegration.

new ModuleFederationPlugin({
    name: 'react-app',
    library: { type: 'global', name: 'react-app' },
    filename: 'remoteEntry.js',
    exposes: {
        angularIntegration: './src/angularIntegration.ts',
    },
}),

To complete the integration, it's necessary to add the following line to the index.html of the Angular application: <script src="scripts/react-integration.js"></script>. This script will be discussed in detail later, but its inclusion already prepares the platform for integration between Angular and React.


Dev Build

In a nutshell, we don't use Module Federation for the dev build but rely on webpack-dev-server, which provides static file serving and HMR (Hot Module Replacement) for React. Angular is built and serves as static files for Webpack.


P.S.: Honestly, the first version of the article had a lot of details about this process, but due to the routine of setting up file paths, I decided not to complicate things. Just like that!


Code Interaction

When you are programming and getting into the flow

From the integration of React and Angular, we expect the following:

  1. The ability to integrate a React component: We need ease in inserting React components into an Angular application as regular Angular components.
  2. Dynamic data passing to the React component: We want to pass data from Angular to React at the component's initialization and update this data on the fly, similar to how props work in React.
  3. Interaction with the React component from Angular: We need the ability not only to receive data back from the React component into Angular but also to call event handlers of the React component from Angular, which is critically important for implementing functions related to the global state of the application, such as routing.
  4. Correct removal of the React component: When removing a React component from the DOM of the Angular application, it's also necessary to clean up the Virtual DOM of this component to avoid its accumulation and potential memory leaks, as observed through DevTools.


These requirements form the basis for effective and flexible integration between the two frameworks, allowing developers to leverage the advantages of both technologies within a single application.


The technical approaches for using an individual component or page are similar. The general scheme of integration looks as follows:

Interaction Scheme of React and Angular

For successful integration of a React component into an Angular application, the following steps need to be executed:

  1. Loading remoteEntry.js: This file, provided by Module Federation, contains the necessary information for loading and executing React modules in Angular.
  2. Saving the render function in window: This allows globally accessing the React component's render function within the Angular application.
  3. Calling the React component's render from Angular: Initiates rendering the React component in a specified location within the Angular application.
  4. Creating a React root: Using ReactDOM.createRoot, a root, a React element is created based on the specified container.
  5. Rendering the component: The React component is rendered using the root.render method.
  6. Preparing the component with Observer: The component is wrapped in an Observer, allowing dynamic passing of props from Angular. An object is returned to the Angular side, containing:
    • The unmount function for cleaning up the Virtual DOM when the component is no longer needed, for example, when changing pages.
    • An Observer for dynamic passing of props.


Next, we will detail each of these steps, starting with the production build, as it represents a more complex case, and then briefly discuss how a similar process is implemented in dev mode.


AngularJS

Wise AngularJS looks at you

Step 1: On the Angular side, the script react-integration.js is directly included in the index.html file, as is customary with any other script. This script is responsible for dynamically loading the remoteEntry.js file onto the page. The remoteEntry.js file is a special artifact created using the Module Federation plugin and provides an API for interacting with the React application. In our case, this API is named react-app:

// react-integration.js

(function () {
    // #1
    if (
        ["localhost", "some-dev-domen.com"].some((host) =>
            window.location.host.includes(host)
        )
    ) {
        return; // dev mode
    }

    const createScript = (src) => {
        const script = document.createElement("script");

        script.src = src;
        script.type = "module";

        document.head.appendChild(script);
    };

		// #2
    createScript(
        `react-app/remoteEntry.js?hash=${window.REMOTE_ENTRY_HASH}`
    );

    // #3
    const reactAppName = "react-app";
    const moduleName = "angularIntegration";

		// #4
    const getApi = () => {
        return window[reactAppName]
            .get(moduleName)
            .then((mod) => mod())
            .then((mod) => mod.default);
    };

		// #5
	window.renderReactSidebar = (container, globalAngularProps, props) => {
        return getApi().then((render) =>
            render(container, "sidebar", globalAngularProps, props)
        );
    };

    window.renderReactPage = (container, globalAngularProps, props) => {
        return getApi().then((render) =>
            render(container, "page", globalAngularProps, props)
        );
    };
})();

Let's take a closer look at the process of working with the react-integration.js script:

  1. Runtime Environment Check: First, we ensure we are in production mode. For the dev build, Module Federation is not used; and instead, webpack-dev-server is run.
  2. Adding the remoteEntry.js Script: The remoteEntry.js script is dynamically added to the page, including a hash in its address to prevent browser caching. This is especially important for updates to ensure the browser loads the current version. The hash can be generated during the Angular build process via Gulp, for example, using the current time (Date.now()).
  3. Preparing Variables: Variables are defined with the name of the application and the exported elements configured in Module Federation. This allows us to clearly define which parts of the React application will be used.
  4. Creating an API for Module Loading: Functionality is implemented to conveniently obtain and render React components from the module.
  5. Registering Rendering Functions: Functions such as renderReactSidebar and renderReactPage are added to the window object, which will be called from Angular, in controllers or directives, to render React components.


Step 2: Instead of adding functions such as renderReactSidebar to the Angular Dependency Injection (DI) system, a simpler approach was chosen by directly including these functions in the window object. This facilitates their accessibility and invocation from different parts of the Angular application. When these functions are called, a container from Angular, into which the React component will be embedded, global settings such as handlers for ui-router, and initial props for the components to be passed. This method allows for flexible integration and management of React components within the Angular application, providing all necessary data and context for their correct operation.


Step 3: Next, using an Angular directive, the process of rendering the React component can be initiated. This is done by calling the corresponding function added to window (for example, renderReactSidebar), directly from the directive. At this point, the container (DOM element) into which the React component should be embedded, as well as the necessary props and any global settings, are passed as arguments to the function.

// sidebar-d.js

(function () {
    'use strict';

		// # 1
    angular.module('front').directive('frontSidebar', sidebar);

    /* @ngInject */
    function sidebar() {
        return {
            controller: sidebarCtrl,
            templateUrl: 'scripts/components/sidebar/sidebar-d.html',
        };
    }

    /* @ngInject */
    async function sidebarCtrl(
        $scope,
        $state,
        DynamicService,
    ) {
				// #2
        const globalAngularProps = {
            goToRoute: (state, payload) => {
                $state.go(state, payload, { reload: false });
            },
        };

				// #3
        const sidebarNode = document.getElementById('front-sidebar');
        // #4
        const { observer, unmount } = await window.renderReactSidebar(sidebarNode, globalAngularProps, {
            someProp: 'initial state'
        });

				// #5
        DynamicService.subscribe().then(function (count) {
            observer.updateProps({ someProp: 'updated state' });
        });

				// #6
        $scope.$on('$destroy', () => {
            if (unmount) {
                unmount();
            }
        })
    }
})(); 

The process of integrating a React component into an Angular application through a directive includes the following steps:

  1. Angular Directive Registration: An Angular directive is created and registered, allowing React components to be used as Angular components. This simplifies the insertion of a React component into Angular templates, for example, by using <front-sidebar></front-sidebar> in an Angular application.
  2. Declaration of Global Values: In Angular, you can declare and pass global values, such as routing settings, to the React component. These values can later be used in React Context to provide data to React components.
  3. Container Lookup: The directive searches for a container by its identifier, into which the React component will be inserted. This container is predefined in the Angular template.
  4. Initialization of React Component Rendering: Using previously declared rendering functions, such as renderReactSidebar, the React component is rendered in the found container. This process returns an observer for managing props and an unmount function to remove the component information from memory (Virtual DOM).
  5. Interaction with Angular Services: The directive can subscribe to Angular services and be used observer to update the React component's props when the data received from the service changes.
  6. Lifecycle Management: When the Angular directive is destroyed, for example, when navigating to another page, the unmount function must be called for the React component to clean up the Virtual DOM and prevent memory leaks correctly.


The action plan for integrating React pages into an Angular application is similar to that used for components but requires additional routing configuration. This is particularly important if the Angular application uses ui-router, as Angular needs to be informed about routing changes occurring on the React side.

// routes.js

.state('dashboard.test', {
    url: '/test',
    template: '<react-page></react-page>',
})

Configuring routing between Angular and React can indeed be a challenging task, especially when ui-router for Angular and react-router-dom for React are used. Both routers aim to control state changes and navigation within the application, which can lead to conflicts if they are not properly configured to work together.


If readers are interested in learning more about ways to resolve these conflicts and how to configure both routers for harmonious coexistence, I can delve into this topic in a future article, describing possible approaches and best practices for integrating routing in micro-frontend architectures.


React

Fresh React doesn't look at you

Moving to the work on the React side, we use the angularIntegration.js script as the entry point for our application. This script serves as a key element for integrating React components into an Angular application, allowing for the pinpoint insertion of React elements directly from Angular.

// angularIntegration.ts

import { renderPage, renderSidebar } from './index';
import { GlobalPropsFromAngular } from './angularContext';

export default function angularIntegration(
    container: HTMLElement | null,
    content: 'page' | 'sidebar',
    globalPropsFromAngular: GlobalPropsFromAngular,
    props: Record<string, any>,
) {
    switch (content) {
        case 'page':
            return renderPage(container, globalPropsFromAngular);
        case 'sidebar':
            return renderSidebar(container, globalPropsFromAngular, props as any);
    }
}

The angularIntegration function, exported from the React application, was previously declared for the Module Federation plugin. It is used for injecting the React tree into a specified Angular application container, determining which specific components will be displayed.


The essence of any rendering function is similar. Let's look at the detailed process of rendering a sidebar:

// index.tsx

// #1
export const renderSidebar = (
    container: HTMLElement | null,
    globalAngularProps: GlobalPropsFromAngular,
    props: SideMenuBarProps,
) => {
		// #2
    const { Component, observer } = withPropsObserver(SideMenuBar, props);

		// #3
    const unmount = render(container, globalAngularProps, <Component {...props} />, 'sidebar');

		// #4
    return { observer, unmount };
};

function withPropsObserver<T extends object>(Component: FC<T>, initialProps: T) {
    let lazyUpdatedProps: T | null = null;

		// #2.a
    const observer: {
        updateProps: (props: T) => void;
    } = {
        updateProps: (props) => {
            lazyUpdatedProps = lazyUpdatedProps ? { ...lazyUpdatedProps, ...props } : props;
        },
    };

		// #2.b
    const Wrapper: FC<T> = () => {
        const [props, setProps] = useState<T>(initialProps);

				// #2.c
        useEffect(() => {
            if (lazyUpdatedProps) {
                setProps(lazyUpdatedProps);
                lazyUpdatedProps = null;
            }

            observer.updateProps = (newProps) => {
                setProps((prev) => ({ ...prev, ...newProps }));
            };
        }, []);

        return <Component {...props} />;
    };

    return {
        Component: Wrapper,
        observer,
    };
}

const render = (
    container: HTMLElement | null,
    globalAngularProps: GlobalPropsFromAngular,
    content: ReactNode,
    identifierPrefix: string,
) => {
    if (!container) {
        throw Error('Root element not found!');
    }

		// #3.a
    const root = ReactDOM.createRoot(container, {
        identifierPrefix,
    });

		// #3.b
    root.render(
		<AngularContextProvider {...globalAngularProps}>{content}</AngularContextProvider>,
    );

		// #3.c
    return () => root.unmount();
};
  1. The renderSidebar function initiates the rendering process of the sidebar, represented by a React component.

  2. We want to have the ability to pass props to our component, so we wrap it in withPropsObserver. Here, the Higher-Order Component pattern is used:

    1. An observer object with an updateProps function is created. This function allows for updating the component's props from Angular. Initially, updateProps acts as a placeholder to collect lazyUpdatedProps — props that have been updated prior to the component's initialization.
    2. The definition of Wrapper — a wrapper that extends the component with additional capabilities for storing and managing props received from Angular.
    3. Upon the component's mounting, Wrapper uses lazyUpdatedProps to initialize the local state and updates observer.updateProps so that this function can change the component's state.
  3. Now, we start rendering the component, for this, we call the render function.

    1. Using ReactDOM.createRoot, the component is embedded into the provided Angular application container, creating a new React tree.
    2. AngularContextProvider is included to provide access to global data and Angular handlers, for example, to support routing.
    3. The unmount function is returned along with the rendering result to allow subsequent removal of the Virtual DOM.
  4. After successful rendering, the observer object and unmount function are passed back to the Angular application. This gives Angular the ability to manage the state and lifecycle of the React component, as well as the component tree.


Dev Build

For development, we choose webpack-dev-server because of its advantage in Hot Module Replacement compared to Gulp, which speeds up working with React code. Angular's static file serves as the index.html template for Webpack, and React render functions are specified in window through index.tsx, simplifying integration and avoiding the use of react-integration.js.

// index.tsx

if (process.env.NODE_ENV === 'development') {
    window.renderReactPage = (container, globalAngularProps) => {
        return renderPage(container, globalAngularProps);
    };

    window.renderReactSidebar = (container, globalAngularProps, props) => {
        return renderSidebar(container, globalAngularProps, props);
    };
}


Conclusion

We have explored the build setup and code interaction for integrating React and AngularJS, highlighting some nuances along the way. Despite potential technical complexities, this approach facilitates an efficient transition from an outdated stack to a modern one, opening new opportunities for development.


If you're interested in the topic, I'm ready to share solutions to common challenges, such as setting up routing, authentication, and using feature flags. Wishing you a pleasant day!