Skip to content

Luigi - The Enterprise Micro Frontend Framework

Executive Summary

Luigi is an open-source, enterprise-ready JavaScript framework for building modular web applications using micro frontend architecture. Created by SAP and governed by the NeoNephos Foundation, Luigi enables independent teams to develop, deploy, and maintain UI components using different technologies while providing a unified user experience through consistent navigation, authentication, and communication patterns.

Project Origin & Governance

Open Source Enterprise Framework

Repository: github.com/SAP/luigi Original Creator: SAP SE Current Governance: NeoNephos Foundation (The Linux Foundation Europe) License: Apache 2.0 Funding: European Union NextGenerationEU program

History & Evolution

Luigi was originally developed by SAP as an internal solution for building modular administrative interfaces across their product portfolio. Recognizing the broader industry need for enterprise-grade micro frontend solutions, SAP open-sourced Luigi under the Apache 2.0 license.

Key Milestones:

  • Initial Release: SAP open-sources Luigi
  • Community Growth: 903+ GitHub stars, 177+ forks, 64+ contributors
  • Governance Transfer: Project moved to NeoNephos Foundation under Linux Foundation Europe
  • Production Adoption: Used by SAP, emporix, and other enterprises
  • Ecosystem Integration: Became foundation for OpenMFP and Platform Mesh

NeoNephos Foundation

Linux Foundation Europe Initiative

Luigi is governed by the NeoNephos Foundation, which operates under The Linux Foundation Europe and focuses on advancing open-source projects aligned with European digital sovereignty objectives under the IPCEI-CIS (Important Projects of Common European Interest - Cloud Infrastructure and Services) program.

Sister Projects under NeoNephos:

  • OpenMFP - Configuration-driven micro frontend platform built on Luigi
  • Platform Mesh - Kubernetes-native platform using OpenMFP/Luigi
  • Gardener - Kubernetes cluster management
  • IronCore - Cloud infrastructure
  • Garden Linux - Container-optimized OS

Community & Contributing

Active Open Source Community

Luigi maintains an active, welcoming community with multiple channels for collaboration and support.

Communication Channels

GitHub:

  • Repository: github.com/SAP/luigi
  • Discussions: Q&A, ideas, and general discussion
  • Issues: 36+ open issues, bug reports, feature requests
  • Pull Requests: 11+ active PRs

Real-Time Communication:

Documentation:

How to Contribute

Contribution Guidelines

Getting Started:

  1. Read CONTRIBUTING.md in the repository
  2. Check open issues for good first issues
  3. Join Slack to discuss your contribution
  4. Fork the repository and create a branch
  5. Follow code standards (Prettier, ESLint)
  6. Write tests for new features
  7. Submit PR with clear description

Development Setup:

# Clone repository
git clone https://github.com/SAP/luigi.git
cd luigi

# Install dependencies (monorepo)
npm run bootstrap

# Run tests
npm test

# Start development server
npm run simpledev

Quality Standards:

  • Code Formatting: Prettier with husky Git hooks
  • Testing: Unit tests, E2E tests with Cypress, backward compatibility tests
  • Security: HTTPS-only, CSP headers, CORS validation, origin allowlists
  • Documentation: Update docs for new features
  • Licensing: REUSE tool for third-party component tracking

Overview

What is Luigi?

Luigi is a JavaScript framework that enables you to create administrative user interfaces driven by local and distributed views (micro frontends). It breaks down big frontend monoliths into smaller, independent chunks that can be developed by separate teams using different technologies.

Core Problems Solved

Monolithic Frontend Challenges:

  • Technology Lock-in: Entire app must use same framework
  • Team Bottlenecks: Multiple teams competing for deployment slots
  • Slow Development: Changes require full app rebuild and redeployment
  • Scalability Issues: Large codebases become unmaintainable
  • Version Conflicts: Dependency conflicts across features

Luigi's Solutions:

  • Technology Agnostic: Mix Angular, React, Vue, UI5, Svelte, or plain JavaScript
  • Independent Deployment: Teams deploy their micro frontends separately
  • Modular Architecture: Applications decompose into clear functional modules
  • Unified UX: Consistent navigation, auth, and communication despite different technologies
  • Secure Communication: PostMessage API with origin validation and CSP

Key Benefits

Enterprise-Grade Features

For Development Teams:

  • Framework Freedom: Use best tool for each job
  • Team Autonomy: Independent development and deployment
  • Faster Iterations: No coordination needed for releases
  • Easier Testing: Test micro frontends in isolation
  • Simpler Maintenance: Smaller codebases, clearer ownership

For Organizations:

  • Reduced Risk: Gradual migration from monoliths
  • Better Scalability: Scale teams and features independently
  • Future-Proof: Easy to adopt new technologies
  • Cost Efficiency: Reuse components across applications
  • Talent Flexibility: Hire specialists in different frameworks

For Users:

  • Consistent Experience: Unified navigation and authentication
  • Better Performance: Lazy-load features on demand
  • Rich Functionality: Best-of-breed components from different teams
  • Fast Load Times: Optimized bundle sizes

Architecture

Luigi consists of two primary components that work together to create a micro frontend application:

┌────────────────────────────────────────────────────────────────────┐
│                         Luigi Core                                  │
│                    (Shell Application)                              │
│                                                                      │
│  ┌────────────────────────────────────────────────────────────┐    │
│  │                   Configuration Layer                       │    │
│  │  - Navigation Structure (nodes tree)                        │    │
│  │  - Authentication Settings (auth providers)                 │    │
│  │  - Authorization Rules (permissions)                        │    │
│  │  - General Settings (theme, header, footer)                 │    │
│  │  - Routing Configuration (hash/path based)                  │    │
│  └────────────────────────────────────────────────────────────┘    │
│                              │                                       │
│  ┌────────────────────────────────────────────────────────────┐    │
│  │                    Core Components                          │    │
│  │                                                             │    │
│  │  ┌──────────────┐  ┌──────────────┐  ┌──────────────┐     │    │
│  │  │ Top Nav      │  │   Header     │  │  User Menu   │     │    │
│  │  │ (Level 1)    │  │  (Logo/Title)│  │  (Profile)   │     │    │
│  │  └──────────────┘  └──────────────┘  └──────────────┘     │    │
│  │                                                             │    │
│  │  ┌──────────────┐                    ┌──────────────┐     │    │
│  │  │ Side Nav     │                    │  Tab Nav     │     │    │
│  │  │ (Children)   │                    │  (Tabs)      │     │    │
│  │  └──────────────┘                    └──────────────┘     │    │
│  │                                                             │    │
│  │  ┌──────────────────────────────────────────────────┐     │    │
│  │  │         Main Content Area (Iframe Container)     │     │    │
│  │  │                                                   │     │    │
│  │  │  ┌─────────────────────────────────────────┐    │     │    │
│  │  │  │    Micro Frontend A (iframe)            │    │     │    │
│  │  │  │    - Isolated execution context         │    │     │    │
│  │  │  │    - Secure postMessage communication   │    │     │    │
│  │  │  │    - Luigi Client API integration       │    │     │    │
│  │  │  └─────────────────────────────────────────┘    │     │    │
│  │  └──────────────────────────────────────────────────┘     │    │
│  └────────────────────────────────────────────────────────────┘    │
│                              │                                       │
│  ┌────────────────────────────────────────────────────────────┐    │
│  │              PostMessage Communication Layer                │    │
│  │  - Origin validation                                        │    │
│  │  - Message serialization/deserialization                    │    │
│  │  - Context injection                                        │    │
│  │  - Event routing                                            │    │
│  └────────────────────────────────────────────────────────────┘    │
└────────────────────────────────────────────────────────────────────┘
                              ↕ (PostMessage API)
┌────────────────────────────────────────────────────────────────────┐
│                        Luigi Client                                 │
│              (Embedded in each Micro Frontend)                      │
│                                                                      │
│  ┌────────────────────────────────────────────────────────────┐    │
│  │                      Client API                             │    │
│  │                                                             │    │
│  │  - linkManager: Navigation control                         │    │
│  │  - uxManager: UI interactions (alerts, modals)             │    │
│  │  - lifecycleManager: Initialization, context updates       │    │
│  │  - storageManager: Cross-MFE persistent storage            │    │
│  │  - getContext(): Access global & node context              │    │
│  │  - sendCustomMessage(): Custom communication               │    │
│  │  - getToken(): Access auth token                           │    │
│  └────────────────────────────────────────────────────────────┘    │
└────────────────────────────────────────────────────────────────────┘

Luigi Core (Shell Application)

The Container Application

Technology: Svelte-based framework (but framework-agnostic from configuration perspective)

Responsibilities:

  • Navigation Management: Render top nav, side nav, tabs based on configuration
  • Routing: Handle URL changes, deep linking, browser history
  • Authentication: Integrate with OAuth2, OIDC, or custom auth providers
  • Authorization: Check permissions, hide/show navigation nodes
  • Context Management: Inject global and node-specific context into micro frontends
  • Lifecycle Management: Load/unload micro frontends, preserve views, handle modals/drawers
  • Theming: Apply themes, pass CSS variables to micro frontends
  • Localization: Translate navigation labels, manage locale switching

Installation:

npm install @luigi-project/core

Luigi Client (Micro Frontend Library)

The Communication Bridge

Technology: Framework-agnostic JavaScript library

Responsibilities:

  • Initialization: Handshake with Luigi Core on load
  • Context Access: Receive global context (user, permissions, entities)
  • Navigation API: Programmatically navigate between micro frontends
  • UX Integration: Show alerts, modals, loading indicators via Luigi shell
  • Storage API: Cross-micro-frontend persistent storage
  • Event Communication: Send/receive custom messages to other micro frontends
  • Token Access: Retrieve authentication tokens for API calls

Installation:

npm install @luigi-project/client

Usage:

// ES6
import LuigiClient from '@luigi-project/client';

// CommonJS
const LuigiClient = require('@luigi-project/client');

// Global (no bundler)
window.LuigiClient

Communication Flow

User clicks navigation item
Luigi Core routes to URL: /products/123
Luigi Core identifies navigation node
Luigi Core creates iframe with viewUrl
Micro Frontend loads in iframe
Luigi Client initializes in micro frontend
Luigi Client sends init message via postMessage
Luigi Core validates origin
Luigi Core responds with context:
    {
      productId: "123",
      user: { name: "Alice", roles: ["admin"] },
      theme: "dark",
      locale: "en"
    }
Luigi Client triggers init listeners
Micro Frontend receives context and renders
User clicks "Edit Product" button
Micro Frontend calls LuigiClient.linkManager().navigate('/products/123/edit')
Luigi Client sends navigate message via postMessage
Luigi Core validates and routes to /products/123/edit
New micro frontend loads with updated context

Core Concepts

1. Navigation Nodes

Tree-Based Navigation Structure

Navigation in Luigi is defined as a tree of nodes. Each node represents a route and can have child nodes.

Node Structure:

{
  pathSegment: 'products',        // URL segment
  label: 'Products',               // Display name
  icon: 'product',                 // Icon identifier
  viewUrl: '/products.html',       // Micro frontend URL
  children: [                      // Child nodes
    {
      pathSegment: ':id',          // Dynamic segment
      label: 'Product Details',
      viewUrl: '/product-details.html',
      context: {                   // Node-specific context
        currentProduct: ':id'
      }
    }
  ]
}

Navigation Types:

  • Top Navigation: First-level nodes (horizontal menu bar)
  • Side Navigation: Children of active top node (vertical sidebar)
  • Tab Navigation: Tabs within a specific section
  • Context Navigation: Nodes that appear based on entity context

Dynamic Path Segments:

pathSegment: ':productId'  // Matches /products/123, /products/456, etc.

2. View URLs

Micro Frontend Loading

Each navigation node has a viewUrl that points to the micro frontend to load.

Absolute URLs:

viewUrl: 'https://my-microfrontend.com/products'

Relative URLs (resolved against configuration):

viewUrl: '/products'  // Resolved to https://base-url.com/products

Dynamic URLs (with context variables):

viewUrl: '/products/{context.productId}'
// Resolves to: /products/123

3. Context

Shared State Across Micro Frontends

Context is the mechanism for passing data from Luigi Core to micro frontends.

Global Context: Available to all micro frontends

Luigi.setConfig({
  globalContext: {
    user: { name: 'Alice', email: 'alice@example.com' },
    tenant: 'acme-corp',
    theme: 'dark'
  }
});

Node Context: Specific to a navigation node

{
  pathSegment: 'product',
  context: {
    productId: ':id',           // From URL parameter
    view: 'details'
  }
}

Accessing Context in Micro Frontend:

LuigiClient.addInitListener((context) => {
  console.log(context.user);        // Global context
  console.log(context.productId);   // Node context
});

// Or get current context anytime
const context = LuigiClient.getContext();

4. Categories & Grouping

Organize Navigation Items

Categories group related navigation nodes.

Top Navigation Dropdown:

{
  pathSegment: 'settings',
  label: 'Settings',
  category: 'admin',              // Group in dropdown
  icon: 'settings'
}

Side Navigation Collapsible Sections:

{
  pathSegment: 'overview',
  label: 'Overview',
  category: {
    label: 'Analytics',
    icon: 'bar-chart',
    collapsible: true
  }
}

Subcategories (Luigi 2.23.0+):

category: 'admin::user-management'  // Double colon for subcategories

5. View Groups & Preserved Views

Performance Optimization

View Groups allow multiple micro frontends to share the same iframe, reducing load times.

{
  pathSegment: 'products',
  viewGroup: 'productsGroup',
  children: [
    {
      pathSegment: 'list',
      viewGroup: 'productsGroup'  // Reuses same iframe
    },
    {
      pathSegment: ':id',
      viewGroup: 'productsGroup'  // Reuses same iframe
    }
  ]
}

Preserved Views keep micro frontends in memory when navigating away:

{
  pathSegment: 'product',
  context: {
    id: ':id'
  },
  preserveView: true  // Don't destroy iframe when navigating away
}

Users can return to preserved views with:

LuigiClient.linkManager().goBack();

6. Authentication & Authorization

Security Configuration

Authentication Providers: - OpenID Connect (OIDC) - OAuth2 Implicit Grant - Custom Provider

OIDC Example:

Luigi.setConfig({
  auth: {
    use: 'openIdConnect',
    openIdConnect: {
      authority: 'https://auth.example.com',
      client_id: 'my-app',
      scope: 'openid profile email',
      automaticSilentRenew: true
    }
  }
});

Authorization Rules:

{
  pathSegment: 'admin',
  label: 'Admin Panel',
  // Only visible to users with 'admin' permission
  nodeAccessibilityResolver: (nodeConfig, context) => {
    return context.user.roles.includes('admin');
  }
}

Anonymous Access:

{
  pathSegment: 'public',
  anonymousAccess: 'exclusive'  // Only visible when NOT logged in
}

Luigi Core API

Basic Configuration:

Luigi.setConfig({
  navigation: {
    nodes: [
      {
        pathSegment: 'home',
        label: 'Home',
        icon: 'home',
        viewUrl: '/home.html'
      },
      {
        pathSegment: 'products',
        label: 'Products',
        icon: 'product',
        viewUrl: '/products.html',
        children: [
          {
            pathSegment: ':id',
            viewUrl: '/product-details.html',
            context: {
              productId: ':id'
            }
          }
        ]
      }
    ]
  },
  routing: {
    useHashRouting: true  // Use # in URLs (default)
  }
});

General Settings

Header Configuration:

Luigi.setConfig({
  settings: {
    header: {
      logo: '/assets/logo.png',
      title: 'My Application',
      favicon: '/assets/favicon.ico'
    },
    hideNavigation: false,  // Show/hide all navigation
    responsiveNavigation: 'simple'  // 'simple' | 'Fiori3' | 'semiCollapsible'
  }
});

Theme Configuration:

Luigi.setConfig({
  settings: {
    theming: {
      themes: [
        { id: 'light', name: 'Light Theme' },
        { id: 'dark', name: 'Dark Theme' }
      ],
      defaultTheme: 'light'
    }
  }
});

Footer Configuration:

Luigi.setConfig({
  settings: {
    footer: {
      text: '© 2026 My Company',
      links: [
        { label: 'Privacy', url: '/privacy' },
        { label: 'Terms', url: '/terms' }
      ]
    }
  }
});

Advanced Features

Iframe Interceptor (modify iframe before creation):

Luigi.setConfig({
  settings: {
    iframeCreationInterceptor: (iframe, viewUrl, nodeParams) => {
      iframe.sandbox = 'allow-scripts allow-same-origin';
      iframe.title = 'Micro Frontend';
      return iframe;
    }
  }
});

Custom Alert Handler:

Luigi.setConfig({
  settings: {
    customAlertHandler: (settings) => {
      // Use custom UI library for alerts
      myCustomAlert(settings.text, settings.type);
    }
  }
});

Luigi Client API

Lifecycle Management

Initialization:

import LuigiClient from '@luigi-project/client';

// Add init listener (called when Luigi is ready)
LuigiClient.addInitListener((context, origin) => {
  console.log('Luigi initialized');
  console.log('Context:', context);
  console.log('Origin:', origin);

  // Initialize your micro frontend with context
  initApp(context);
});

// Check if Luigi is initialized
if (LuigiClient.isLuigiClientInitialized()) {
  const context = LuigiClient.getContext();
}

Context Updates:

// Listen for context updates (e.g., when navigating within view group)
LuigiClient.addContextUpdateListener((context) => {
  console.log('Context updated:', context);
  updateApp(context);
});

Inactive State (when view is preserved but not visible):

LuigiClient.addInactiveListener(() => {
  console.log('Micro frontend is now inactive');
  pauseDataPolling();
});

Basic Navigation:

// Navigate to absolute path
LuigiClient.linkManager().navigate('/products/123');

// Navigate to relative path
LuigiClient.linkManager().navigate('details');  // Appends to current path

// Navigate with parameters
LuigiClient.linkManager()
  .withParams({ view: 'edit', mode: 'advanced' })
  .navigate('/products/123');

Contextual Navigation:

// Navigate from parent context
LuigiClient.linkManager()
  .fromContext('product')
  .navigate('/analytics');

// Navigate from closest context
LuigiClient.linkManager()
  .fromClosestContext()
  .navigate('settings');

Go Back:

// Navigate to previous view
LuigiClient.linkManager().goBack();

// Go back with parameters
LuigiClient.linkManager().goBack({ refreshed: true });

Modal Navigation:

// Open in modal
LuigiClient.linkManager()
  .withParams({ mode: 'edit' })
  .openAsModal('/products/123/edit', { title: 'Edit Product', size: 'l' });

// Close modal
LuigiClient.linkManager().modal().close();

Split View:

// Open split view
LuigiClient.linkManager()
  .openAsSplitView('/products/123/preview', { size: 40, collapsed: false });

// Collapse/expand split view
LuigiClient.linkManager().splitView().collapse();
LuigiClient.linkManager().splitView().expand();

// Close split view
LuigiClient.linkManager().splitView().close();

Drawer:

// Open drawer
LuigiClient.linkManager()
  .openAsDrawer('/notifications', { size: 's', backdrop: true });

// Close drawer
LuigiClient.linkManager().drawer().close();

Intent-Based Navigation (semantic navigation):

LuigiClient.linkManager().navigateToIntent('Sales-order', {
  id: '123',
  mode: 'edit'
});

UX Manager (UI Interactions)

Loading Indicators:

// Show loading indicator
LuigiClient.uxManager().showLoadingIndicator();

// Hide loading indicator
LuigiClient.uxManager().hideLoadingIndicator();

Alerts:

// Show success alert
LuigiClient.uxManager().showAlert({
  text: 'Product saved successfully',
  type: 'success',  // 'info' | 'success' | 'warning' | 'error'
  closeAfter: 3000  // Auto-close after 3 seconds
});

// Alert with HTML
LuigiClient.uxManager().showAlert({
  text: 'Click <a href="/help">here</a> for help',
  type: 'info'
});

Confirmation Modals:

LuigiClient.uxManager()
  .showConfirmationModal({
    header: 'Delete Product',
    body: 'Are you sure you want to delete this product?',
    buttonConfirm: 'Delete',
    buttonDismiss: 'Cancel'
  })
  .then(() => {
    // User clicked "Delete"
    deleteProduct();
  })
  .catch(() => {
    // User clicked "Cancel"
  });

Document Title:

LuigiClient.uxManager().setDocumentTitle('Product Details - My App');

Locale & Theme:

// Get current locale
const locale = LuigiClient.uxManager().getCurrentLocale();  // 'en', 'de', etc.

// Get current theme
const theme = LuigiClient.uxManager().getCurrentTheme();  // 'light', 'dark', etc.

Backdrop:

// Show backdrop (overlay)
LuigiClient.uxManager().showBackdrop();

// Hide backdrop
LuigiClient.uxManager().hideBackdrop();

Dirty Status (unsaved changes):

// Mark form as dirty (warns user before navigating away)
LuigiClient.uxManager().setDirtyStatus(true);

// Clear dirty status
LuigiClient.uxManager().setDirtyStatus(false);

Storage Manager (Cross-MFE Storage)

Set & Get Items:

// Store value (returns Promise)
await LuigiClient.storageManager().setItem('user-preference', 'dark-theme');

// Retrieve value (returns Promise)
const preference = await LuigiClient.storageManager().getItem('user-preference');

Remove Items:

// Remove single item
await LuigiClient.storageManager().removeItem('user-preference');

// Clear all storage
await LuigiClient.storageManager().clear();

Check Existence:

// Check if key exists
const exists = await LuigiClient.storageManager().has('user-preference');

// Get all keys
const allKeys = await LuigiClient.storageManager().getAllKeys();

Custom Messages

Send Messages:

// Send custom message to Luigi Core
LuigiClient.sendCustomMessage({
  id: 'my-event',
  data: { productId: '123', action: 'refresh' }
});

Receive Messages:

// Listen for custom messages from Luigi Core
LuigiClient.addCustomMessageListener('refresh-data', (data) => {
  console.log('Received refresh command:', data);
  reloadData();
});

Authentication

Get Token:

// Retrieve current auth token
const token = LuigiClient.getToken();

// Use token in API calls
fetch('/api/products', {
  headers: {
    'Authorization': `Bearer ${token}`
  }
});

Path & Node Parameters

Get Node Parameters:

// Get node parameters (from navigation config)
const params = LuigiClient.getNodeParams();

Get Path Parameters:

// Get path parameters (from URL, e.g., :id)
const pathParams = LuigiClient.getPathParams();
console.log(pathParams.productId);  // '123' from /products/123

Get Search Parameters (query string):

// Get search/query parameters
const searchParams = LuigiClient.getSearchParams();
console.log(searchParams.view);  // 'edit' from ?view=edit

Creating a Luigi Application

1. Core Application Setup

Install Dependencies:

npm install @luigi-project/core

HTML Setup (index.html):

<!DOCTYPE html>
<html>
<head>
  <title>My Luigi App</title>
  <link rel="stylesheet" href="/luigi-core/luigi.css">
</head>
<body>
  <script src="/luigi-core/luigi.js"></script>
  <script src="/luigi-config.js"></script>
</body>
</html>

Configuration (luigi-config.js):

Luigi.setConfig({
  navigation: {
    nodes: [
      {
        pathSegment: 'home',
        label: 'Home',
        icon: 'home',
        viewUrl: 'https://my-microfrontend.com/home.html',
        children: [
          {
            pathSegment: 'overview',
            label: 'Overview',
            viewUrl: 'https://my-microfrontend.com/overview.html'
          }
        ]
      }
    ]
  },
  routing: {
    useHashRouting: true
  },
  settings: {
    header: {
      logo: '/logo.png',
      title: 'My Application'
    },
    responsiveNavigation: 'simple'
  }
});

2. Micro Frontend Setup

Install Luigi Client:

npm install @luigi-project/client

Initialize in Your App:

import LuigiClient from '@luigi-project/client';

// Wait for Luigi to initialize
LuigiClient.addInitListener((context) => {
  console.log('Initialized with context:', context);

  // Access user info
  const user = context.user;

  // Access custom context
  const productId = context.productId;

  // Initialize your application
  renderApp(context);
});

Navigation Example:

// In your UI component
function navigateToProduct(productId) {
  LuigiClient.linkManager().navigate(`/products/${productId}`);
}

function showEditModal(productId) {
  LuigiClient.linkManager()
    .openAsModal(`/products/${productId}/edit`, {
      title: 'Edit Product',
      size: 'l'
    });
}

3. Example: Angular Micro Frontend

import { Component, OnInit } from '@angular/core';
import LuigiClient from '@luigi-project/client';

@Component({
  selector: 'app-product-list',
  template: `
    <div *ngIf="user">
      <h1>Products for {{ user.name }}</h1>
      <button (click)="createProduct()">Create Product</button>
    </div>
  `
})
export class ProductListComponent implements OnInit {
  user: any;

  ngOnInit() {
    LuigiClient.addInitListener((context) => {
      this.user = context.user;
    });
  }

  createProduct() {
    LuigiClient.linkManager()
      .openAsModal('/products/new', {
        title: 'Create Product',
        size: 'm'
      });
  }
}

4. Example: React Micro Frontend

import React, { useEffect, useState } from 'react';
import LuigiClient from '@luigi-project/client';

function ProductDetails() {
  const [product, setProduct] = useState(null);
  const [productId, setProductId] = useState(null);

  useEffect(() => {
    LuigiClient.addInitListener((context) => {
      setProductId(context.productId);
      fetchProduct(context.productId);
    });
  }, []);

  const fetchProduct = async (id) => {
    const token = LuigiClient.getToken();
    const response = await fetch(`/api/products/${id}`, {
      headers: { 'Authorization': `Bearer ${token}` }
    });
    setProduct(await response.json());
  };

  const handleEdit = () => {
    LuigiClient.linkManager().navigate('edit');
  };

  return (
    <div>
      {product && (
        <>
          <h1>{product.name}</h1>
          <button onClick={handleEdit}>Edit</button>
        </>
      )}
    </div>
  );
}

export default ProductDetails;

Authentication Integration

OpenID Connect (OIDC)

Luigi.setConfig({
  auth: {
    use: 'openIdConnect',
    openIdConnect: {
      authority: 'https://auth.example.com',
      client_id: 'my-app-client-id',
      scope: 'openid profile email',
      redirect_uri: 'https://my-app.com/callback',
      post_logout_redirect_uri: 'https://my-app.com',
      automaticSilentRenew: true,
      accessTokenExpiringNotificationTime: 60,
      // Remove sensitive data before storage (GDPR compliance)
      profileStorageInterceptorFn: (profile) => {
        delete profile.email;
        return profile;
      }
    },
    disableAutoLogin: false,
    storage: 'localStorage'  // 'localStorage' | 'sessionStorage' | 'none'
  }
});

OAuth2 Implicit Grant

Luigi.setConfig({
  auth: {
    use: 'oAuth2ImplicitGrant',
    oAuth2ImplicitGrant: {
      authorizeUrl: 'https://auth.example.com/authorize',
      logoutUrl: 'https://auth.example.com/logout',
      oAuthData: {
        client_id: 'my-app',
        redirect_uri: 'https://my-app.com/callback',
        scope: 'read write'
      }
    }
  }
});

Custom Authentication Provider

class MyCustomAuthProvider {
  login() {
    // Custom login logic
    return new Promise((resolve, reject) => {
      // Perform authentication
      const token = authenticateUser();
      resolve(token);
    });
  }

  logout() {
    // Custom logout logic
    clearUserSession();
  }

  setTokenExpirationAction() {
    // Handle token expiration
  }

  setLogoutAction() {
    // Handle logout
  }

  userInfo() {
    // Return user information
    return getUserInfo();
  }

  generateNonce() {
    // Generate security nonce
    return crypto.randomUUID();
  }
}

Luigi.setConfig({
  auth: {
    use: 'myCustomAuth',
    myCustomAuth: new MyCustomAuthProvider()
  }
});

Best Practices

Production-Ready Patterns

1. Security Best Practices

Always Use HTTPS:

// Micro frontends must be served over HTTPS in production
viewUrl: 'https://secure-microfrontend.com/app'

Implement Content Security Policy:

<meta http-equiv="Content-Security-Policy"
      content="default-src 'self';
               frame-src https://trusted-domain.com;
               script-src 'self' 'unsafe-inline';">

Validate Origins:

// In your micro frontend
LuigiClient.addInitListener((context, origin) => {
  // Verify Luigi Core origin
  const allowedOrigins = ['https://my-app.com', 'https://staging.my-app.com'];
  if (!allowedOrigins.includes(origin)) {
    console.error('Untrusted origin:', origin);
    return;
  }
  initApp(context);
});

Restrict Iframe Permissions:

{
  pathSegment: 'external',
  viewUrl: 'https://external.com',
  iframe: {
    sandbox: 'allow-scripts allow-same-origin'  // Minimal permissions
  }
}

2. Performance Optimization

Use View Groups:

// Reuse iframes for related views
{
  pathSegment: 'products',
  viewGroup: 'productManagement',
  children: [
    { pathSegment: 'list', viewGroup: 'productManagement' },
    { pathSegment: ':id', viewGroup: 'productManagement' }
  ]
}

Lazy Load Navigation Nodes:

{
  pathSegment: 'reports',
  label: 'Reports',
  // Load children dynamically when accessed
  children: () => {
    return fetch('/api/report-configs')
      .then(res => res.json())
      .then(configs => configs.map(c => ({
        pathSegment: c.id,
        label: c.name,
        viewUrl: c.url
      })));
  }
}

Preserve Views Strategically:

// Only preserve expensive views
{
  pathSegment: 'dashboard',
  viewUrl: '/dashboard',
  preserveView: true  // Keep in memory for fast return
}

3. Context Design

Keep Global Context Lean:

// Good: Only essential global data
globalContext: {
  user: { id: '123', name: 'Alice' },
  tenant: 'acme',
  locale: 'en'
}

// Bad: Too much data
globalContext: {
  user: { ...everythingAboutUser },
  allProducts: [...],
  allOrders: [...]
}

Use Node Context for Specifics:

{
  pathSegment: 'product',
  context: {
    productId: ':id',
    mode: 'view'
  }
}

4. Navigation Design

Keep Navigation Shallow:

✅ Good: /products/123
✅ Good: /settings/profile

❌ Bad: /region/country/city/store/product/123

Use Categories for Organization:

// Group related items in dropdowns/sections
{
  pathSegment: 'users',
  category: 'admin',
  label: 'User Management'
},
{
  pathSegment: 'roles',
  category: 'admin',
  label: 'Role Management'
}

5. Error Handling

Handle Missing Context:

LuigiClient.addInitListener((context) => {
  if (!context.productId) {
    LuigiClient.uxManager().showAlert({
      text: 'Product ID is required',
      type: 'error'
    });
    LuigiClient.linkManager().navigate('/products');
    return;
  }
  loadProduct(context.productId);
});

Handle Failed Navigation:

LuigiClient.linkManager()
  .navigate('/products/123')
  .catch((error) => {
    console.error('Navigation failed:', error);
    LuigiClient.uxManager().showAlert({
      text: 'Failed to navigate',
      type: 'error'
    });
  });

6. Testing

Unit Test Navigation Logic:

// Mock Luigi Client
jest.mock('@luigi-project/client');

test('navigates to product details', () => {
  const navigate = jest.fn();
  LuigiClient.linkManager.mockReturnValue({ navigate });

  viewProductDetails('123');

  expect(navigate).toHaveBeenCalledWith('/products/123');
});

E2E Test with Cypress:

describe('Product Navigation', () => {
  it('should navigate to product details', () => {
    cy.visit('/');
    cy.get('[data-testid="product-link"]').click();
    cy.url().should('include', '/products/123');
    cy.get('h1').should('contain', 'Product Details');
  });
});

Common Pitfalls

Pitfall 1: Direct URL Manipulation

Don't Bypass Luigi Navigation

Problem: Directly manipulating window.location breaks Luigi's navigation state

// ❌ Bad
window.location.href = '/products/123';

// ✅ Good
LuigiClient.linkManager().navigate('/products/123');

Pitfall 2: Not Validating Origins

Always Verify Message Origins

Problem: Accepting messages from untrusted origins creates security vulnerabilities

// ❌ Bad
LuigiClient.addInitListener((context) => {
  initApp(context);  // No origin check
});

// ✅ Good
LuigiClient.addInitListener((context, origin) => {
  if (origin !== 'https://my-app.com') {
    console.error('Untrusted origin');
    return;
  }
  initApp(context);
});

Pitfall 3: Hardcoding Base URLs

Use Relative Paths

Problem: Hardcoded URLs break in different environments

// ❌ Bad
viewUrl: 'https://production.com/products'

// ✅ Good
viewUrl: '/products'  // Resolved against base URL

Pitfall 4: Synchronous Assumptions

Luigi Client is Asynchronous

Problem: Assuming Luigi Client methods are synchronous

// ❌ Bad
const context = LuigiClient.getContext();
console.log(context.user);  // May be undefined

// ✅ Good
LuigiClient.addInitListener((context) => {
  console.log(context.user);  // Guaranteed to be available
});

Pitfall 5: Missing Loading States

Show Loading Indicators

Problem: Users see blank screens during data fetching

// ✅ Good
LuigiClient.addInitListener(async (context) => {
  LuigiClient.uxManager().showLoadingIndicator();

  try {
    const data = await fetchData(context.productId);
    renderApp(data);
  } finally {
    LuigiClient.uxManager().hideLoadingIndicator();
  }
});

Pitfall 6: Over-Preserving Views

Memory Leaks

Problem: Preserving too many views consumes memory

// ❌ Bad: Every view preserved
{
  pathSegment: 'products',
  preserveView: true,
  children: [
    { pathSegment: 'list', preserveView: true },
    { pathSegment: ':id', preserveView: true },
    { pathSegment: 'analytics', preserveView: true }
  ]
}

// ✅ Good: Only preserve expensive views
{
  pathSegment: 'dashboard',
  preserveView: true  // Only dashboard is complex/expensive
}

Luigi Plugins

Luigi supports plugins to extend functionality:

OAuth2 Plugin

npm install @luigi-project/plugin-auth-oauth2
Luigi.setConfig({
  auth: {
    use: 'oAuth2ImplicitGrant',
    oAuth2ImplicitGrant: {
      // ... OAuth2 config
    }
  }
});

OIDC Plugin

Built into Luigi Core, no separate installation needed.

Authorization Helpers Plugin

For advanced authorization rules and permission management.

Integration with OpenMFP and Platform Mesh

Luigi as Foundation

OpenMFP builds on top of Luigi by providing:

  • Configuration-driven setup (content-configuration.json)
  • Higher-level abstractions for common patterns
  • Kubernetes-native integration
  • Simplified developer experience

Platform Mesh uses OpenMFP (which uses Luigi) to create:

  • Kubernetes-native portal with Extension Manager
  • Entity-based navigation (Organizations → Accounts → Projects)
  • Dynamic extension discovery from CRDs
  • Integration with Security Operator for fine-grained permissions

Relationship:

Luigi (Core Framework)
OpenMFP (Configuration Abstraction)
Platform Mesh (Kubernetes-Native Portal)

When to Use Each:

  • Luigi directly: Maximum flexibility, custom requirements, non-OpenMFP environments
  • OpenMFP: Simplified configuration, common patterns, community standards
  • Platform Mesh: Kubernetes environment, need Extension Manager, entity model required

Technical Stack

Technologies Used

Luigi Core:

  • Framework: Svelte
  • Languages: JavaScript (56.8%), TypeScript (16.4%), Svelte (12.8%), HTML (9.6%), SCSS (2.5%)
  • Build: Rollup
  • Testing: Mocha, Chai, Cypress

Luigi Client:

  • Language: JavaScript/TypeScript
  • Module Formats: ES6, CommonJS, UMD
  • Size: ~20KB minified

Repository:

  • Monorepo: Managed with custom scripts
  • Bootstrap: npm run bootstrap (symbolic linking)
  • Testing: Unit tests, E2E tests, backward compatibility tests
  • CI/CD: GitHub Actions

Troubleshooting

Common Issues

Issue Symptoms Resolution
Micro frontend not loading Blank iframe Check CORS headers, ensure HTTPS, verify viewUrl is accessible
Navigation not working Clicks don't navigate Verify pathSegments are unique, check for route conflicts
Context is empty getContext() returns {} Use addInitListener, verify Luigi Core is configured
Authentication loop Keeps redirecting to login Check auth provider config, verify redirect URIs match
Postmessage errors Console errors about origins Validate origin in micro frontend, check allowlists

Debug Mode

// Enable Luigi debug logs
localStorage.setItem('luigi.debug', 'true');

// Check Luigi version
console.log(LuigiClient.VERSION);

Browser Console

// Access Luigi globally (in shell app)
window.Luigi

// Access Luigi Client globally (in micro frontend)
window.LuigiClient

// Check current navigation state
Luigi.navigation().getCurrentNode()
  • OpenMFP Overview - Configuration-driven platform built on Luigi
  • Platform Mesh Portal - How Platform Mesh uses OpenMFP/Luigi
  • Extension Manager - Kubernetes operator for micro frontends

Further Resources

Official Documentation

Community

  • Slack: Luigi Community Slack
  • GitHub Discussions: Q&A and feature discussions
  • YouTube: Luigi Project Channel

Learning Resources

Governance

  • NeoNephos Foundation: neonephos.org
  • Linux Foundation Europe: Parent organization
  • Contributing: CONTRIBUTING.md in repository
  • License: Apache 2.0

Sources: