DEV Community

Cover image for Building Micro Frontends with React, Vite, and Module Federation: A Complete Guide
Dimuthu Abeysinghe
Dimuthu Abeysinghe

Posted on

Building Micro Frontends with React, Vite, and Module Federation: A Complete Guide

πŸ”— GitHub Repository: https://github.com/Dimuthu7/Micro-frontend-with-React-Vite

A comprehensive guide to building production-ready micro frontends architecture using React, TypeScript, Vite, and Module Federation. Learn how to break down monolithic frontends into independent, scalable applications.

Table of Contents

  1. Introduction to Micro Frontends
  2. Why Micro Frontends?
  3. Benefits of Micro Frontends Architecture
  4. Project Overview
  5. Tech Stack
  6. Understanding Module Federation
  7. Architecture Deep Dive
  8. Implementation Details
  9. Local Development Setup
  10. Docker Setup and Deployment
  11. Running the Project
  12. Best Practices Implemented
  13. Conclusion

Introduction to Micro Frontends

Micro Frontends is an architectural approach that extends the microservices pattern to the frontend. Instead of building a monolithic frontend application, you break it down into smaller, independent applications that can be developed, tested, and deployed separately.

Think of it like a restaurant: instead of one chef preparing everything, you have specialized chefs (teams) working on different dishes (features) that come together to create a complete meal (user experience).


Why Micro Frontends?

The Problem with Monolithic Frontends

As applications grow, monolithic frontends face several challenges:

  1. Scaling Issues: Large codebases become difficult to navigate and maintain
  2. Deployment Bottlenecks: One bug can block the entire release
  3. Team Coordination: Multiple teams working on the same codebase creates conflicts
  4. Technology Lock-in: Difficult to adopt new technologies or upgrade dependencies
  5. Performance: Large bundles slow down initial page load

Benefits of Micro Frontends Architecture

1. Team Autonomy & Scalability

  • Independent Development: Teams can work in parallel without stepping on each other
  • Own Technology Stack: Each team can choose frameworks that best fit their needs
  • Faster Feature Delivery: Smaller codebases mean faster development cycles
  • Reduced Coordination: Less need for cross-team meetings and synchronization

2. Independent Deployment

  • Isolated Releases: Deploy features independently without affecting other parts
  • Faster Rollouts: Release features as soon as they're ready
  • Safer Rollbacks: Rollback individual features without impacting the entire app
  • A/B Testing: Easier to test new features with a subset of users

3. Technology Diversity

  • Framework Flexibility: Use React for one feature, Vue for another, Angular for a third
  • Gradual Migration: Migrate legacy code piece by piece
  • Technology Experimentation: Try new technologies without rewriting everything
  • Independent Upgrades: Upgrade dependencies per micro frontend

4. Code Isolation & Maintainability

  • Smaller Codebases: Easier to understand, test, and maintain
  • Clear Boundaries: Well-defined interfaces prevent tight coupling
  • Easier Onboarding: New developers can focus on one micro frontend
  • Better Organization: Clear separation of concerns

5. Performance Optimization

  • Code Splitting: Load only what's needed, when it's needed
  • Independent Caching: Each micro frontend can have its own caching strategy
  • Smaller Bundles: Smaller applications mean smaller bundle sizes
  • Lazy Loading: Load components on-demand for better performance

6. Fault Isolation

  • Error Containment: Errors in one micro frontend don't crash the entire app
  • Graceful Degradation: Error boundaries can handle failures elegantly
  • Better Resilience: One failing feature doesn't break the user experience

Project Overview

This project demonstrates a production-ready micro frontends architecture using a Product Catalog application. The application consists of:

  • Host Application: The container/orchestrator that displays the product catalog and manages application state
  • Remote Application: A micro frontend that provides detailed product information components

Project Structure

β”œβ”€β”€ host/ # Container application (Host)
β”‚ β”œβ”€β”€ src/
β”‚ β”‚ β”œβ”€β”€ components/
β”‚ β”‚ β”‚ β”œβ”€β”€ Header.tsx # Application header with cart
β”‚ β”‚ β”‚ β”œβ”€β”€ Footer.tsx # Application footer
β”‚ β”‚ β”‚ β”œβ”€β”€ ProductList.tsx # Product grid component
β”‚ β”‚ β”‚ β”œβ”€β”€ ProductListView.tsx # Product list view with header
β”‚ β”‚ β”‚ β”œβ”€β”€ ProductDetailsView.tsx # Product details view container
β”‚ β”‚ β”‚ β”œβ”€β”€ BackButton.tsx # Reusable back navigation button
β”‚ β”‚ β”‚ └── ErrorBoundary.tsx # Error boundary for graceful error handling
β”‚ β”‚ β”œβ”€β”€ data/
β”‚ β”‚ β”‚ └── products.ts # Product data
β”‚ β”‚ β”œβ”€β”€ types/
β”‚ β”‚ β”‚ └── index.ts # TypeScript type definitions
β”‚ β”‚ └── App.tsx # Main application (orchestrator)
β”‚ β”œβ”€β”€ Dockerfile
β”‚ └── nginx.conf
β”œβ”€β”€ remote/ # Micro frontend application (Remote)
β”‚ β”œβ”€β”€ src/
β”‚ β”‚ β”œβ”€β”€ components/
β”‚ β”‚ β”‚ └── ProductCard.tsx # Remote component (exposed via Module Federation)
β”‚ β”‚ └── types/
β”‚ β”‚ └── index.ts # TypeScript type definitions
β”‚ β”œβ”€β”€ Dockerfile
β”‚ └── nginx.conf
β”œβ”€β”€ docker-compose.yml # Docker orchestration
└── README.md
Enter fullscreen mode Exit fullscreen mode

Tech Stack

Core Technologies

  • React 18: Modern UI library with hooks and Suspense for async operations
  • TypeScript 5: Type-safe development with compile-time error checking
  • Vite 5: Fast build tool and dev server with excellent HMR (Hot Module Replacement)
  • Tailwind CSS 3: Utility-first CSS framework for rapid UI development

Micro Frontends Technology

  • Module Federation: Runtime module sharing and dynamic loading
  • @originjs/vite-plugin-federation: Vite plugin that enables Module Federation

DevOps & Infrastructure

  • Docker & Docker Compose: Containerization for consistent environments
  • Nginx: Production web server with optimized configuration
  • Node.js 18+: JavaScript runtime environment

Understanding Module Federation

What is Module Federation?

Module Federation is a JavaScript architecture that allows a JavaScript application to dynamically load code from another application at runtime. It was introduced by Webpack 5 and has been adapted for Vite through plugins.

Key Concepts

1. Host Application (Container)

The host application is the main application that consumes remote modules. It:

  • Loads and orchestrates remote components
  • Manages application state
  • Handles routing and navigation
  • Provides the shell/framework for the application

2. Remote Application (Micro Frontend)

The remote application exposes modules that can be consumed by the host. It:

  • Exposes components, utilities, or features
  • Can run independently
  • Shares dependencies with the host to avoid duplication
  • Maintains its own build and deployment pipeline

3. Shared Dependencies

Both host and remote can share common dependencies (like React, React-DOM) to:

  • Avoid loading duplicate code
  • Ensure consistent versions
  • Reduce bundle sizes
  • Maintain singleton instances

How Module Federation Works

β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚                  Browser (Client)                       β”‚
β”‚                                                         β”‚
β”‚ 1. User visits Host App (localhost:5173)                β”‚
β”‚ 2. Host App loads and initializes                       β”‚
β”‚ 3. User clicks on a product                             β”‚
β”‚ 4. Host App requests Remote Component                   β”‚
β”‚                                                         β”‚
β”‚ β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”                β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”          β”‚
β”‚ β”‚ Host App    β”‚                β”‚ Remote App  β”‚          β”‚
β”‚ β”‚ (Port 5173) │──────────────▢ β”‚ (Port 5174) β”‚          β”‚
β”‚ β”‚             β”‚ HTTP Request   β”‚             β”‚          β”‚
β”‚ β”‚             │◀────────────── β”‚             β”‚          β”‚
β”‚ β”‚             β”‚ remoteEntry.js β”‚             β”‚          β”‚
β”‚ β”‚             β”‚ + ProductCard  β”‚             β”‚          β”‚
β”‚ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜                β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜          β”‚
β”‚                                                         β”‚
β”‚ 5. Module Federation loads remote component dynamically β”‚
β”‚ 6. Component renders in Host App context                β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
Enter fullscreen mode Exit fullscreen mode

Architecture Deep Dive

Component Architecture

The host application follows a clean component-based architecture:

App.tsx (Orchestrator)
β”œβ”€β”€ Header (Cart display)
β”œβ”€β”€ ProductListView (Product catalog)
β”‚   └── ProductList (Product grid)
└── ProductDetailsView (Remote component container)
    └── RemoteProductCard (Loaded from remote app)
Enter fullscreen mode Exit fullscreen mode

Data Flow

β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”         Props          β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚ Host App   │───────────────────────▢│ Remote App  β”‚
β”‚            β”‚                        β”‚             β”‚
β”‚ - State    │◀───────────────────────│ - Component β”‚
β”‚ - Handlers β”‚        Callbacks       β”‚ - UI        β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜                        β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
      β”‚                                      β”‚
      β”‚    Shared React Instance             β”‚
      β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
Enter fullscreen mode Exit fullscreen mode

Complete Request Flow

  1. Initial Page Load

    • Browser requests the host app
    • Nginx serves index.html and host app bundle
    • React app initializes and renders ProductListView
    • Product catalog displays
  2. User Clicks Product

    • Host app updates state with selected product
    • Switches to ProductDetailsView
    • Component lazy loads the remote component
    • Module Federation makes HTTP request to remote app
  3. Remote Component Loading

    • Host requests remoteEntry.js from remote app
    • Remote app serves Module Federation manifest
    • Host requests component bundle (ProductCard.[hash].js)
    • Module Federation runtime loads and executes component
    • Component renders in host context with shared React instance
  4. User Interaction

    • User clicks "Add to Cart" in remote component
    • Callback function executes in host app
    • Host app updates cart count state
    • Header re-renders with new count

Implementation Details

Module Federation Configuration

Host Application (host/vite.config.ts)

import { defineConfig } from 'vite';
import react from '@vitejs/plugin-react';
import federation from '@originjs/vite-plugin-federation';

export default defineConfig({
  plugins: [
    react(),
    federation({
      name: 'host_app',
      remotes: {
        remote_app: {
          external: `http://${process.env.VITE_REMOTE_HOST || 'localhost'}:${process.env.VITE_REMOTE_PORT || '5174'}/assets/remoteEntry.js`,
          shareScope: 'default',
        },
      },
      shared: {
        react: {
          singleton: true,
          requiredVersion: '^18.2.0',
        },
        'react-dom': {
          singleton: true,
          requiredVersion: '^18.2.0',
        },
      },
    }),
  ],
  build: {
    modulePreload: false,
    target: 'esnext',
    minify: false,
    cssCodeSplit: false,
  },
  server: {
    port: Number(process.env.PORT) || 5173,
    host: true,
  },
});
Enter fullscreen mode Exit fullscreen mode

Key Points:

  • remotes: Defines where to load remote modules from
  • shared: Specifies dependencies to share (React, React-DOM)
  • singleton: true: Ensures only one instance of React is loaded
  • Environment variables allow different URLs for different environments

Remote Application (remote/vite.config.ts)

import { defineConfig } from 'vite';
import react from '@vitejs/plugin-react';
import federation from '@originjs/vite-plugin-federation';

export default defineConfig({
  plugins: [
    react(),
    federation({
      name: 'remote_app',
      filename: 'remoteEntry.js',
      exposes: {
        './ProductCard': './src/components/ProductCard',
      },
      shared: {
        react: {
          singleton: true,
          requiredVersion: '^18.2.0',
        },
        'react-dom': {
          singleton: true,
          requiredVersion: '^18.2.0',
        },
      },
    }),
  ],
  build: {
    modulePreload: false,
    target: 'esnext',
    minify: false,
    cssCodeSplit: false,
  },
  server: {
    port: Number(process.env.PORT) || 5174,
    host: true,
    cors: true,
  },
});
Enter fullscreen mode Exit fullscreen mode

Key Points:

  • exposes: Defines which modules/components to expose
  • filename: Name of the Module Federation manifest file
  • shared: Same shared dependencies as host to avoid duplication

Loading Remote Components

ProductDetailsView Component

import React, { Suspense, lazy } from 'react';
import { ErrorBoundary } from './ErrorBoundary';

// Lazy load the remote ProductCard component
const RemoteProductCard = lazy(() => import('remote_app/ProductCard'));

interface ProductDetailsViewProps {
  product: Product;
  onAddToCart: () => void;
  onClose: () => void;
}

export const ProductDetailsView: React.FC<ProductDetailsViewProps> = ({
  product,
  onAddToCart,
  onClose,
}) => {
  return (
    <ErrorBoundary
      fallback={
        <div>Failed to load remote component</div>
      }
    >
      <Suspense
        fallback={
          <div>Loading product details...</div>
        }
      >
        <RemoteProductCard 
          product={product} 
          onAddToCart={onAddToCart} 
        />
      </Suspense>
    </ErrorBoundary>
  );
};
Enter fullscreen mode Exit fullscreen mode

Key Features:

  • Lazy Loading: Uses React's lazy() to dynamically import the remote component
  • Suspense: Provides loading state while component is being fetched
  • Error Boundary: Gracefully handles errors if remote component fails to load
  • Type Safety: TypeScript ensures props match the expected interface

Error Handling

The project implements a robust error boundary:

import { Component, ErrorInfo, ReactNode } from 'react';

interface Props {
  children: ReactNode;
  fallback?: ReactNode;
}

interface State {
  hasError: boolean;
  error?: Error;
}

export class ErrorBoundary extends Component<Props, State> {
  public state: State = {
    hasError: false,
  };

  public static getDerivedStateFromError(error: Error): State {
    return { hasError: true, error };
  }

  public componentDidCatch(error: Error, errorInfo: ErrorInfo) {
    console.error('ErrorBoundary caught an error:', error, errorInfo);
  }

  public render() {
    if (this.state.hasError) {
      return this.props.fallback || <DefaultErrorUI />;
    }
    return this.props.children;
  }
}
Enter fullscreen mode Exit fullscreen mode

This ensures that if the remote component fails to load, the application doesn't crash and shows a user-friendly error message.

Type Safety Across Applications

Both applications share the same TypeScript interfaces:

// host/src/types/index.ts
// remote/src/types/index.ts
export interface Product {
  id: string;
  name: string;
  description: string;
  price: number;
  image: string;
  category: string;
  inStock: boolean;
}
Enter fullscreen mode Exit fullscreen mode

This ensures type safety when passing data between host and remote applications.


Local Development Setup

Prerequisites

  • Node.js 18+ and npm/yarn/pnpm
  • Code Editor (VS Code recommended)
  • Git (for cloning the repository)

Step-by-Step Setup

1. Install Dependencies

Install Remote App Dependencies:

cd remote
npm install
Enter fullscreen mode Exit fullscreen mode

Install Host App Dependencies:

cd ../host
npm install
Enter fullscreen mode Exit fullscreen mode

Or use the Makefile:

make install
Enter fullscreen mode Exit fullscreen mode

2. Understanding the Setup

The applications need to run simultaneously because:

  • The host app needs to fetch the remote component from the remote app
  • Both apps need to be running for Module Federation to work
  • The remote app must be accessible via HTTP

3. Running the Applications

You need two terminal windows:

Terminal 1 - Remote App (must start first):

cd remote
npm run dev
Enter fullscreen mode Exit fullscreen mode

The remote app will run on http://localhost:5174

Terminal 2 - Host App:

cd host
npm run dev
Enter fullscreen mode Exit fullscreen mode

The host app will run on http://localhost:5173

4. Verify the Setup

  1. Open your browser and navigate to http://localhost:5173
  2. You should see the product catalog
  3. Click on any product
  4. The product details should load from the remote micro frontend
  5. Check the browser console for any errors

Environment Variables

You can customize the setup using environment variables:

Host App (.env file):

PORT=5173
VITE_REMOTE_HOST=localhost
VITE_REMOTE_PORT=5174
Enter fullscreen mode Exit fullscreen mode

Remote App (.env file):

PORT=5174
Enter fullscreen mode Exit fullscreen mode

Troubleshooting Local Setup

Issue: Remote component not loading

  • Ensure the remote app is running before starting the host app
  • Check that both apps are running on the correct ports
  • Verify the remote URL in host/vite.config.ts
  • Check browser console for CORS errors

Issue: CORS errors

  • The remote app's Vite dev server has CORS enabled
  • Ensure both apps are running
  • Check that ports match the configuration

Issue: Type errors

  • Run npm run build to check for TypeScript errors
  • Ensure both apps have the same type definitions
  • Check that shared types are synchronized

Docker Setup and Deployment

Why Docker?

Docker provides:

  • Consistent Environments: Same environment in development and production
  • Easy Deployment: Package everything needed to run the application
  • Isolation: Each application runs in its own container
  • Orchestration: Docker Compose manages multiple containers

Docker Architecture

β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚         Docker Network (microfrontends-network)         β”‚
β”‚                                                         β”‚
β”‚  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”               β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”        β”‚
β”‚  β”‚  host-app    β”‚               β”‚ remote-app   β”‚        β”‚
β”‚  β”‚  :5173       │──────────────▢│ :5174        β”‚        β”‚
β”‚  β”‚  (Nginx)     β”‚  HTTP Request β”‚ (Nginx)      β”‚        β”‚
β”‚  β”‚              │◀──────────────│              β”‚        β”‚
β”‚  β”‚              β”‚  remoteEntry  β”‚              β”‚        β”‚
β”‚  β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜               β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜        β”‚
β”‚                                                         β”‚
β”‚  Both containers communicate via service names          β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
Enter fullscreen mode Exit fullscreen mode

Dockerfile Structure

Both applications use multi-stage builds:

Stage 1: Builder

FROM node:18-alpine AS builder

WORKDIR /app

# Copy package files
COPY package*.json ./

# Install dependencies
RUN npm ci

# Copy source code
COPY . .

# Build the application
RUN npm run build
Enter fullscreen mode Exit fullscreen mode

Stage 2: Production

FROM nginx:alpine

# Install wget for health checks
RUN apk add --no-cache wget

# Copy built assets from builder stage
COPY --from=builder /app/dist /usr/share/nginx/html

# Copy nginx configuration
COPY nginx.conf /etc/nginx/conf.d/default.conf

# Expose port
EXPOSE 80

# Start nginx
CMD ["nginx", "-g", "daemon off;"]
Enter fullscreen mode Exit fullscreen mode

Benefits of Multi-Stage Builds:

  • Smaller final image (only production files)
  • Faster builds (cached layers)
  • Security (no build tools in production image)

Docker Compose Configuration

services:
  remote-app:
    build:
      context: ./remote
      dockerfile: Dockerfile
    container_name: micro-frontend-remote
    ports:
      - "5174:80"
    networks:
      - micro-frontend-network
    healthcheck:
      test: ["CMD", "wget", "--quiet", "--tries=1", "--spider", "http://localhost/assets/remoteEntry.js"]
      interval: 10s
      timeout: 5s
      retries: 5

  host-app:
    build:
      context: ./host
      dockerfile: Dockerfile
      args:
        - VITE_REMOTE_HOST=remote-app
        - VITE_REMOTE_PORT=80
    container_name: micro-frontend-host
    ports:
      - "5173:80"
    depends_on:
      remote-app:
        condition: service_healthy
    networks:
      - micro-frontend-network

networks:
  micro-frontend-network:
    driver: bridge
Enter fullscreen mode Exit fullscreen mode

Key Features:

  • Health Checks: Ensures remote app is ready before starting host app
  • Service Names: Containers communicate using service names (remote-app)
  • Network Isolation: Both apps on the same Docker network
  • Port Mapping: Exposes apps on host machine ports

Nginx Configuration

Why Nginx?

  1. Production Web Server: More performant than development servers
  2. CORS Headers: Required for Module Federation cross-origin requests
  3. SPA Routing: Handles client-side routing correctly
  4. Performance: Gzip compression, caching, security headers

Remote App Nginx Config

server {
    listen 80;
    root /usr/share/nginx/html;

    # CORS headers for Module Federation
    location = /assets/remoteEntry.js {
        add_header Access-Control-Allow-Origin * always;
        add_header Access-Control-Allow-Methods 'GET, POST, OPTIONS' always;
        add_header Cache-Control "no-cache, no-store, must-revalidate";
    }

    # CORS for JavaScript files
    location ~* ^/assets/.*\.js$ {
        add_header Access-Control-Allow-Origin * always;
        expires 1y;
        add_header Cache-Control "public, immutable";
    }

    # SPA routing
    location / {
        try_files $uri $uri/ /index.html;
    }
}
Enter fullscreen mode Exit fullscreen mode

Key Points:

  • CORS Headers: Essential for cross-origin Module Federation requests
  • No Cache for remoteEntry.js: Ensures latest version is always loaded
  • Cache for Assets: Optimizes performance with long-term caching
  • SPA Routing: Serves index.html for all routes

Running the Project

Option 1: Docker (Recommended for Production)

  • Start Both Applications: docker-compose up --build
  • Start in Detached Mode: docker-compose up -d --build
  • View Logs: docker-compose logs -f
  • Stop Applications: docker-compose down

Using Makefile:

make docker-up              # Start with logs
make docker-up-detached     # Start in background
make docker-down            # Stop containers
make docker-logs            # View logs
Enter fullscreen mode Exit fullscreen mode

Access Applications:

Option 2: Local Development

Using Makefile:

make install    # Install dependencies
make dev        # Instructions for running both apps
Enter fullscreen mode Exit fullscreen mode

Manual:

# Terminal 1
cd remote
npm run dev

# Terminal 2
cd host
npm run dev
Enter fullscreen mode Exit fullscreen mode

Option 3: Production Build

Build Both Applications:

make build

Or manually:

# Build remote app first
cd remote
npm run build

# Build host app
cd ../host
npm run build
Enter fullscreen mode Exit fullscreen mode

Preview Production Build:

# Terminal 1
cd remote
npm run preview

# Terminal 2
cd host
npm run preview---
Enter fullscreen mode Exit fullscreen mode

Best Practices Implemented

1. Component Architecture

  • Clean separation of concerns
  • Focused, reusable components
  • Clear component hierarchy

2. Error Handling

  • Error boundaries for remote components
  • Fallback UI for failed loads
  • User-friendly error messages

3. Performance Optimization

  • Lazy loading with React Suspense
  • Code splitting per micro frontend
  • Shared dependencies to reduce bundle size
  • Nginx caching strategies

4. Type Safety

  • Shared TypeScript types across applications
  • Type-safe component props
  • Compile-time error checking

5. Development Experience

  • Hot Module Replacement (HMR) in development
  • Clear project structure
  • Environment variable configuration
  • Makefile for common tasks

6. Production Readiness

  • Multi-stage Docker builds
  • Health checks for orchestration
  • Security headers in Nginx
  • Optimized build configurations
  • CORS properly configured

7. Code Organization

  • Shared types in both applications
  • Consistent naming conventions
  • Clear file structure
  • Separation of concerns

Conclusion

This project demonstrates a production-ready micro frontends architecture using React, Vite, and Module Federation.

When to Use Micro Frontends

Good Fit:

  • Large applications with multiple teams
  • Need for independent deployments
  • Different parts require different technologies
  • Gradual migration from legacy systems

Not Ideal For:

  • Small applications with a single team
  • Tightly coupled features
  • Limited DevOps resources
  • Simple applications without scaling needs

Next Steps

  • Add more micro frontends (e.g., user profile, checkout)
  • Implement state management (Redux, Zustand)
  • Add routing (React Router)
  • Set up CI/CD pipelines
  • Add monitoring and logging
  • Implement authentication/authorization

Resources


Built with ❀️ for learning Micro Frontends Architecture

This architecture provides a solid foundation for building scalable, maintainable frontend applications. By understanding these concepts and patterns, you can apply them to your own projects and teams.

Top comments (0)