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.

Revisions

  1. mzbac revised this gist Aug 18, 2025. 1 changed file with 163 additions and 68 deletions.
    231 changes: 163 additions & 68 deletions gistfile1.txt
    Original file line number Diff line number Diff line change
    @@ -168,9 +168,10 @@
    </header>

    <nav>
    <button onclick="loadMFE('dashboard')" id="btn-dashboard">Dashboard MFE</button>
    <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</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>

    @@ -188,41 +189,27 @@
    const mfeModules = {
    dashboard: {
    mount: (element) => {
    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 class="dashboard-widget">
    <h3>User Activity</h3>
    <p>Active Users: 1,234</p>
    <p>New Signups: 89</p>
    </div>
    <div class="dashboard-widget">
    <h3>System Status</h3>
    <p>All systems operational ✅</p>
    // 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>
    </div>
    `;

    // Simulate some interactive behavior
    const widgets = element.querySelectorAll('.dashboard-widget');
    widgets.forEach(widget => {
    widget.style.cursor = 'pointer';
    widget.addEventListener('click', () => {
    widget.style.background = '#f0f4ff';
    setTimeout(() => {
    widget.style.background = 'white';
    }, 200);
    });
    });

    console.log('Dashboard MFE mounted');
    `;
    } catch (error) {
    // MFE internal error - re-throw to be caught by container
    throw new Error(`Dashboard MFE Error: ${error.message}`);
    }

    // Return unmount function
    return () => {
    console.log('Dashboard MFE unmounted');
    element.innerHTML = '';
    @@ -257,36 +244,65 @@

    settings: {
    mount: (element) => {
    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>
    // 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>
    <div class="form-group">
    <label>Theme</label>
    <select>
    <option>Light</option>
    <option>Dark</option>
    <option>Auto</option>
    </select>
    `;
    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 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>
    `;
    }, 500);

    // Initial loading state
    element.innerHTML = `
    <div class="mfe-wrapper">
    <h2>⚙️ Settings Micro Frontend</h2>
    <div class="loading">Loading settings...</div>
    </div>
    `;

    @@ -297,6 +313,11 @@
    element.innerHTML = '';
    };
    }
    },

    // Broken MFE - simulates a completely failed module
    broken: {
    mount: undefined // This MFE is completely broken!
    }
    };

    @@ -331,32 +352,106 @@

    // Unmount current MFE if exists
    if (currentUnmount) {
    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 = '';

    // Mount the MFE by passing the container element
    currentUnmount = mfeModule.mount(container);
    currentMFE = name;
    // Create an error boundary wrapper
    const mfeWrapper = document.createElement('div');
    mfeWrapper.id = `mfe-${name}-wrapper`;
    container.appendChild(mfeWrapper);

    console.log(`Container: Loaded and mounted "${name}" MFE`);
    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">
    Failed to load micro frontend: ${error.message}
    <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();
  2. mzbac created this gist Aug 18, 2025.
    404 changes: 404 additions & 0 deletions gistfile1.txt
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,404 @@
    <!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</button>
    <button onclick="loadMFE('profile')" id="btn-profile">Profile MFE</button>
    <button onclick="loadMFE('settings')" id="btn-settings">Settings 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) => {
    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 class="dashboard-widget">
    <h3>User Activity</h3>
    <p>Active Users: 1,234</p>
    <p>New Signups: 89</p>
    </div>
    <div class="dashboard-widget">
    <h3>System Status</h3>
    <p>All systems operational ✅</p>
    </div>
    </div>
    `;

    // Simulate some interactive behavior
    const widgets = element.querySelectorAll('.dashboard-widget');
    widgets.forEach(widget => {
    widget.style.cursor = 'pointer';
    widget.addEventListener('click', () => {
    widget.style.background = '#f0f4ff';
    setTimeout(() => {
    widget.style.background = 'white';
    }, 200);
    });
    });

    console.log('Dashboard MFE mounted');

    // Return unmount function
    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) => {
    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>
    `;

    console.log('Settings MFE mounted');

    return () => {
    console.log('Settings MFE unmounted');
    element.innerHTML = '';
    };
    }
    }
    };

    // 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) {
    currentUnmount();
    currentUnmount = null;
    }

    // Dynamically import the MFE module
    const mfeModule = await importMFE(name);

    // Clear container
    container.innerHTML = '';

    // Mount the MFE by passing the container element
    currentUnmount = mfeModule.mount(container);
    currentMFE = name;

    console.log(`Container: Loaded and mounted "${name}" MFE`);

    } catch (error) {
    console.error('Failed to load MFE:', error);
    container.innerHTML = `
    <div class="error">
    Failed to load micro frontend: ${error.message}
    </div>
    `;
    }
    }

    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>