Skip to content

Instantly share code, notes, and snippets.

@mzbac
Last active August 18, 2025 07:42
Show Gist options
  • Save mzbac/e0526c49f4279f9bd06305cf7480be11 to your computer and use it in GitHub Desktop.
Save mzbac/e0526c49f4279f9bd06305cf7480be11 to your computer and use it in GitHub Desktop.
mfe demo
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Micro Frontend Demo</title>
<style>
body {
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
margin: 0;
padding: 20px;
background: #f5f5f5;
}
.container {
max-width: 1200px;
margin: 0 auto;
background: white;
border-radius: 12px;
box-shadow: 0 2px 8px rgba(0,0,0,0.1);
overflow: hidden;
}
header {
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
color: white;
padding: 20px;
text-align: center;
}
h1 {
margin: 0;
font-size: 24px;
}
.subtitle {
opacity: 0.9;
font-size: 14px;
margin-top: 5px;
}
nav {
display: flex;
gap: 10px;
padding: 20px;
background: #fafafa;
border-bottom: 1px solid #e0e0e0;
}
button {
padding: 10px 20px;
border: none;
border-radius: 6px;
background: #667eea;
color: white;
cursor: pointer;
font-size: 14px;
transition: all 0.3s ease;
}
button:hover {
background: #5a67d8;
transform: translateY(-1px);
box-shadow: 0 4px 12px rgba(102, 126, 234, 0.3);
}
button:active {
transform: translateY(0);
}
button.active {
background: #764ba2;
}
#mfe-container {
padding: 20px;
min-height: 300px;
}
.loading {
text-align: center;
padding: 40px;
color: #666;
}
.error {
background: #fee;
color: #c00;
padding: 20px;
border-radius: 6px;
margin: 20px;
}
/* Styles for MFEs */
.mfe-wrapper {
padding: 20px;
background: #f9f9f9;
border-radius: 8px;
animation: fadeIn 0.3s ease;
}
@keyframes fadeIn {
from { opacity: 0; transform: translateY(10px); }
to { opacity: 1; transform: translateY(0); }
}
.dashboard-widget {
background: white;
padding: 20px;
border-radius: 8px;
margin-bottom: 15px;
box-shadow: 0 2px 4px rgba(0,0,0,0.05);
}
.profile-card {
background: white;
padding: 30px;
border-radius: 8px;
text-align: center;
box-shadow: 0 2px 4px rgba(0,0,0,0.05);
}
.avatar {
width: 100px;
height: 100px;
border-radius: 50%;
background: linear-gradient(135deg, #667eea, #764ba2);
margin: 0 auto 20px;
display: flex;
align-items: center;
justify-content: center;
color: white;
font-size: 36px;
}
.settings-form {
background: white;
padding: 20px;
border-radius: 8px;
box-shadow: 0 2px 4px rgba(0,0,0,0.05);
}
.form-group {
margin-bottom: 15px;
}
label {
display: block;
margin-bottom: 5px;
font-weight: 500;
color: #333;
}
input, select {
width: 100%;
padding: 8px 12px;
border: 1px solid #ddd;
border-radius: 4px;
font-size: 14px;
}
</style>
</head>
<body>
<div class="container">
<header>
<h1>Micro Frontend Container App</h1>
<div class="subtitle">Dynamic Import Demo with Mount Functions</div>
</header>
<nav>
<button onclick="loadMFE('dashboard')" id="btn-dashboard">Dashboard MFE (Has Error)</button>
<button onclick="loadMFE('profile')" id="btn-profile">Profile MFE</button>
<button onclick="loadMFE('settings')" id="btn-settings">Settings MFE (Random Error)</button>
<button onclick="loadMFE('broken')" id="btn-broken">Broken MFE</button>
<button onclick="unmountCurrent()">Unmount Current</button>
</nav>
<div id="mfe-container">
<div class="loading">Select a micro frontend to load</div>
</div>
</div>
<script>
// Container App Code
let currentMFE = null;
let currentUnmount = null;
// Simulated MFE modules (in real app, these would be separate files/bundles)
const mfeModules = {
dashboard: {
mount: (element) => {
// SIMULATE ERROR: This MFE has a bug!
try {
// Intentional error to demonstrate error handling
const someData = null;
const result = someData.property; // This will throw!
element.innerHTML = `
<div class="mfe-wrapper">
<h2>๐Ÿ“Š Dashboard Micro Frontend</h2>
<div class="dashboard-widget">
<h3>Sales Overview</h3>
<p>Monthly Revenue: $45,230</p>
<p>Growth: +12.5%</p>
</div>
</div>
`;
} catch (error) {
// MFE internal error - re-throw to be caught by container
throw new Error(`Dashboard MFE Error: ${error.message}`);
}
return () => {
console.log('Dashboard MFE unmounted');
element.innerHTML = '';
};
}
},
profile: {
mount: (element) => {
element.innerHTML = `
<div class="mfe-wrapper">
<h2>๐Ÿ‘ค Profile Micro Frontend</h2>
<div class="profile-card">
<div class="avatar">JD</div>
<h3>John Doe</h3>
<p>[email protected]</p>
<p>Member since: January 2024</p>
<br>
<button onclick="alert('Profile MFE: Edit clicked!')">Edit Profile</button>
</div>
</div>
`;
console.log('Profile MFE mounted');
return () => {
console.log('Profile MFE unmounted');
element.innerHTML = '';
};
}
},
settings: {
mount: (element) => {
// Simulate async operation that might fail
setTimeout(() => {
// Random error simulation (20% chance)
if (Math.random() < 0.2) {
const error = new Error('Failed to load user settings from API');
console.error('Settings MFE Error:', error);
element.innerHTML = `
<div class="mfe-wrapper">
<h2>โš™๏ธ Settings Micro Frontend</h2>
<div class="error">
<strong>Settings Error:</strong> Failed to load user settings from API
<br><br>
<button onclick="loadMFE('settings')">Retry</button>
</div>
</div>
`;
return;
}
// Normal rendering
element.innerHTML = `
<div class="mfe-wrapper">
<h2>โš™๏ธ Settings Micro Frontend</h2>
<div class="settings-form">
<div class="form-group">
<label>Language</label>
<select>
<option>English</option>
<option>Spanish</option>
<option>French</option>
</select>
</div>
<div class="form-group">
<label>Theme</label>
<select>
<option>Light</option>
<option>Dark</option>
<option>Auto</option>
</select>
</div>
<div class="form-group">
<label>Notifications</label>
<input type="checkbox" id="notifications" checked>
<label for="notifications" style="display: inline; margin-left: 8px;">
Enable email notifications
</label>
</div>
<br>
<button onclick="alert('Settings MFE: Settings saved!')">Save Settings</button>
</div>
</div>
`;
}, 500);
// Initial loading state
element.innerHTML = `
<div class="mfe-wrapper">
<h2>โš™๏ธ Settings Micro Frontend</h2>
<div class="loading">Loading settings...</div>
</div>
`;
console.log('Settings MFE mounted');
return () => {
console.log('Settings MFE unmounted');
element.innerHTML = '';
};
}
},
// Broken MFE - simulates a completely failed module
broken: {
mount: undefined // This MFE is completely broken!
}
};
// Dynamic import simulation (in real app, use actual dynamic imports)
async function importMFE(name) {
// Simulate network delay
await new Promise(resolve => setTimeout(resolve, 300));
// In real implementation, this would be:
// const module = await import(`./mfe/${name}/index.js`);
// return module;
if (!mfeModules[name]) {
throw new Error(`MFE "${name}" not found`);
}
return mfeModules[name];
}
async function loadMFE(name) {
const container = document.getElementById('mfe-container');
// Update active button
document.querySelectorAll('nav button').forEach(btn => {
btn.classList.remove('active');
});
document.getElementById(`btn-${name}`)?.classList.add('active');
try {
// Show loading state
container.innerHTML = '<div class="loading">Loading micro frontend...</div>';
// Unmount current MFE if exists
if (currentUnmount) {
try {
currentUnmount();
} catch (unmountError) {
console.error('Error during unmount:', unmountError);
// Continue anyway - don't let unmount errors prevent loading new MFE
}
currentUnmount = null;
}
// Dynamically import the MFE module
const mfeModule = await importMFE(name);
// Validate the module has required mount function
if (!mfeModule || typeof mfeModule.mount !== 'function') {
throw new Error(`Invalid MFE module: missing mount function`);
}
// Clear container
container.innerHTML = '';
// Create an error boundary wrapper
const mfeWrapper = document.createElement('div');
mfeWrapper.id = `mfe-${name}-wrapper`;
container.appendChild(mfeWrapper);
try {
// Mount the MFE with error handling
currentUnmount = mfeModule.mount(mfeWrapper);
currentMFE = name;
console.log(`Container: Successfully loaded and mounted "${name}" MFE`);
} catch (mountError) {
console.error(`Error mounting ${name} MFE:`, mountError);
// Show error UI but keep the container app running
container.innerHTML = `
<div class="error">
<h3>โš ๏ธ Micro Frontend Error</h3>
<p><strong>${name}</strong> MFE failed to mount</p>
<p>Error: ${mountError.message}</p>
<br>
<button onclick="loadMFE('${name}')">๐Ÿ”„ Retry</button>
<button onclick="loadMFE('profile')">Go to Profile</button>
</div>
`;
// Log to monitoring service in production
logError({
mfe: name,
error: mountError.message,
stack: mountError.stack,
timestamp: new Date().toISOString()
});
}
} catch (error) {
console.error('Failed to load MFE:', error);
// Show user-friendly error with recovery options
container.innerHTML = `
<div class="error">
<h3>โš ๏ธ Failed to Load Micro Frontend</h3>
<p>The <strong>${name}</strong> module could not be loaded.</p>
<p><small>Error: ${error.message}</small></p>
<br>
<div style="display: flex; gap: 10px;">
<button onclick="loadMFE('${name}')">๐Ÿ”„ Try Again</button>
<button onclick="loadMFE('profile')">Load Profile Instead</button>
<button onclick="reportIssue('${name}', '${error.message}')">๐Ÿ“ง Report Issue</button>
</div>
</div>
`;
// Remove active state from failed MFE button
document.getElementById(`btn-${name}`)?.classList.remove('active');
}
}
// Error logging function (would send to monitoring service)
function logError(errorData) {
console.group('๐Ÿšจ MFE Error Report');
console.error('MFE:', errorData.mfe);
console.error('Message:', errorData.error);
console.error('Time:', errorData.timestamp);
console.error('Stack:', errorData.stack);
console.groupEnd();
// In production, send to error tracking service:
// fetch('/api/errors', {
// method: 'POST',
// body: JSON.stringify(errorData)
// });
}
// Mock function to report issues
function reportIssue(mfeName, errorMessage) {
alert(`Issue reported for ${mfeName} MFE:\n${errorMessage}\n\nSupport team has been notified.`);
}
function unmountCurrent() {
if (currentUnmount) {
currentUnmount();
currentUnmount = null;
currentMFE = null;
document.getElementById('mfe-container').innerHTML =
'<div class="loading">Select a micro frontend to load</div>';
// Remove active state from buttons
document.querySelectorAll('nav button').forEach(btn => {
btn.classList.remove('active');
});
console.log('Container: Unmounted current MFE');
}
}
// Example of how this would work with real dynamic imports:
/*
// Real implementation in separate files:
// mfe/dashboard/index.js
export function mount(element) {
// Dashboard MFE implementation
const root = document.createElement('div');
element.appendChild(root);
// ... render dashboard
return () => {
// Cleanup
element.removeChild(root);
};
}
// container/app.js
async function loadMFE(name) {
const module = await import(`./mfe/${name}/index.js`);
const unmount = module.mount(containerElement);
// Store unmount function for later cleanup
}
*/
</script>
</body>
</html>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment