Skip to main content

TypeScript Modern.js MFE App Static

A sophisticated micro frontend application archetype built with Modern.js framework, featuring Module Federation for seamless integration with shell hosts, optimized Rspack bundling, and production-ready containerization.

Overview

This archetype generates a complete micro frontend application using Modern.js with TypeScript, designed to be consumed by MFE shell hosts through Module Federation. It provides a self-contained, deployable micro frontend that can be developed, tested, and deployed independently while seamlessly integrating into larger applications.

Architecture Diagram

Technology Stack

  • Modern.js: React-based framework with Rspack bundling
  • TypeScript 5+: Strong typing and modern JavaScript features
  • Module Federation: Webpack's micro frontend solution
  • Rspack: Ultra-fast Rust-based bundler
  • React 18: Modern React with concurrent features
  • Tailwind CSS: Utility-first CSS framework
  • Vitest: Unit testing framework
  • Playwright: End-to-end testing
  • Docker: Containerization for deployment
  • Kubernetes: Container orchestration support

Key Features

Micro Frontend Architecture

  • Module Federation: Exposes components and routes as federated modules
  • Independent Deployment: Self-contained application with own CI/CD
  • Runtime Integration: Dynamic loading into shell applications
  • Isolated Development: Independent development and testing
  • Version Management: Semantic versioning for federated modules

Modern.js Features

  • File-based Routing: Automatic route generation from file structure
  • Server-Side Rendering: Optional SSR for better SEO
  • Code Splitting: Automatic code splitting with Rspack
  • Hot Module Replacement: Fast development experience
  • Built-in Testing: Integrated testing utilities

Production Features

  • Docker Optimization: Multi-stage builds for production
  • Kubernetes Ready: Helm charts and deployment manifests
  • CDN Integration: Static asset optimization
  • Performance Monitoring: Built-in performance metrics
  • Health Checks: Application health monitoring

Project Structure

{{ project-name }}/
├── src/
│ ├── App.tsx # Root application component
│ ├── components/
│ │ └── {{ project-name }}-content.tsx # Main content component
│ ├── modern-app-env.d.ts # Modern.js environment types
│ ├── modern.runtime.ts # Runtime configuration
│ ├── pages/
│ │ ├── index.css # Page styles
│ │ ├── layout.tsx # Root layout
│ │ └── page.tsx # Home page
│ ├── router.tsx # Router configuration
│ └── routes/ # Additional routes (if needed)
├── public/
│ └── health/
│ └── index.html # Health check endpoint
├── biome.json # Code quality and formatting
├── Dockerfile # Container configuration
├── modern.config.ts # Modern.js configuration
├── module-federation.config.ts # Module Federation setup
├── nginx.conf # Production server config
├── package.json # Dependencies and scripts
├── pnpm-lock.yaml # Lock file
├── postcss.config.js # PostCSS configuration
├── README.md # Project documentation
├── tailwind.config.js # Tailwind CSS configuration
└── tsconfig.json # TypeScript configuration

Module Federation Configuration

Federation Configuration

// module-federation.config.ts
import { ModuleFederationPlugin } from '@module-federation/enhanced'

export default {
name: '{{ project_name }}',
exposes: {
'./App': './src/App',
'./Content': './src/components/{{ project-name }}-content'
},
shared: {
react: {
singleton: true,
requiredVersion: '^18.0.0'
},
'react-dom': {
singleton: true,
requiredVersion: '^18.0.0'
},
'@modern-js/runtime': {
singleton: true
}
}
}

Main Application Component

// src/App.tsx
import React from 'react'
import { BrowserRouter, Routes, Route } from 'react-router-dom'
import { HomePage } from './pages/page'
import { Layout } from './pages/layout'

const App: React.FC = () => {
return (
<BrowserRouter>
<Layout>
<Routes>
<Route path="/" element={<HomePage />} />
{/* Add more routes as needed */}
</Routes>
</Layout>
</BrowserRouter>
)
}

export default App

Content Component

// src/components/{{ project-name }}-content.tsx
import React from 'react'

interface ContentProps {
title?: string
children?: React.ReactNode
}

export const {{ ProjectName }}Content: React.FC<ContentProps> = ({
title = "{{ project_name }}",
children
}) => {
return (
<div className="container mx-auto px-4 py-8">
<h1 className="text-3xl font-bold text-gray-900 mb-6">{title}</h1>
<div className="bg-white rounded-lg shadow-sm p-6">
{children || (
<p className="text-gray-600">
Welcome to your {{ project_name }} micro frontend application!
</p>
)}
</div>
</div>
)
}

Development Workflow

Modern.js Configuration

Modern.js Config

// modern.config.ts
import { defineConfig } from '@modern-js/app-tools'
import { moduleFederationPlugin } from '@module-federation/modern-js'

export default defineConfig({
runtime: {
router: true,
state: true
},

server: {
port: 3001,
host: '0.0.0.0'
},

dev: {
port: 3001,
host: '0.0.0.0'
},

html: {
title: 'Customer Management MFE',
meta: {
description: 'Customer management micro frontend application'
}
},

output: {
distPath: {
root: 'dist'
},
assetPrefix: process.env.NODE_ENV === 'production'
? 'https://cdn.example.com/customer-app/'
: 'http://localhost:3001/'
},

tools: {
rspack: {
optimization: {
splitChunks: {
chunks: 'all',
cacheGroups: {
vendor: {
test: /[\\/]node_modules[\\/]/,
name: 'vendors',
chunks: 'all'
}
}
}
}
},

tailwindcss: {
config: './tailwind.config.js'
}
},

plugins: [
moduleFederationPlugin({
name: 'customer_app',
exposes: {
'./CustomerModule': './src/components/federated/CustomerModule',
'./CustomerRoutes': './src/routes/customers',
'./CustomerService': './src/services/customerService'
},
shared: {
react: { singleton: true },
'react-dom': { singleton: true },
'@modern-js/runtime': { singleton: true }
}
})
],

testing: {
transformer: 'ts-jest'
}
})

Testing Strategy

Component Testing

// tests/unit/CustomerCard.test.tsx
import { render, screen, fireEvent } from '@testing-library/react'
import { CustomerCard } from '@/components/customer/CustomerCard'
import type { Customer } from '@/types/customer'

const mockCustomer: Customer = {
id: '1',
firstName: 'John',
lastName: 'Doe',
email: 'john.doe@example.com',
company: 'Acme Corp',
status: 'active',
segment: 'enterprise',
totalOrders: 15,
lifetimeValue: 5000,
createdAt: '2023-01-01T00:00:00Z',
updatedAt: '2023-01-01T00:00:00Z'
}

describe('CustomerCard', () => {
it('renders customer information correctly', () => {
render(<CustomerCard customer={mockCustomer} />)

expect(screen.getByText('John Doe')).toBeInTheDocument()
expect(screen.getByText('john.doe@example.com')).toBeInTheDocument()
expect(screen.getByText('Acme Corp')).toBeInTheDocument()
expect(screen.getByText('active')).toBeInTheDocument()
expect(screen.getByText('enterprise')).toBeInTheDocument()
expect(screen.getByText('15')).toBeInTheDocument()
expect(screen.getByText('$5,000')).toBeInTheDocument()
})

it('calls onSelect when card is clicked', () => {
const onSelect = jest.fn()
render(<CustomerCard customer={mockCustomer} onSelect={onSelect} />)

fireEvent.click(screen.getByText('John Doe'))
expect(onSelect).toHaveBeenCalledWith(mockCustomer)
})

it('calls onEdit when edit button is clicked', () => {
const onEdit = jest.fn()
render(<CustomerCard customer={mockCustomer} onEdit={onEdit} />)

fireEvent.click(screen.getByText('Edit'))
expect(onEdit).toHaveBeenCalledWith(mockCustomer)
})

it('hides actions when showActions is false', () => {
render(<CustomerCard customer={mockCustomer} showActions={false} />)

expect(screen.queryByText('Edit')).not.toBeInTheDocument()
expect(screen.queryByText('Delete')).not.toBeInTheDocument()
})
})

Federation Testing

// tests/unit/federation/CustomerModule.test.tsx
import { render, screen } from '@testing-library/react'
import CustomerModule from '@/components/federated/CustomerModule'
import type { CustomerModuleProps } from '@/types/federation'

// Mock the customer service
jest.mock('@/services/customerService')

const defaultProps: CustomerModuleProps = {
basePath: '/customers',
apiBaseUrl: 'http://localhost:3000/api',
permissions: { read: true, write: true, delete: false }
}

describe('CustomerModule Federation', () => {
it('renders without crashing', () => {
render(<CustomerModule {...defaultProps} />)
expect(screen.getByTestId('customer-module')).toBeInTheDocument()
})

it('applies theme correctly', () => {
const theme = { name: 'dark', className: 'dark-theme' }
render(<CustomerModule {...defaultProps} theme={theme} />)

const module = screen.getByTestId('customer-module')
expect(module).toHaveClass('dark-theme')
expect(module).toHaveAttribute('data-theme', 'dark')
})

it('respects permission settings', () => {
const restrictedPermissions = { read: true, write: false, delete: false }
render(<CustomerModule {...defaultProps} permissions={restrictedPermissions} />)

// Should not show create/edit buttons with restricted permissions
expect(screen.queryByText('Add Customer')).not.toBeInTheDocument()
})
})

Deployment

Docker Configuration

# Multi-stage build for Modern.js MFE app
FROM node:18-alpine AS base
WORKDIR /app
COPY package*.json ./
RUN npm ci --only=production

FROM node:18-alpine AS build
WORKDIR /app
COPY package*.json ./
RUN npm ci
COPY . .
RUN npm run build

FROM nginx:alpine AS production
COPY --from=build /app/dist /usr/share/nginx/html
COPY docker/nginx.conf /etc/nginx/nginx.conf

# Add health check
HEALTHCHECK --interval=30s --timeout=3s --start-period=5s --retries=3 \
CMD wget --no-verbose --tries=1 --spider http://localhost/ || exit 1

EXPOSE 80
CMD ["nginx", "-g", "daemon off;"]

Kubernetes Deployment

apiVersion: apps/v1
kind: Deployment
metadata:
name: customer-mfe-app
labels:
app: customer-mfe-app
spec:
replicas: 3
selector:
matchLabels:
app: customer-mfe-app
template:
metadata:
labels:
app: customer-mfe-app
spec:
containers:
- name: customer-mfe-app
image: your-registry/customer-mfe-app:latest
ports:
- containerPort: 80
env:
- name: NODE_ENV
value: "production"
resources:
requests:
cpu: 100m
memory: 128Mi
limits:
cpu: 500m
memory: 512Mi
livenessProbe:
httpGet:
path: /
port: 80
initialDelaySeconds: 30
periodSeconds: 10
readinessProbe:
httpGet:
path: /
port: 80
initialDelaySeconds: 5
periodSeconds: 5
---
apiVersion: v1
kind: Service
metadata:
name: customer-mfe-app-service
spec:
selector:
app: customer-mfe-app
ports:
- port: 80
targetPort: 80
type: ClusterIP

Quick Start

  1. Generate the application:

    archetect render git@github.com:p6m-archetypes/typescript-modernjs-mfe-app-static.archetype.git
  2. Install dependencies:

    npm install
  3. Start development server:

    npm run dev
  4. Access the application:

    http://localhost:3001
  5. Run complete MFE demo:

    ./scripts/create_mfe_demo.sh
  6. Run tests:

    npm run test
    npm run test:e2e
  7. Build for production:

    npm run build

Best Practices

Module Federation

  • Keep shared dependencies minimal and well-versioned
  • Design federated components to be self-contained
  • Implement proper error boundaries for federation failures
  • Use semantic versioning for exposed modules

Performance

  • Optimize bundle sizes for faster loading
  • Implement proper lazy loading strategies
  • Use CDN for static assets
  • Monitor federation loading performance

Development

  • Maintain independent development environments
  • Use proper TypeScript types for federation contracts
  • Implement comprehensive testing for federated components
  • Document federation APIs clearly

Deployment

  • Use independent CI/CD pipelines
  • Implement proper rollback strategies
  • Monitor federated module health
  • Use container orchestration for scalability