Skip to content

Dimuthu7/Micro-frontend-with-React-Vite

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

8 Commits
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

🚀 Micro Frontends with React, TypeScript, Vite, Tailwind CSS & Module Federation

A production-ready Micro Frontends architecture demonstration using modern web technologies. This project showcases a real-world Product Catalog application where the host app dynamically loads product detail components from a remote micro frontend.

📋 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

Component Architecture

The host application follows a component-based architecture with clear separation of concerns:

  • App.tsx: Main orchestrator managing state and routing between views
  • ProductListView: Displays the product catalog with header information
  • ProductDetailsView: Container for remote product details component with error handling
  • ProductList: Reusable product grid component
  • Header: Application header with shopping cart
  • Footer: Application footer
  • BackButton: Reusable navigation component
  • ErrorBoundary: Error handling wrapper for remote components

🎯 Features

  • TypeScript - Full type safety across host and remote apps
  • Tailwind CSS - Modern, utility-first CSS framework
  • Module Federation - Runtime code sharing and dynamic loading
  • Docker Support - Containerized applications with Docker Compose
  • Error Boundaries - Graceful error handling
  • Lazy Loading - Optimized performance with React Suspense
  • Production Ready - Nginx configuration, health checks, and best practices
  • Real-world App - Product Catalog with shopping cart functionality

🛠️ Tech Stack

  • React 18 - UI library with hooks and Suspense
  • TypeScript 5 - Type-safe development
  • Vite 5 - Fast build tool and dev server
  • Tailwind CSS 3 - Utility-first CSS framework
  • Module Federation - Runtime module sharing
  • Docker & Docker Compose - Containerization
  • Nginx - Production web server

🚀 Quick Start

Option 1: Docker (Recommended)

The easiest way to run the entire application:

# Build and start both applications
docker-compose up --build

# Or run in detached mode
docker-compose up -d --build

# View logs
docker-compose logs -f

# Stop applications
docker-compose down

Access the applications:

Option 2: Local Development

Prerequisites

  • Node.js 18+ and npm/yarn/pnpm

Installation

  1. Install dependencies for the remote app:

    cd remote
    npm install
  2. Install dependencies for the host app:

    cd ../host
    npm install

Running the Applications

You need to run both applications simultaneously. Open two terminal windows:

Terminal 1 - Remote App (must start first):

cd remote
npm run dev

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

Terminal 2 - Host App:

cd host
npm run dev

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

View the Application

Open your browser and navigate to http://localhost:5173. You should see:

  • A product catalog with multiple products
  • Click on any product to view detailed information loaded from the remote micro frontend
  • Shopping cart functionality

🏗️ Architecture

Why Micro Frontends Architecture?

Micro Frontends architecture is crucial for modern web applications because it addresses several key challenges:

🎯 Key Benefits

  1. Team Autonomy & Scalability

    • Different teams can work independently on separate micro frontends
    • Teams can choose their own tech stack and deployment cycles
    • Reduces coordination overhead in large organizations
    • Enables parallel development and faster feature delivery
  2. Independent Deployment

    • Deploy micro frontends independently without affecting other parts
    • Rollback individual features without impacting the entire application
    • Faster release cycles and reduced deployment risk
    • A/B testing and gradual rollouts become easier
  3. Technology Diversity

    • Different micro frontends can use different frameworks (React, Vue, Angular)
    • Teams can upgrade dependencies independently
    • Experiment with new technologies without rewriting the entire app
    • Gradual migration path for legacy applications
  4. Code Isolation & Maintainability

    • Smaller, focused codebases are easier to understand and maintain
    • Clear boundaries prevent tight coupling
    • Easier to onboard new developers
    • Better code organization and separation of concerns
  5. Performance Optimization

    • Load only what's needed, when it's needed (code splitting)
    • Independent caching strategies per micro frontend
    • Smaller bundle sizes per application
    • Better performance through lazy loading
  6. Fault Isolation

    • Errors in one micro frontend don't crash the entire application
    • Error boundaries can gracefully handle failures
    • Better resilience and user experience

Host Application

The host application (host/) is the container/orchestrator that:

  • Displays the product catalog using local components
  • Manages application state (selected product, cart count)
  • Dynamically loads the ProductCard component from the remote app at runtime
  • Handles routing and navigation between views
  • Provides error boundaries for graceful failure handling
  • Uses a clean component architecture for maintainability

Component Structure:

App.tsx (Orchestrator)
├── Header (Cart display)
├── ProductListView (Product catalog)
│   └── ProductList (Product grid)
└── ProductDetailsView (Remote component container)
    └── RemoteProductCard (Loaded from remote app)

Remote Application

The remote application (remote/) is a standalone micro frontend that:

  • Exposes the ProductCard component via Module Federation
  • Can run independently for development and testing
  • Shares React and React-DOM with the host to avoid duplication
  • Maintains its own build and deployment pipeline
  • Provides detailed product information UI

How Applications Connect

Module Federation Connection Flow

┌─────────────────────────────────────────────────────────────┐
│                    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                   │
└─────────────────────────────────────────────────────────────┘

Technical Connection Process

  1. Build Time:

    • Remote app builds and generates remoteEntry.js (Module Federation manifest)
    • Host app is configured to consume from remote URL
    • Shared dependencies (React, React-DOM) are configured
  2. Runtime Connection:

    • When host app needs the remote component, it makes an HTTP request to the remote app
    • Remote app serves remoteEntry.js which contains module metadata
    • Module Federation runtime loads the component code dynamically
    • Component executes in the host app's context with shared dependencies
  3. Communication:

    • Components communicate via props (passed from host to remote)
    • Callbacks allow remote components to communicate back to host
    • Shared state can be managed through context or state management libraries

Why Nginx is Needed

Nginx serves multiple critical purposes in this architecture:

1. Production Web Server

  • Serves static files (HTML, CSS, JS) efficiently
  • Handles HTTP requests and routing
  • More performant than development servers (Vite dev server)

2. Module Federation Support

  • CORS Headers: Remote app must allow cross-origin requests from host app
    add_header 'Access-Control-Allow-Origin' '*';
    add_header 'Access-Control-Allow-Methods' 'GET, OPTIONS';
  • Proper MIME Types: Ensures JavaScript modules are served with correct content-type
  • Caching Strategy: Optimizes loading of remoteEntry.js and component chunks

3. SPA Routing

  • Handles client-side routing for React applications
  • Serves index.html for all routes (fallback)
  • Prevents 404 errors on direct URL access or page refresh

4. Performance Optimization

  • Gzip Compression: Reduces bundle sizes for faster loading
  • Static File Caching: Caches assets with appropriate headers
  • HTTP/2 Support: Enables multiplexing for better performance

5. Security

  • Security headers (X-Frame-Options, X-Content-Type-Options, etc.)
  • Protection against common web vulnerabilities
  • SSL/TLS termination in production

6. Development vs Production

  • Development: Vite dev server handles hot module replacement
  • Production: Nginx serves optimized, minified bundles
  • Docker containers use Nginx for production-ready deployment

Internal System Flow

Complete Request Flow

┌─────────────────────────────────────────────────────────────────┐
│                    User Interaction Flow                        │
└─────────────────────────────────────────────────────────────────┘

1. INITIAL PAGE LOAD
   ┌─────────────┐
   │   Browser   │
   └──────┬──────┘
          │ HTTP GET /
          ▼
   ┌─────────────────┐
   │  Nginx (Host)   │  Serves index.html + host app bundle
   └──────┬──────────┘
          │
          ▼
   ┌─────────────────┐
   │   Host App      │  React app initializes
   │   (App.tsx)     │  - Loads ProductListView
   │                 │  - Renders product catalog
   └─────────────────┘

2. USER CLICKS PRODUCT
   ┌─────────────────┐
   │   Host App      │  setSelectedProduct(product)
   │   (App.tsx)     │  - Switches to ProductDetailsView
   └──────┬──────────┘
          │
          ▼
   ┌──────────────────┐
   │ProductDetailsView│  Lazy loads remote component
   │  (Suspense)      │  - Triggers Module Federation
   └──────┬───────────┘
          │
          │ HTTP Request: GET /remoteEntry.js
          ▼
   ┌─────────────────┐
   │  Nginx (Remote) │  Serves Module Federation manifest
   └──────┬──────────┘
          │
          │ HTTP Request: GET /assets/ProductCard.[hash].js
          ▼
   ┌─────────────────┐
   │  Nginx (Remote) │  Serves component bundle
   └──────┬──────────┘
          │
          ▼
   ┌──────────────────┐
   │ Module Federation│  Loads & executes component
   │     Runtime      │  - Shares React instance
   │                  │  - Renders in host context
   └──────┬───────────┘
          │
          ▼
   ┌─────────────────┐
   │RemoteProductCard│  Component renders with props
   │  (Remote App)   │  - Displays product details
   │                 │  - onAddToCart callback available
   └─────────────────┘

3. USER ADDS TO CART
   ┌─────────────────┐
   │RemoteProductCard│  Calls onAddToCart()
   └──────┬──────────┘
          │
          ▼
   ┌─────────────────┐
   │   Host App      │  handleAddToCart()
   │   (App.tsx)     │  - Updates cartCount state
   └──────┬──────────┘
          │
          ▼
   ┌─────────────────┐
   │     Header      │  Re-renders with new count
   └─────────────────┘

Module Federation Configuration

Host (host/vite.config.ts):

// Consumes remote app
remotes: {
  remote_app: 'http://localhost:5174/assets/remoteEntry.js'
},
// Shares dependencies to avoid duplication
shared: {
  react: { singleton: true },
  'react-dom': { singleton: true }
}

Remote (remote/vite.config.ts):

// Exposes component
exposes: {
  './ProductCard': './src/components/ProductCard'
},
// Shares dependencies
shared: {
  react: { singleton: true },
  'react-dom': { singleton: true }
}

Data Flow

┌──────────────┐         Props          ┌──────────────┐
│   Host App   │───────────────────────▶│ Remote App   │
│              │                        │              │
│  - State     │◀───────────────────────│  - Component │
│  - Handlers  │      Callbacks         │  - UI        │
└──────────────┘                        └──────────────┘
     │                                          │
     │  Shared React Instance                   │
     └──────────────────────────────────────────┘

Docker Network Flow

┌─────────────────────────────────────────────────────────┐
│         Docker Network (microfrontends-network)         │
│                                                         │
│  ┌──────────────┐               ┌──────────────┐        │
│  │  host-app    │               │ remote-app   │        │
│  │  :5173       │──────────────▶│ :5174        │        │
│  │  (Nginx)     │  HTTP Request │ (Nginx)      │        │
│  │              │◀──────────────│              │        │
│  │              │  remoteEntry  │              │        │
│  └──────────────┘               └──────────────┘        │
│                                                         │
│  Both containers can communicate via service names      │
│  Host app uses: http://remote-app/assets/remoteEntry.js │
└─────────────────────────────────────────────────────────┘

📦 Building for Production

Using Docker

# Build production images
docker-compose build

# Start production containers
docker-compose up -d

Manual Build

Build Remote App First:

cd remote
npm run build

Build Host App:

cd host
npm run build

Note: In production, update the remote URL in host/vite.config.ts to point to your production remote app URL.

🐳 Docker Details

Docker Compose

The docker-compose.yml file orchestrates both applications:

  • remote-app: Runs on port 5174
  • host-app: Runs on port 5173, depends on remote-app
  • Health checks ensure proper startup order
  • Shared network for inter-container communication

Dockerfiles

Both applications use multi-stage builds:

  1. Builder stage: Installs dependencies and builds the application
  2. Production stage: Serves the built application with Nginx

Nginx Configuration

  • Optimized for SPA routing
  • Gzip compression enabled
  • Security headers configured
  • CORS headers for Module Federation (remote app)
  • Proper caching strategies

🎓 Key Concepts

Module Federation

Module Federation allows JavaScript applications to:

  • Dynamically load code from other applications at runtime
  • Share dependencies to avoid duplication
  • Enable independent deployment of micro frontends
  • Maintain team autonomy with separate codebases

Type Safety

TypeScript ensures type safety across applications:

  • Shared type definitions in both apps
  • Type-safe component props
  • Compile-time error checking

Best Practices Implemented

  1. Component Architecture - Clean separation of concerns with focused, reusable components
  2. Error Boundaries - Graceful error handling for remote components with fallback UI
  3. Lazy Loading - Performance optimization with React Suspense for code splitting
  4. Shared Dependencies - React and React-DOM shared as singletons to avoid duplication
  5. Environment Variables - Configurable remote URLs for different environments
  6. Health Checks - Docker health checks for reliable orchestration and startup order
  7. Security Headers - Nginx security headers configured for production security
  8. Code Splitting - Optimized bundle sizes with dynamic imports
  9. Type Safety - Shared TypeScript types ensure consistency across apps
  10. Containerization - Docker ensures consistent environments across development and production

🔧 Configuration

Environment Variables

Create .env files based on .env.example:

Host App:

PORT=5173
VITE_REMOTE_HOST=localhost
VITE_REMOTE_PORT=5174

Remote App:

PORT=5174

Docker Environment

For Docker, the remote host should be the service name:

VITE_REMOTE_HOST=remote-app
VITE_REMOTE_PORT=80

🧪 Development

Linting

# Host app
cd host
npm run lint

# Remote app
cd remote
npm run lint

Type Checking

TypeScript type checking is integrated into the build process:

npm run build

🐛 Troubleshooting

Remote component not loading

  1. Ensure the remote app is running before starting the host app
  2. Check browser console for CORS or network errors
  3. Verify the remote URL in host/vite.config.ts
  4. Check that remoteEntry.js is accessible

Docker issues

  1. Port conflicts: Ensure ports 5173 and 5174 are available
  2. Build failures: Check Docker logs with docker-compose logs
  3. Network issues: Verify both containers are on the same network

CORS errors

  • The remote app includes CORS headers in its Nginx configuration
  • For local development, ensure both apps are running
  • For Docker, the apps communicate via the internal network

📚 Learning Resources

🤝 Contributing

This is a learning project. Feel free to:

  • Experiment with different architectures
  • Add more micro frontends
  • Implement additional features
  • Share your improvements

📄 License

MIT


Built with ❤️ for learning Micro Frontends Architecture

About

A production-ready Micro Frontends architecture demonstration using modern web technologies. This project showcases a real-world Product Catalog application where the host app dynamically loads product detail components from a remote micro frontend.

Topics

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors