DEV Community

mobisoftinfotech
mobisoftinfotech

Posted on

React Micro Frontend Architecture – An in Depth Tutorial With Example

Image description

Why Micro-Frontends?

Micro frontend architecture is becoming increasingly popular due to its performance benefits and the ability to reduce developer dependencies. By breaking down a monolithic frontend into smaller, independent micro-applications, teams can work on different parts of a project simultaneously, improving scalability and maintainability.

What You Will Learn in This Tutorial

In this guide, we will walk through the process of setting up a micro-frontend architecture using React and Vite. You will learn:

  • How to configure Module Federation with Vite.
  • How to create a remote micro-frontend that exposes components.
  • How to dynamically reuse a remote component in a host application.

Project Use Case: E-Commerce Application

For this tutorial, we will use an e-commerce application as an example. The host application (home app) will consume and display a Featured Products List from the Products Micro-Frontend. This approach showcases how a fully functional component can be shared across multiple applications without duplicating code.

By the end of this tutorial, you will have a clear understanding of how to structure a micro-frontend application with React and Vite, making your micro frontend architecture more flexible and scalable.

Image description

Setting Up the Microfrontend Project

Create the Main Directory

mkdir micro-frontend-project
cd micro-frontend-project

Create two separate Vite projects:

  • Products (Remote App) → Micro-frontend exposing a component.
  • Host (Main App) → Application consuming the remote component.

Setting Up the Remote Products Feature

Step 1: Create the Products Project

npm create vite@latest

  • Enter project name: products
  • Select React
  • Select TypeScript

Image description

Step 2: Install Module Federation Plugin

cd products
npm install @module-federation/vite

Step 3: Implement Product List Feature

1. Create Product List Page:

Create src/pages/ProductsList/ProductsList.tsx to display featured and all products.

import { useEffect, useState } from "react";
import { ProductListItem } from "../../components/ProductListItem/ProductListItem";
import { IProduct } from "../../interfaces/IProduct";
import "./ProductsList.css";
import FeaturedProductsList from "../../components/FeaturedProductsList/FeaturedProductsList";


export const ProductList = () => {
 const [products, setProducts] = useState<IProduct[]>([]);


 useEffect(() => {
   fetchProducts();
 }, []);


 const fetchProducts = async () => {
   try {
     const productsResponse = await fetch("https://dummyjson.com/products");
     const productsResponseJson = await productsResponse.json();
     setProducts(productsResponseJson.products);
   } catch (error) {
     console.log("error", error);
   }
 };


 return (
   <div className="product-list-container">
     <h2>Products</h2>
     <FeaturedProductsList></FeaturedProductsList>
     <h2 className="heading">All Products</h2>
     <div className="products-list">
       {products.map((item) => (
         <ProductListItem product={item}></ProductListItem>
       ))}
     </div>
   </div>
 );
};

Enter fullscreen mode Exit fullscreen mode

Here, we are using a dummy API for the product list.

2. Create Product List CSS:

Create src/pages/ProductsList/ProductsList.css

.products-list {
 display: grid;
 grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
 grid-auto-rows: minmax(250px, auto);
}
.product-list-container {
 height: 100vh;
 width: 100%;
 text-align: flex-start;
}
Enter fullscreen mode Exit fullscreen mode

3. Create Product Interface:

Create src/interfaces/IProduct.ts

export interface IProduct {
 title: string;
 description: string;
 price: string;
 images: string[];
}

Enter fullscreen mode Exit fullscreen mode

4. Create Product Card Component:

Create src/components/ProductListItem/ProductListItem.tsx

import { IProduct } from "../../interfaces/IProduct";
import "./ProductListItem.css";


interface ProductListItemProps {
 product: IProduct;
}


export const ProductListItem = (props: ProductListItemProps) => {
 const { product } = props;
 return (
   <div className="product-card">
     <div className="product-image-container">
       <img src={product.images[0]} className="product-image"></img>
     </div>
     <h3>{product.title}</h3>
     <h2>{`$ ${product.price}`}</h2>
     <h6>{product.description}</h6>
   </div>
 );
};

Enter fullscreen mode Exit fullscreen mode

5. Create Product Card CSS file:

Create src/components/ProductListItem/ProductListItem.css

.product-image {
 width: 120px;
 height: 120px;
}
.product-card {
 background-color: white;
 border-radius: 4px;
 box-shadow: rgba(100, 100, 111, 0.2) 0px 7px 29px 0px;
 padding: 8px;
 margin: 8px;
 min-width: 285px;
}
.product-image-container {
 display: flex;
 justify-content: center;
}
Enter fullscreen mode Exit fullscreen mode

6. Create Featured Product List Component:

Create src/components/FeaturedProductsList/FeaturedProductsList.tsx

import { useEffect, useState } from "react";
import { IProduct } from "../../interfaces/IProduct";
import { ProductListItem } from "../ProductListItem/ProductListItem";
import "./FeaturedProductsList.css";


const FeaturedProductsList = () => {
 const [featuredProducts, setFeaturedProducts] = useState<IProduct[]>([]);


 useEffect(() => {
   fetchProducts();
 }, []);


 const fetchProducts = async () => {
   try {
     const productsResponse = await fetch('https://dummyjson.com/products?limit=10&skip=10&select=title,price,images,description');
     const productsResponseJson = await productsResponse.json();
     setFeaturedProducts(productsResponseJson.products);
   } catch (error) {
     console.log("error", error);
   }
 };


 return (
   <div>
     <h2 className="heading">Featured Products</h2>
     <div className="featured-product-list">
       {featuredProducts.map((item) => (
         <ProductListItem product={item}></ProductListItem>
       ))}
     </div>
   </div>
 );
};


export default FeaturedProductsList;
Enter fullscreen mode Exit fullscreen mode

7. Create Featured Product List CSS file:

Create src/components/FeaturedProductsList/FeaturedProductsList.css

.featured-product-list {
 display: flex;
 flex-direction: row;
 overflow-x: scroll;
}

Enter fullscreen mode Exit fullscreen mode

8. Use the Products page in the App file:

import './App.css'
import { ProductList } from './pages/ProductsList/ProductsList'


function App() {


 return (
   <>
    <ProductList></ProductList>
   </>
 )
}


export default App
Enter fullscreen mode Exit fullscreen mode
  1. Update App.css default CSS: Update only root css
#root {
 max-width: 1280px;
 padding: 2rem;
 height: 100vh;
}

Enter fullscreen mode Exit fullscreen mode

10. Update port in the package.json:

...  
"scripts": {
   "dev": "vite --port 3000",
   "build": "tsc -b && vite build",
   "lint": "eslint .",
   "preview": "vite preview --port 3000"
 },
...

Enter fullscreen mode Exit fullscreen mode

Step 4: Configure Module Federation

Update vite.config.ts
import { federation } from "@module-federation/vite";
import react from "@vitejs/plugin-react";
import { defineConfig } from "vite";
import { dependencies } from "./package.json";
export default defineConfig(() => {
 return {
   build: {
     target: "chrome89",
   },
   plugins: [
     federation({
       filename: "remoteEntry.js",
       name: "products",
       exposes: {
         "./featured-products":
           "./src/components/FeaturedProductsList/FeaturedProductsList.tsx",
       },
       remotes: {},
       shared: {
         react: {
           requiredVersion: dependencies.react,
           singleton: true,
         },
       },
     }),
     react(),
   ],
 };
});

Enter fullscreen mode Exit fullscreen mode

filename: "remoteEntry.js": Specifies the entry file for the microfrontend, which other applications can access.
name: "products": Defines the unique identifier for this microfrontend.
This allows other microfrontends (hosts) to import the FeaturedProductsList component.
"./featured-products": This is how the module will be imported remotely.
"./src/components/FeaturedProductsList/FeaturedProductsList.tsx": The actual file that gets exposed.

Setting Up the Host Application

Step 1: Create the Host Project

npm create vite@latest

  • Enter project name: host
  • Select React
  • Select TypeScript

Step 2: Install Module Federation Plugin

cd host
npm install @module-federation/vite

Step 3: Implement Home Page

1. Create Home Page:

Create pages/home/Home.tsx

import React, { Suspense } from "react";


const FeaturedProducts = React.lazy(
   // @ts-ignore
   async () => import('products/featured-products'),
);


const Home = () => {
 return (
   <div>
     <h2>Home</h2>
     <Suspense fallback="loading...">
       <FeaturedProducts />
     </Suspense>
   </div>
 );
};


export default Home;

Enter fullscreen mode Exit fullscreen mode

Imported Featured Products from Products (Remote app)

2. Update

App.tsx

import "./App.css";
import Home from "./pages/home/Home";


function App() {
 return <Home></Home>;
}


export default App;

Enter fullscreen mode Exit fullscreen mode

3. Update App.css default CSS:

Update only root css

#root {
 max-width: 1280px;
 padding: 2rem;
 height: 100vh;
}

Enter fullscreen mode Exit fullscreen mode

Step 4: Configure Module Federation

Update vite.config.ts

import { defineConfig } from "vite";
import react from "@vitejs/plugin-react";
import {federation} from "@module-federation/vite";
import { dependencies } from './package.json';




// https://vite.dev/config/
export default defineConfig({
 build: {
   target: 'esnext',
   minify: false
 },
 plugins: [
   federation({
     name: "app",
     remotes: {
       products: {
         type: 'module',
         name: 'products',
         entry: 'http://localhost:3000/remoteEntry.js',
         entryGlobalName: 'remote',
         shareScope: 'default',
       },
     },
     filename: "remoteEntry.js",     
     shared: {
       react: {
         requiredVersion: dependencies.react,
         singleton: true,
       },
     },
   }),

   react(),
 ], 
});

Enter fullscreen mode Exit fullscreen mode
  • remotes: Specifies the remote applications (microfrontends) that will be loaded dynamically.
  • products: The key that identifies the remote application.
  • type: 'module': Indicates that the remote entry is an ES module.
  • name: 'products': The unique name of the remote application.
  • entry: 'http://localhost:3000/remoteEntry.js': The remote entry file URL where the microfrontend is hosted.
  • entryGlobalName: 'remote': Specifies a global namespace for the remote module.
  • shareScope: 'default': Ensures dependency sharing across microfrontends.
  • shared: Specifies shared dependencies across microfrontends to avoid duplicate React versions.

Final Steps: Run Both Applications

Run the Remote App (Products)

cd products
npm run dev

On http://localhost:3000/, you can see the featured products and a list of all products.

Image description

Run the Host App

cd host
npm run dev

Now, the Host Application dynamically loads the Featured Products Component from the Products Micro-Frontend using vite module federation

Image description

This repository contains the code for the tutorial: “React Micro Frontend Architecture – An in Depth Tutorial With Example“, published by Mobisoft – Web Application Development Company, Houston.

Feel free to download a sample example I’ve created from GitHub to get started.

Conclusion

Micro-frontend architecture is revolutionizing front-end development by enabling teams to work independently on different parts of an application while ensuring seamless integration. In this tutorial, we explored how to set up a micro-frontend architecture using React, Vite, and Module Federation, allowing components to be dynamically shared across multiple applications.

By implementing a Products Micro-Frontend and a Host Application, we demonstrated how to reuse a fully functional component (Featured Products List) without duplicating code.

This approach makes frontend development scalable, modular, and efficient, reducing dependencies between teams and improving performance. With micro-frontends, applications can be developed and deployed independently, making them easier to maintain and extend over time.

Image description

Source Link: React Micro Frontend Architecture – An in Depth Tutorial With Example

Playwright CLI Flags Tutorial

5 Playwright CLI Flags That Will Transform Your Testing Workflow

  • --last-failed: Zero in on just the tests that failed in your previous run
  • --only-changed: Test only the spec files you've modified in git
  • --repeat-each: Run tests multiple times to catch flaky behavior before it reaches production
  • --forbid-only: Prevent accidental test.only commits from breaking your CI pipeline
  • --ui --headed --workers 1: Debug visually with browser windows and sequential test execution

Learn how these powerful command-line options can save you time, strengthen your test suite, and streamline your Playwright testing experience. Practical examples included!

Watch Video 📹️

Top comments (0)

Image of Timescale

📊 Benchmarking Databases for Real-Time Analytics Applications

Benchmarking Timescale, Clickhouse, Postgres, MySQL, MongoDB, and DuckDB for real-time analytics. Introducing RTABench 🚀

Read full post →

👋 Kindness is contagious

If this article connected with you, consider tapping ❤️ or leaving a brief comment to share your thoughts!

Okay