
Introduction
Having worked in two teams with very similar business operations, one using a micro front-end architecture and the other not, I have firsthand experience with both approaches. I want to share my insights and understanding of micro front-ends to explain why we need them.
This is a long read, so feel free to bookmark it and read it at your own pace.
What is “Large Front-End”?
Large Front-End refers to the expansion of front-end development responsibilities and technologies, going beyond browser-based development to encompass the entire front-end technology stack. It emphasizes cross-platform development and unified management.
From a Technology Stack Perspective:
- Web Front-End: PC websites, mobile H5 pages
- Cross-Platform Development: Hybrid applications, React Native, Flutter, Mini Programs
- Desktop Applications: Electron and other desktop applications
- Back-End Related: Node.js services, BFF (Backend For Frontend) layer
- Infrastructure and Operations: Performance optimization, toolchains, front-end monitoring
Development Process:
Large Front-End is not only responsible for business code development but also involves building, DevOps, performance optimization, and other aspects. For example, client-side tasks can be moved to the build phase (e.g., SSG – Static Site Generation) or to the server through SSR (Server-Side Rendering), combined with container technology to customize the application environment for greater flexibility.
From a Business Perspective:
A Large Front-End team is responsible for the development of all business lines for the same consumer-facing (C-end) product, although the team can be divided into smaller groups focused on specific areas. For example, a travel platform’s Large Front-End team would be responsible for developing systems for flights, hotels, trains, and other services, as well as managing the architecture of common elements like UI component libraries and request libraries.
Background
To facilitate our discussion, let’s assume our business scenario is developing a travel booking app that includes flights, hotels, and train tickets. Each service includes listing pages, order placement pages, order details, and after-sales service pages. The homepage serves as the entry point for each service and provides search functionality.

Typically, we have several architectural options to choose from: Architecture
Single-App Architecture
The simplest approach is to put the homepage, flight search, order placement, payment, and order details all in the same project. Their components can be shared within the same project. If you use an SPA (Single Page Application) model, page transitions don’t require a full page refresh, so users don’t see a blank screen. We can even use transition animations, keepalive, preloading, and pre-fetching techniques to give users a smooth experience.
Scenario:
Consider two requirements: one for the flight booking service and another to modify the train ticket service. These requirements are unrelated, but because they are in the same codebase, merging and releasing code requires alignment. Rolling back one requirement inevitably leads to rolling back the other. Alternatively, we can choose to release them sequentially, but either way, they affect each other. Over time, having different business teams working in the same codebase can lead to increasing chaos.
This is just a simple example. In real-world scenarios, there can be hundreds or thousands of such requirements. Developers and management teams need to spend a lot of effort analyzing and handling these issues, greatly increasing communication costs and making it easy for business lines to interfere with each other.
The above example is just to illustrate the evolution of architectures. I believe many people will jump directly to later architectures rather than actually adopting a single-app architecture. If a business is relatively simple, then using the above architecture is perfectly fine. We can then add new applications during subsequent business iterations, transforming it into a multi-app architecture.
Our Single-App Architecture is different from SPA. SPA refers to a single-page application, emphasizing that an application has only one page and only loads the HTML once. Subsequent virtual page transitions are implemented through JavaScript. Our Single-App Architecture can be either an SPA or an MPA (Multi-Page Application). They are concepts in different dimensions and need to be distinguished.
Multi-App Architecture
We can split the above application into multiple applications. We can not only split them by business line but also divide the same business line into smaller sub-domains, such as splitting the pre-booking and post-booking processes into different applications.
For example, the above application can be split according to the following dimensions:

Finally, the pages will be split into:
- Homepage application (relatively special, we won’t discuss it for now)
- Flight listing page
- Flight booking page
- Hotel listing page
- Hotel booking page
- Train listing page
- Train booking page
We use multiple monolithic applications to form a multi-app architecture. Different applications are responsible for different business domains, and they are connected by passing information through mutual jumps. This avoids the mutual interference between different business lines mentioned above. Different business teams can release their respective systems.
However, there is a special page, the homepage, which contains all three businesses. Who should maintain this application? If we still use the SPA architecture mentioned above, we will still face the dilemma of multiple teams developing the same application. The problem remains unsolved. Let’s summarize the advantages and disadvantages of this model.
Advantages
- More flexible releases
- Different domains are isolated from each other
- Developers focus on their own domains
Aside: It is strongly recommended to use TypeScript to define the types, encapsulate them into separate methods, and expose them externally. Parameter passing during jumps is one of the most common sources of problems and increases communication costs.
How to Solve Common Dependencies?
But new problems arise. Although they are independent businesses, or different pages of the same business, there are inevitably common elements, such as:
- Common header
- Common footer
- Engineering code
- UI component library
- Request library
- Environment variables (e.g., language), user information
- App SDK for interacting with Native, etc.
Multi-App + Shared Modules
To solve the problem of common dependencies when dividing by domain:
An experienced developer might think of modularization, npm/Module Federation, etc. That’s right, many teams actually solve it this way. To summarize, there are two ways to introduce them.
Introduction at Build Time
We can encapsulate some common components, business logic, and underlying logic into separate components, and install them when using them. This allows us to reuse code and make business logic clearer, with professionals doing what they are good at.
Introduction at Render Time
We can use a remote URL to introduce modules, or we can use Module Federation to introduce modules. The advantage of doing this is that updates to common modules do not require business teams to reinstall them, and developers of common modules can better control updates and iterations. Of course, the premise is that the updates are backward compatible. For those breaking changes, the version number convention should be followed, and it is often more stable for users to reinstall, test, and then use them.
These two solutions have their pros and cons.
Introduction at build time: Version upgrades of the common library must be accompanied by the reinstallation, testing, and release by the user (business side).
Introduction at render time: If there are no breaking changes, the user does not need to go through the release process again after the common library is released. However, the developers of the common library need to consider more compatibility and release it after thorough testing.
It should be noted that we can also use server-side rendering when introducing modules at render time, preloading modules on the server side to make them static modules. Introduction at render time can use either client-side rendering or server-side rendering, and the two do not conflict.
| Introduction at Build Time | Introduction at Render Time | |
|---|---|---|
| Support Dynamic Updates | No | Yes |
| Debugging Cost | High/Medium | Medium/Low |
| Communication Cost | High | Low |
| Separation of Responsibilities | High | Low |
| Server-Side Rendering | Yes | Yes |
Let’s look at a real-world example. For the above homepage, flight, hotel, and train ticket business lines, which model would you choose?
Introduction at build time or introduction at render time?

The above architecture splits the business well and also handles the common modules of the business well. It can already meet most business scenarios, but
There are still shortcomings, and it can even be said to be the biggest bottleneck.
Cross-Application Context Fragmentation
In a multi-app architecture, businesses are split into multiple independent applications. When a user jumps from one application to another, although some information can be passed through URL parameters, Storage, etc., there is a lack of a true page-level context.
The so-called “page-level context” refers to:
- Variables stored when the user opens the page;
- Variables destroyed when the page is closed;
- The context is only valid for the current page and is not affected by other pages.
However, the browser’s native capabilities cannot meet this requirement:
sessionStorage‘s lifecycle depends on the browser window, not a single tab;localStorageis globally shared and is even less suitable for single-link context management.
Typical Scenario: Channel Rights Verification
Here is a real business requirement (I have encountered similar problems in two companies):
Only users who enter through specific channel links will enjoy certain rights when placing an order.
In this scenario, there are two common implementation methods:
- URL Parameter Passing
- Must pass the channel code and other information from the entry page all the way to the order placement page;
- The link is long and has many dependencies, making it easy to miss.
- Storage Storage
sessionStorage/localStoragecan be shared across pages, but cannot be limited to a single link;- Cannot be destroyed when the tab is closed, making it easy to pollute or reuse incorrectly.
To achieve this requirement, we mobilized nearly 5 teams to modify nearly a hundred jumps. Any missed link will cause the entire business link to be interrupted. Our team is relatively better because we use micro front-ends internally, and the jumps are controllable for us.
Why We Need Page-Level Global Context
A true page-level global context can not only solve the above link problems but also bring additional value:
- Global Switch
- For example, a certain function needs to be followed uniformly by all product lines.
- User Information Management
- User language, preference settings, and other information do not need to be passed repeatedly.
- Common Page Elements
- For example, Header, Footer, avoid repeated access and maintenance.
Although there are alternative solutions, these solutions are often cumbersome and redundant, and are far less concise and efficient than the native context model.
Summary of the Disadvantages of Multi-App Architecture
In the absence of a page-level global context, multi-app architecture has the following core disadvantages:
- Broken Context
- Applications cannot naturally share context, leading to redundant and error-prone information passing.
- Fragmented User Experience
- White screen and loading delays when switching pages;
- Returning to the previous page requires a page refresh;
- Significant gap compared to the smooth experience of native apps.
- High Maintenance and Upgrade Costs of Common Libraries
- System-level logic (monitoring, tracking, statistics) needs to be accessed separately by each application;
- Common dependencies (such as request libraries) are scattered across applications, and upgrades require individual modifications;
- The focus is not truly separated, resulting in huge redundancy and collaboration costs.
Case Study 2: The Cost of Upgrading a Request Library
Take the upgrade of a request library as an example:
A certain gateway modification required replacing the self-built request library with a group-built solution. Because the request library was scattered in every corner of the application, the upgrade workload was huge:
- Need to reinstall dependencies, modify calling methods, and adapt to the new library in all applications;
- Statistics showed that the front-end side alone cost an engineer a year and a half of full-time work;
This is not an individual execution problem, but a lack of unified governance capabilities at the architectural level:
- There is no clear plan for what are common capabilities and what are the responsibilities of the business side;
- Even if there are specifications, they are easily distorted in the long-term iteration;
- When team members change or new solutions appear, it is easy to deviate from the original agreement.
Forcing reliance on “people to comply” is often ineffective;
If there is a stronger architectural mechanism to separate common and business logic, the problem will be easier to solve.
Answer: Base-Style Micro Front-End
To solve the above problems:
- Context Fragmentation
- Fragmented User Experience
- Let business teams focus on business;
- Common packages can be iterated flexibly;
- Architectural strategies can be strongly enforced;
The answer is – Base-Style Micro Front-End Architecture.
The micro front-end concept naturally solves the fragmentation problem of multi-app architecture by integrating multiple applications at runtime and providing a unified context and common dependency governance mechanism.
Why Base-Style Micro Front-End?
It is important to note that the concept of micro front-end itself is very broad:
Front-end applications can be split into multiple sub-applications and then combined through different methods such as build-time integration, server-side assembly, or browser runtime loading. Any of these forms can be called “micro front-end.” For example, solutions like Module Federation and Bit are essentially in the category of micro front-end.
However, these solutions may not necessarily solve the key problems we mentioned earlier:
- Context Fragmentation
- Fragmented User Experience
- Difficulties in Upgrading Common Dependencies
To truly solve these problems, what is needed is – Base-Style Micro Front-End (Host-based Micro Frontend).
What is Base-Style Micro Front-End
Base-style micro front-end can also be called Base-Sub-Application Architecture:
- A Base Application (Host App) is responsible for global context, routing, and dependency management;
- Sub-applications (Sub Apps) run on top of the base and share context with the base (such as user information, language, feature switches, etc.);
- Page jumps have no white screen, and the experience is smooth and consistent;
- Common libraries and system-level logic are centrally managed to avoid repeated access and difficult upgrades.
Advantages of Base-Style Micro Front-End
Before discussing these advantages, it is important to clarify one point: only when using the base-style micro front-end architecture reasonably and effectively can its advantages be truly realized. For example, even if a micro front-end architecture is adopted, if each team still implements its own login logic and the page jump method is the same as the traditional MPA, the benefits of micro front-end will hardly be reflected. In other words, reasonable use and a dedicated team to maintain the base application are prerequisites for realizing the advantages of micro front-end.
1. Separation of Responsibilities
The separation of responsibilities here emphasizes vertical separation, that is, the division of labor between basic capability development and business development, rather than the division of labor between different business modules.
- The common capabilities required by each sub-application do not need to be repeatedly implemented by the business team and can be provided uniformly by the base application. For example:
- Error monitoring
- User behavior recording
- Permission control
- Global UI theme management
- Communication between sub-applications
- Application-level grayscale release
- Fallback handling when an application encounters an error
This vertical separation of responsibilities allows the business team to focus on core business development, improve development efficiency, and reduce repetitive work.
2. Version Coexistence
When developing new features, we usually conduct grayscale releases, but in many cases, this is just a simple switch and compatibility with the old version, rather than a strict grayscale release.
- The micro front-end architecture allows multiple versions of the same application to coexist. The base application can decide which version to use when loading the sub-application, so as to realize true grayscale release and version rollback.
- For example, if the new version released this week contains major modifications to the hotel and flight modules, and you want to roll back to the previous version, you only need to switch the application registry back to the old version.
- Of course, the rollback here only refers to the front-end code level; if configuration changes are involved, corresponding rollbacks are also required. If other content also implements versioning, version rollback can even be automated without manual intervention.
3. Solving Multi-Environment Problems
When different applications are in different test environments, it is often very troublesome to uniformly jump to the same test environment, and the communication cost is very high.
- Under the micro front-end architecture, you only need to point the application under development to the corresponding environment, and other applications can maintain a stable version, which greatly simplifies multi-environment management.
4. Improving Cross-Application User Experience
- Timely Response: Page switching can be completed quickly, while supporting friendly transition animations to avoid the abruptness of traditional click jumps.
- Keep-Alive: Support maintaining the DOM state of the previous page when returning to the previous page, improving user experience and operational continuity.
5. Cross-Application Performance Optimization (Not Unique to Micro Front-End, But Easier to Implement)
The micro front-end architecture also has obvious advantages in performance:
- Request Reuse: Avoid repeated requests and improve loading efficiency.
- Preloading: Load sub-application resources in advance to shorten waiting time.
- Pre-request: Predict and load resources that may be needed in the next step based on user operations to improve response speed.
6. Global Session Management
The base-style micro front-end architecture supports a global session mechanism:
- No matter how the application jumps, as long as a unified global session storage variable is provided, the user state can continue to exist.
- For example, multi-module modification operations will no longer lose user session information due to jumps.
In summary, the base-style micro front-end has significant advantages in terms of separation of responsibilities, version management, multi-environment support, user experience, performance optimization, and global session management, but the premise is that it must be used reasonably and the base application must be maintained by a dedicated team.
Common Frameworks and Technology Stacks for Micro Front-End
Client-Side Integration — single-spa
single-spa is a JavaScript micro front-end framework used to aggregate multiple single-page applications (SPAs) into a single overall application. Using single-spa for front-end architecture design can bring the following benefits:
- Integrate multiple front-end frameworks on the same page without refreshing the page (such as React, AngularJS, Angular, Ember, etc.).
- Support independent deployment of each single-page application.
- New features can use new frameworks, and old applications can coexist with them without rewriting.
single-spa’s base application is very lightweight and does not even need to use React. Sub-applications only need to export lifecycle methods that meet the agreement: bootstrap, mount, unmount.
However, single-spa also has certain limitations:
- Sub-applications exist in the form of JavaScript and can only be loaded and rendered on the client side.
- Does not provide a sandbox mechanism. If multiple sub-applications coexist, they need to be responsible for cleaning up side effects, otherwise conflicts may occur.
Client-Side Integration with Sandbox — qiankun / wujie / microApp
Similar to single-spa, these frameworks also implement micro front-ends by assembling multiple sub-applications on the client side.
The difference is that they introduce a sandbox mechanism, using browser features (such as , , or snapshot recovery) to isolate the global variables and side effects of each sub-application, so as to ensure security boundaries and avoid mutual interference between sub-applications.Proxyiframe
This makes them more suitable for complex page scenarios with horizontal splitting, and can provide a more stable running environment while ensuring independence.
Module Federation (MF)
Module Federation is a divide-and-conquer architecture pattern for JavaScript applications (similar to microservices in the back end). It allows multiple applications (or micro front-ends) to share code and resources, thereby supporting the splitting and on-demand loading of applications.
It should be noted that the core problem solved by Module Federation is “how to load and share modules between applications”. In other words, using Module Federation does not necessarily mean a micro front-end architecture, and implementing a micro front-end does not have to rely on Module Federation.
Module Federation does not conflict with the above solutions at all. single-spa/qiankun / wujie / microApp solves the loading and unloading of applications, and Module Federation solves the export and loading of modules. However, you can also regard the application as a module and export it to the Host application using Module Federation. You can also use Module Federation to import components in a sub-application while using other micro front-end frameworks. They can coexist.
Next, we will focus on client-side integration with sandbox — qiankun / wujie / microApp. When it comes to micro front-ends, most people will think of these solutions.
qiankun / wujie / microApp These micro front-end frameworks seem to be very mature, but they are rarely used in C-end applications, and most articles will say that micro front-ends are not a silver bullet. I think this is more related to the shortcomings of these frameworks, rather than the concept of micro front-ends. It is precisely because of these shortcomings that many people are hesitant to implement micro front-ends.
Disadvantages of Client-Side Integrated Micro Front-End
Strong Dependence on Client-Side Rendering
- Sub-applications usually exist in the form of JavaScript bundles and can only be loaded and rendered on the browser side.
- For those sub-applications that originally supported server-side rendering or streaming rendering, the base application also needs to wait for the HTML to be loaded and parsed. The final presentation is still client-side rendering instead of true server-side rendering. What many frameworks say is that they support server-side rendering, accurately speaking, they should support embedding the server-side rendering of sub-applications in the base application in a client-side rendering manner, rather than true server-side rendering.
Complex Global State and Side Effect Management
- When multiple sub-applications coexist, global variables, event bindings, timers, and other side effects are prone to mutual interference.
- If the framework does not provide a sandbox (such as native single-spa), each sub-application needs to handle the cleanup of side effects by itself, otherwise it may lead to conflicts or memory leaks.
- Even if there is a sandbox, there are still ways to escape the sandbox.
Additional Performance Overhead
- Sandboxes are inseparable from with functions, proxies, and even client-side rewriting of JS/CSS, which increases additional performance overhead. breaks the static scope, making it impossible for the engine to optimize, and the performance of function calls, attribute access, and loops will decrease.
with
Debugging Becomes Complex
The sub-application is no longer a complete system. You need to start multiple applications during development. If these factors are not considered when implementing the application, it will often lead to lower developer efficiency. I think this is one of the reasons why many developers hate micro front-ends.
Even though micro front-ends have so many advantages, there are still so many problems in the specific frameworks implemented. These shortcomings do not affect its advantages at all. I think this is not a problem with the concept of micro front-ends, but can only show that the current frameworks still have many problems that need to be solved, and even in many aspects we need to rethink.
Better Micro Front-End
Refer to Cloudflare Workers and Micro Frontends: Made for Each Other This article, we can get some inspiration. In fact, the core idea of this solution can be completely independent of Cloudflare Worker. Combining our pain points and practical experience, we can build a more efficient and secure micro front-end solution.
Build-Time Sandbox
Most existing micro front-end sandbox mechanisms are implemented on the client side, which brings additional runtime overhead and makes it difficult to detect potential problems early. A more ideal solution should complete sandbox processing at the build stage:
- Sub-Application Isolation: Ensure that the code of each sub-application meets the specifications at build time, and discover attempts to “escape the sandbox” during the development phase.
- CSS Sandbox: By solving style conflicts at the build stage, you can avoid additional style processing on the client side, thereby improving performance.
- Automatic Unloading Logic: We can integrate the unloading code of the sub-application during build time, so that it behaves like a normal component in the host application without the host needing to handle it additionally.
Here I have to mention bit.dev. bit.dev supports managing the engineering code of the application (such as scaffolding, webpack configuration, etc.) as a component. If the engineering component is modified, all projects that depend on these components can trigger a rebuild. This idea is very critical for micro front-end implementation, because the build itself directly affects the final presentation of the code. If the engineering code cannot be iterated efficiently, the maintainability and stability of the micro front-end will be seriously affected.
Support Streaming Rendering (Stream SSR)
In order to improve user experience, we hope that micro front-ends can be compatible with server-side rendering (SSR), and even support streaming SSR (Stream SSR). Many people think that micro front-ends and SSR conflict, but this is not actually the case: