Photo by Markus Spiske on Unsplash
Introduction
Web developers are beginning to take a strong interest in the idea of Micro-Frontends. Remix and Next.js are two examples of React meta-frameworks that have a lot of useful capabilities, but they don't always give complete support for creating micro-frontends. By providing built-in support for micro-frontends, Modern.js fills this gap and enables developers to design modular, scalable, and flexible apps.
Modern.js Framework
ByteDance created Modern.js, a progressive web framework built on React. Giving developers an outstanding development experience and empowering applications to provide better user experiences simplifies the process of creating contemporary web applications. In addition to having built-in state management, data gathering, and routing capabilities, Modern.js offers all required setups and tools for React applications.
Key features of Modern.js include:
- Rust Bundler: The primary bundler is Rspack, however, it can be switched to Webpack.
- Progressive: Facilitates the incremental integration of features via templates and plugins.
- Integration: Offers isomorphic CSR and SSR development in a unified development and production environment.
- Out of the Box: Provides debugging tools, ESLint, default TypeScript support, and integrated build tools.
- Ecology: Module packaging, micro-frontend support, and integrated state management.
- Routing Modes: Allows for file-convention-based and self-controlled routing.
- With integrated micro-frontend functionality, Modern.js facilitates scalable, maintainable, and flexible apps while streamlining the development process.
Rspack
Rspack is a high-performance JavaScript bundler written in Rust, designed to be compatible with the webpack ecosystem while providing significantly faster build speeds.
Why Rspack?
Rspack was created to solve performance issues at ByteDance, where large app projects had excessively long build and startup times. Rspack addresses these problems with:
- Fast Dev Mode Startup: Ensures quick startup to maintain developer productivity.
- Quick Builds: Reduces CI/CD pipeline build times, improving productivity and application delivery.
- Flexible Configuration: Maintains webpack's flexibility, easing the transition for legacy projects.
- Optimized Production Builds: Enhances production optimizations with Rust's multithreading capabilities.
Let's start
This guide will walk us through setting up a Modern.js project, enabling micro-frontend capabilities, and configuring multiple applications to work together seamlessly. We will use pnpm as our package manager and Rspack as the bundler. The shell app will act as the main application that dynamically loads micro-frontends.
Step-by-Step Instructions
1. Initialize the Repository
Start by initializing a new pnpm project in the root directory.
pnpm init
This will create a package.json file to manage dependencies and scripts for your project.
2. Create the Shell Application
Next, create the shell application using Modern.js.
npx @modern-js/create@latest shell-app
( Note: npx @modern-js/create@latest will also commit new changes. So you might have to manually remove .git folder and keep it only on the top level: rm -rf .git )
Select the following options:
- Project type: Web App
- Programming language: TS (TypeScript)
Package manager: pnpm
In the latest versions, Rspack is used as the default bundler.
3. Run the Shell Application
Navigate into the shell application directory and start the development server.
cd shell-app
pnpm start
Running the application ensures everything is set up correctly. You should see the default Modern.js application running in your browser.
4. Enable Micro-frontend Features
Enable micro-frontend features for the shell application.
pnpm run new
Select the following options:
- Operation: Enable Features
- Feature: Enable Micro Frontend
Enabling micro-frontend features configures the project to support dynamic loading and micro-frontend integration, allowing us to build modular and scalable applications.
5. Update modern.config.ts
Modify the configuration file to include necessary plugins and runtime settings.
// shell-app/modern.config.ts
import { appTools, defineConfig } from '@modern-js/app-tools';
import { garfishPlugin } from '@modern-js/plugin-garfish';
export default defineConfig({
runtime: {
router: true,
state: true,
masterApp: {},
},
plugins: [appTools(), garfishPlugin()],
});
This configuration enables the router and sets up the shell app as a main application using the garfishPlugin.
(Garfish, developed by the ByteDance team, is a micro-frontend framework similar to single-spa. Modern.js handles the rest under the hood, so we don't need to focus much on it.)
6. Create Custom Entry and Update App.tsx
Remove the default routes folder and create a new App.tsx file in the src directory.
// shell-app/src/App.tsx
import { Suspense } from 'react';
import { defineConfig } from '@modern-js/runtime';
import {
BrowserRouter as Router,
Routes,
Route,
} from '@modern-js/runtime/router';
import styled from '@modern-js/runtime/styled';
const AppContainer = styled.div`
text-align: center;
padding: 20px;
background-color: #f5f5f5;
`;
const Title = styled.h1`
color: #333;
`;
const Loading = styled.div`
font-size: 1.2em;
color: #666;
`;
const App: React.FC = () => {
return (
<Router>
<AppContainer>
<Suspense fallback={<Loading>Loading...</Loading>}>
<Routes>
<Route
path="/"
element={<Title>Welcome to the Shell Application</Title>}
/>
</Routes>
</Suspense>
</AppContainer>
</Router>
);
};
export default defineConfig(App, {
masterApp: {
manifest: {
getAppList: async () => {
// here we will add our mfe
return [];
},
},
},
});
Here, we set up the main component for the shell application, configuring it as the master application that will manage micro-frontends.
7. Create the First Micro-frontend Application
Navigate back to the root directory and create the first micro-frontend.
cd ..
npx @modern-js/create@latest mfe1
Select the following options:
- Project type: Web App
- Programming language: TS (TypeScript)
- Package manager: pnpm
8. Enable Micro-frontend Feature for mfe1
Enable micro-frontend features for the first micro-frontend application.
cd mfe1
pnpm run new
Select the following options:
- Operation: Enable Features
- Feature: Enable Micro Frontend
9. Update modern.config.ts for mfe1
Update the configuration file for the micro-frontend.
// mfe1/modern.config.ts
import { appTools, defineConfig } from '@modern-js/app-tools';
import { garfishPlugin } from '@modern-js/plugin-garfish';
export default defineConfig({
server: {
port: 8081,
},
deploy: {
microFrontend: true,
},
plugins: [appTools(), garfishPlugin()],
});
This configuration sets the port and enables micro-frontend features.
10. Create Custom Entry and Update App.tsx for mfe1
Remove the default routes folder and create a new App.tsx file in the src directory.
// mfe1/src/App.tsx
import React from 'react';
import {
BrowserRouter as Router,
Routes,
Route,
Link,
} from '@modern-js/runtime/router';
import styled from '@modern-js/runtime/styled';
const Container = styled.div`
font-size: 1.2em;
color: #333;
`;
const AppContainer = styled.div`
text-align: center;
padding: 20px;
background-color: #f5f5f5;
`;
const NavBar = styled.nav`
margin-bottom: 20px;
a {
margin: 0 10px;
color: #007acc;
text-decoration: none;
}
`;
const Contact: React.FC = () => {
return <Container>Contact Page</Container>;
};
const Home: React.FC = () => {
return <Container>Home Page</Container>;
};
const About: React.FC = () => {
return <Container>About Page</Container>;
};
const App: React.FC = () => {
return (
<Router basename="mfe1">
<AppContainer>
<NavBar>
<Link to="/">Home</Link>
<Link to="/about">About</Link