Skip to content

OpenMFP - Open Micro Frontend Platform

Executive Summary

OpenMFP (Open Micro Frontend Platform) is a configuration-driven micro frontend framework that enables the dynamic integration of independent frontend services into unified portal applications. Built on SAP's Luigi Framework, OpenMFP provides a technology-agnostic platform for composing distributed web applications without requiring code changes for integration.

Project Origin & Governance

NeoNephos Foundation Project

OpenMFP is governed by the NeoNephos Foundation, a Linux Foundation Europe initiative dedicated to advancing open-source projects that align with European digital sovereignty objectives under the IPCEI-CIS (Important Projects of Common European Interest - Cloud Infrastructure and Services) program.

Funding: Supported by the European Union's NextGenerationEU initiative.

Foundation Structure:

  • Governing Board: Strategic oversight and direction
  • Technical Advisory Council: Technical guidance and standards
  • General Members: Contributing organizations and individuals

Sister Projects: OpenMFP is part of a broader ecosystem including Platform Mesh, Gardener, IronCore, and Garden Linux, all under NeoNephos governance.

Community & Contributing

Open Source & Community-Driven

OpenMFP operates as an open-source project with active community engagement and transparent governance under Linux Foundation principles.

How to Contribute:

  • GitHub: github.com/openmfp - Code contributions, issues, pull requests
  • Projects Repository: Submit proposals for new projects or features through NeoNephos Projects Repository
  • Community Calls: Monthly meetings for discussions and planning
  • Zulip Chat: Real-time communication with maintainers and contributors
  • Mailing Lists: Announcements and technical discussions

Official Resources:

Contribution Model

The foundation welcomes contributions from individuals and organizations. All projects follow neutral governance models ensuring no single entity controls the direction. Contribution guidelines are available in the community repository.

Overview

What is OpenMFP?

OpenMFP is a framework that enables the dynamic integration of services into a unified common interface experience via Micro Frontends. It allows organizations to compose multiple independent frontend services into cohesive portal applications through simple configuration files, without requiring code changes.

Core Problems Solved

Traditional monolithic frontend applications face several challenges:

  • Tight Coupling: All features bundled into a single deployment
  • Team Bottlenecks: Multiple teams competing to deploy to the same application
  • Technology Lock-in: Entire application must use the same framework
  • Scalability Issues: Large codebases become difficult to maintain
  • Slow Iterations: Changes to one feature require full application redeployment

OpenMFP addresses these by enabling micro frontend architecture where independent teams can:

  • Build features with their preferred technology stack
  • Deploy independently without coordination
  • Integrate seamlessly through configuration only
  • Share data and context across boundaries
  • Maintain consistent user experience

Core Concepts

1. Micro Frontends

Independent UI Applications

Micro frontends are standalone web applications that can be composed into a larger portal. Each micro frontend:

  • Runs independently with its own build and deploy pipeline
  • Can use any technology stack (React, Angular, Vue, UI5, vanilla JS, etc.)
  • Owns its own domain functionality and user experience
  • Integrates with the portal through standard interfaces

2. Luigi Framework Foundation

Built on Luigi

OpenMFP is built on top of the Luigi Framework, SAP's open-source micro frontend orchestrator. Luigi provides the underlying architecture for:

  • Iframe-based isolation: Each micro frontend runs in its own iframe for security and independence
  • Navigation management: Centralized routing and menu system
  • Context sharing: Communication between shell and micro frontends
  • Authorization: Per-node permission controls
  • Localization: Multi-language support

Luigi is technology-agnostic and Fiori-compliant, making it suitable for enterprise environments while remaining flexible for any UI framework.

3. Content Configuration System

Configuration-Driven Integration

The heart of OpenMFP's simplicity is its content configuration approach. Micro frontends register themselves by providing a content-configuration.json file that describes:

  • Navigation structure
  • URL endpoints
  • Entity relationships
  • Visibility rules
  • Permissions
  • Localization strings

No code changes required - the portal dynamically discovers and integrates micro frontends at runtime.

4. Portal Architecture

Shell Application

The OpenMFP Portal is the shell application (built with Angular and TypeScript) that:

  • Loads and orchestrates micro frontends
  • Provides navigation, header, and user management UI
  • Manages authentication and context
  • Handles routing and deep linking
  • Enforces permissions and feature toggles

5. Entity-Based Navigation

Hierarchical Context System

OpenMFP supports entity hierarchies (e.g., Organization → Project → Resource) where:

  • Navigation reflects entity relationships
  • Micro frontends can appear contextually based on current entity
  • URLs encode entity context: /organizations/acme/projects/project-1/dashboard
  • Extensions dynamically load based on entity type

6. Extension Manager Integration

Kubernetes Native

In cloud-native environments (like Platform Mesh), OpenMFP integrates with Extension Manager operators where micro frontend configurations are stored as Kubernetes CRDs (Custom Resource Definitions). The portal fetches configurations from the cluster, enabling GitOps workflows.

Architecture

┌─────────────────────────────────────────────────────────────────┐
│                      OpenMFP Portal (Shell)                      │
│  ┌────────────────────────────────────────────────────────────┐ │
│  │              Luigi Core (Navigation & Context)              │ │
│  │  - Router                                                   │ │
│  │  - Navigation Tree                                          │ │
│  │  - Context Provider                                         │ │
│  │  - Authentication Handler                                   │ │
│  └─────────────┬───────────────┬───────────────┬──────────────┘ │
│                │               │               │                 │
└────────────────┼───────────────┼───────────────┼─────────────────┘
                 │               │               │
                 ▼               ▼               ▼
      ┌──────────────┐  ┌──────────────┐  ┌──────────────┐
      │ Micro        │  │ Micro        │  │ Micro        │
      │ Frontend A   │  │ Frontend B   │  │ Frontend C   │
      │              │  │              │  │              │
      │ (Angular)    │  │ (React)      │  │ (Vue)        │
      │              │  │              │  │              │
      │ - UI Logic   │  │ - UI Logic   │  │ - UI Logic   │
      │ - Content    │  │ - Content    │  │ - Content    │
      │   Config     │  │   Config     │  │   Config     │
      └──────────────┘  └──────────────┘  └──────────────┘

Configuration Flow:
─────────────────

┌─────────────────┐       ┌──────────────────────────────────┐
│ Extension CRD   │──────▶│ Extension Manager Operator       │
│ (Kubernetes)    │       │ - Validates ContentConfiguration │
│                 │       │ - Exposes via API                │
└─────────────────┘       └─────────────┬────────────────────┘
                          ┌─────────────────────────────────┐
                          │ OpenMFP Portal                  │
                          │ - Fetches all configurations    │
                          │ - Builds navigation tree        │
                          │ - Loads micro frontends         │
                          └─────────────────────────────────┘

Content Configuration Structure

content-configuration.json

The content configuration file is a Luigi configuration fragment that describes how a micro frontend integrates with the portal.

Minimal Configuration

{
  "luigiConfigFragment": {
    "nodes": [
      {
        "pathSegment": "my-app",
        "label": "My Application",
        "urlSuffix": "/my-app",
        "entityType": "global",
        "viewGroup": {
          "preloadUrl": false
        }
      }
    ]
  }
}

Advanced Configuration with Entities

{
  "luigiConfigFragment": {
    "nodes": [
      {
        "pathSegment": "dashboard",
        "label": "{{dashboard}}",
        "entityType": "project",
        "urlSuffix": "/dashboard",
        "visibleForEntityContext": ["project"],
        "viewGroup": {
          "preloadUrl": false,
          "requiredIFramePermissions": {
            "allow": ["clipboard-write", "fullscreen"],
            "sandbox": "allow-scripts allow-same-origin"
          }
        },
        "children": [
          {
            "pathSegment": "metrics",
            "label": "{{metrics}}",
            "urlSuffix": "/metrics"
          }
        ]
      }
    ],
    "texts": [
      {
        "locale": "en",
        "textDictionary": {
          "dashboard": "Dashboard",
          "metrics": "Metrics"
        }
      },
      {
        "locale": "de",
        "textDictionary": {
          "dashboard": "Dashboard",
          "metrics": "Metriken"
        }
      }
    ]
  }
}

Configuration Properties

Key Configuration Fields

Property Description Example
pathSegment URL path component "dashboard"
label Display text in navigation "Dashboard" or "{{key}}" for i18n
urlSuffix Relative path to micro frontend "/dashboard"
entityType Entity classification "global", "project", "organization"
children Nested navigation nodes Array of node objects
visibleForEntityContext Show only for specific entity types ["project", "account"]
visibleForContext JMESPath query for conditional visibility "entity.status == 'active'"
hideFromNav Exclude from navigation menu true
requiredIFramePermissions Iframe allow and sandbox attributes Object with permissions

Entity System

Dynamic Entity Loading

Nodes can define entities that trigger dynamic extension loading:

{
  "pathSegment": "projects",
  "label": "Projects",
  "defineEntity": {
    "id": "project",
    "contextKey": "projectId",
    "dynamicFetchId": "workspace"
  }
}

When navigating to /projects/123, the portal:

  1. Extracts projectId=123 from URL
  2. Requests extensions with entityType="project" for workspace scope
  3. Dynamically adds matching micro frontends to navigation

Title Resolver

Dynamic Titles from APIs

Fetch navigation titles from external APIs:

{
  "pathSegment": "project",
  "titleResolver": {
    "request": {
      "url": "/api/projects/${projectId}"
    },
    "titlePropertyChain": "data.name",
    "fallbackTitle": "Project",
    "prerenderFallback": true
  }
}

Conditional Display

Configuration Missing Hints

Show helpful messages when required configuration is absent:

{
  "configurationMissing": "!account.clusterConfig",
  "configurationHint": "Please configure cluster connection",
  "configurationLink": {
    "path": "/settings/cluster",
    "label": "Configure Now"
  }
}

Feature Toggles

Experimental Features

Control visibility with feature flags:

{
  "pathSegment": "beta-feature",
  "label": "Beta Feature",
  "visibleForFeatureToggles": ["beta-access"]
}

Enable in URL: https://portal.example.com?ft=beta-access

Intent-Based Navigation

Semantic Cross-Navigation

Define inbound intents for robust deep linking:

{
  "targetAppConfig": {
    "crossNavigation": {
      "inbounds": {
        "viewProject": {
          "semanticObject": "project",
          "action": "view",
          "pathSegment": "/projects/:projectId/view"
        }
      }
    }
  }
}

Other micro frontends can navigate without knowing exact paths:

LuigiClient.linkManager().navigate({
  semanticObject: "project",
  action: "view",
  params: { projectId: "123" }
});

How It Works: Integration Flow

End-to-End Integration

1. Development Phase

Micro Frontend Team:

  1. Create application with any framework (Angular, React, Vue, etc.)
  2. Install Luigi Client library: npm install @luigi-project/client
  3. Add Luigi Client to application for context access and navigation
  4. Create content-configuration.json in public/assets folder
  5. Define navigation nodes, entity types, and visibility rules

2. Registration Phase

Kubernetes/Extension Manager (Platform Mesh scenario):

  1. Create Extension CRD with configuration URL
  2. Extension Manager validates configuration
  3. Configuration exposed via API endpoint

OR Standalone:

  1. Host configuration file at https://my-app.com/assets/content-configuration.json
  2. Register URL in portal configuration

3. Runtime Phase

OpenMFP Portal:

  1. Fetch: Portal requests all content configurations from Extension Manager API or direct URLs
  2. Validate: Checks configuration schema and permissions
  3. Merge: Combines all configurations into unified Luigi navigation tree
  4. Render: Builds navigation menu and routing table
  5. Route: User navigates to /my-app → Portal loads micro frontend in iframe
  6. Context: Portal injects entity context (project ID, user info, etc.) into micro frontend
  7. Communicate: Micro frontend uses Luigi Client to navigate, show alerts, update settings

4. User Interaction

User clicks "Dashboard" in navigation
Portal routes to /projects/123/dashboard
Portal identifies entity: project=123
Portal loads dashboard micro frontend in iframe
Portal sends context: { projectId: "123", user: {...} }
Dashboard micro frontend receives context via Luigi Client
Dashboard fetches project data and renders UI
User clicks "View Metrics" in dashboard
Dashboard calls LuigiClient.linkManager().navigate("/metrics")
Portal routes to /projects/123/dashboard/metrics
Portal updates iframe URL to show metrics view

Creating a Micro Frontend

Quick Start Guide

Prerequisites

# Required tools
- Node.js (LTS version)
- npm or yarn
- Angular CLI (for Angular projects)

Step 1: Create Application

# Angular example
ng new my-microfrontend
cd my-microfrontend

Step 2: Install Luigi Client

npm install @luigi-project/client

Step 3: Initialize Luigi Client

// src/main.ts (Angular) or equivalent entry point
import LuigiClient from '@luigi-project/client';

// Initialize Luigi Client
LuigiClient.addInitListener(() => {
  console.log('Luigi Client initialized');

  // Get context from portal
  const context = LuigiClient.getContext();
  console.log('Current context:', context);
});

Step 4: Create Content Configuration

# Create configuration file
mkdir -p public/assets
cat > public/assets/content-configuration.json << 'EOF'
{
  "luigiConfigFragment": {
    "nodes": [
      {
        "pathSegment": "my-app",
        "label": "My App",
        "urlSuffix": "/",
        "entityType": "global",
        "viewGroup": {
          "preloadUrl": false
        }
      }
    ]
  }
}
EOF

Step 5: Serve Application

npm start
# Application runs at http://localhost:4200
# Configuration available at http://localhost:4200/assets/content-configuration.json

Step 6: Enable in Portal (Development Mode)

  1. Open OpenMFP Portal
  2. Click user menu → Settings
  3. Navigate to Development tab
  4. Enable "Is Development Mode Active"
  5. Add configuration URL: http://localhost:4200/assets/content-configuration.json
  6. Save settings
  7. Refresh portal → Your micro frontend appears in navigation

Step 7: Use Luigi Client APIs

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

// Navigation
LuigiClient.linkManager().navigate('/another-app');

// Show alert
LuigiClient.uxManager().showAlert({
  text: 'Operation successful',
  type: 'success'
});

// Get context
const context = LuigiClient.getContext();
const projectId = context.projectId;

// Update title
LuigiClient.uxManager().setDocumentTitle('My Custom Title');

// Get event bus for cross-micro-frontend communication
LuigiClient.addCustomMessageListener('myEvent', (data) => {
  console.log('Received event:', data);
});

Use Cases & Benefits

When to Use OpenMFP

Perfect For:

1. Multi-Team Organizations - Different teams own different portal sections - Teams use different technology stacks - Independent deployment pipelines needed

2. Modular Portals - Admin dashboards with pluggable features - Customer portals with customizable modules - Internal tools consolidation

3. Gradual Migration - Migrate monolithic apps piece-by-piece - Introduce new frameworks alongside legacy code - A/B test new implementations

4. Multi-Tenant Platforms - Different tenants get different feature sets - Dynamic feature enablement - White-label portals

5. Marketplace Applications - Third-party extensions - Plugin ecosystems - App stores

Key Benefits

Advantages of OpenMFP

For Developers:

  • Technology Freedom: Use React, Angular, Vue, or anything else
  • Independent Deployment: Deploy when ready, no coordination
  • Simplified Integration: Configuration file is all you need
  • Context Access: Automatic access to user, entity, and app context
  • Developer Mode: Test locally without deploying

For Organizations:

  • Team Autonomy: Teams work independently, reducing bottlenecks
  • Reduced Complexity: No shared codebase means fewer conflicts
  • Faster Innovation: Experimental features can be developed in parallel
  • Better Scalability: Applications scale independently
  • Reusability: Micro frontends can be reused across portals

For Users:

  • Consistent Experience: Unified navigation and authentication
  • Fast Performance: Lazy loading of features on demand
  • Rich Functionality: Best-of-breed features from different teams
  • Seamless Integration: No visible boundaries between micro frontends

Technical Stack

Technologies Used

OpenMFP Portal:

  • Frontend: Angular, TypeScript
  • Micro Frontend Framework: Luigi Framework (SAP)
  • Build: Webpack, npm
  • Containerization: Docker
  • Languages: TypeScript (48.7%), JavaScript (38.5%), HTML, SCSS

Luigi Framework:

  • Core: JavaScript/TypeScript
  • Isolation: Iframe-based
  • Communication: postMessage API
  • Routing: Hash or path-based
  • Authentication: Pluggable providers

Micro Frontend Requirements:

  • Required: Luigi Client library (@luigi-project/client)
  • Optional: Any web framework (React, Angular, Vue, Svelte, vanilla JS)
  • Hosting: Any web server capable of serving static files

Best Practices

Configuration Best Practices

1. Use Entity Types Wisely

  • global - Always visible (Home, Settings)
  • organization - Visible within organization context
  • project - Visible within project context
  • Custom types for domain-specific hierarchies

2. Leverage i18n

  • Always use {{key}} syntax for labels
  • Provide translations in texts array
  • Include all supported locales

3. Design for Context

  • Use visibleForEntityContext to show features only where relevant
  • Implement titleResolver for dynamic, data-driven titles
  • Use JMESPath queries for complex visibility logic

4. Optimize Loading

  • Set preloadUrl: false for rarely-used features
  • Use viewGroup to batch-load related micro frontends
  • Implement lazy loading in your micro frontend

5. Secure Iframes

  • Specify minimal requiredIFramePermissions
  • Use sandbox attribute to restrict capabilities
  • Validate all context data received from portal

6. Handle Missing Configuration

  • Provide configurationHint and configurationLink for setup-required features
  • Use configurationMissing to hide incomplete features gracefully

7. Version Your Configuration

  • Treat configuration as code
  • Use semantic versioning
  • Test configuration changes in development mode first

Common Patterns

Pattern 1: Dashboard with Widgets

{
  "nodes": [
    {
      "pathSegment": "dashboard",
      "label": "Dashboard",
      "entityType": "project",
      "defineSlot": "dashboard-widgets",
      "children": [
        {
          "pathSegment": "widget-cpu",
          "label": "CPU Metrics",
          "navSlot": "dashboard-widgets",
          "urlSuffix": "/widgets/cpu"
        },
        {
          "pathSegment": "widget-memory",
          "label": "Memory Metrics",
          "navSlot": "dashboard-widgets",
          "urlSuffix": "/widgets/memory"
        }
      ]
    }
  ]
}

Pattern 2: Feature-Toggled Beta Features

{
  "nodes": [
    {
      "pathSegment": "analytics",
      "label": "Analytics (Beta)",
      "urlSuffix": "/analytics",
      "visibleForFeatureToggles": ["analytics-beta"],
      "icon": "lab"
    }
  ]
}

Pattern 3: Conditional Admin Features

{
  "nodes": [
    {
      "pathSegment": "admin",
      "label": "Administration",
      "urlSuffix": "/admin",
      "visibleForContext": "user.roles[?contains(@, 'admin')]",
      "children": [
        {
          "pathSegment": "users",
          "label": "User Management",
          "urlSuffix": "/admin/users"
        }
      ]
    }
  ]
}

Pattern 4: Dynamic Entity Navigation

{
  "nodes": [
    {
      "pathSegment": "organizations",
      "label": "Organizations",
      "entityType": "global",
      "defineEntity": {
        "id": "organization",
        "contextKey": "organizationId",
        "dynamicFetchId": "workspace"
      },
      "children": [
        {
          "pathSegment": ":organizationId",
          "titleResolver": {
            "request": {
              "url": "/api/organizations/${organizationId}"
            },
            "titlePropertyChain": "data.displayName",
            "fallbackTitle": "Organization"
          },
          "children": [
            {
              "pathSegment": "projects",
              "label": "Projects",
              "entityType": "organization"
            }
          ]
        }
      ]
    }
  ]
}

Common Pitfalls

Pitfall 1: Hardcoded URLs

Avoid Hardcoded Paths

Problem: Hardcoding navigation URLs like /projects/123/dashboard makes micro frontends brittle when portal structure changes.

Solution: Use intent-based navigation:

// Instead of this:
window.location.href = '/projects/123/dashboard';

// Do this:
LuigiClient.linkManager().navigate('/dashboard');
// Or semantic navigation:
LuigiClient.linkManager().navigate({
semanticObject: 'project',
action: 'view',
params: { projectId: '123' }
});

Pitfall 2: Context Assumptions

Validate Context Data

Problem: Assuming context properties exist without validation causes runtime errors.

Solution: Always validate context:

const context = LuigiClient.getContext();
if (!context.projectId) {
console.error('No project context available');
return;
}

Pitfall 3: Iframe Communication

Don't Bypass Luigi Client

Problem: Using window.parent.postMessage() directly breaks encapsulation and Luigi's security model.

Solution: Always use Luigi Client APIs:

// Wrong:
window.parent.postMessage({ action: 'navigate', path: '/home' }, '*');

// Correct:
LuigiClient.linkManager().navigate('/home');

Pitfall 4: Missing Configuration Validation

Test Configuration

Problem: Deploying invalid configuration breaks portal navigation.

Solution: - Use development mode to test configuration locally - Validate JSON schema before deployment - Implement CI/CD checks for configuration files - Monitor portal logs for configuration errors

Pitfall 5: Over-Nesting Navigation

Keep Navigation Shallow

Problem: Deep navigation hierarchies (5+ levels) create poor UX and complex URLs.

Solution: Keep navigation 3 levels maximum:

✅ Good: /organizations/acme/projects
❌ Bad: /organizations/acme/divisions/west/teams/alpha/projects/proj1

Pitfall 6: Localization Gaps

Missing Translations

Problem: Forgetting to provide translations for all locales shows raw keys like {{dashboard}} to users.

Solution: Always provide complete translation sets:

"texts": [
{ "locale": "en", "textDictionary": { "dashboard": "Dashboard" } },
{ "locale": "de", "textDictionary": { "dashboard": "Dashboard" } },
{ "locale": "fr", "textDictionary": { "dashboard": "Tableau de bord" } }
]

Monitoring & Debugging

Troubleshooting Tools

Development Mode

Enable development mode in portal settings to:

  • Load local micro frontends without deployment
  • See configuration validation errors
  • Test changes in real-time
  • Debug context passing

Browser DevTools

Console Logs:

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

Inspect Context:

// In micro frontend console
LuigiClient.getContext();

Check Configuration: Access configuration endpoint directly:

https://your-app.com/assets/content-configuration.json

Common Issues

Issue Symptoms Resolution
Micro frontend not appearing Missing from navigation Check entityType matches current context; verify configuration URL is accessible
Blank iframe White screen in portal Check micro frontend CORS headers; ensure Luigi Client is initialized
Navigation not working Clicks don't route Verify pathSegment is unique; check for conflicting routes
Context is empty LuigiClient.getContext() returns {} Ensure addInitListener is used; check portal sends context correctly
Permissions denied Features not accessible Review visibleForContext and visibleForFeatureToggles settings
i18n keys showing Raw {{key}} in UI Add translations to texts array for all locales

Integration with Platform Mesh

Kubernetes Native Usage

When used within Platform Mesh, OpenMFP integrates with the Extension Manager Operator for Kubernetes-native micro frontend management.

Integration Points:

  1. Extension CRDs: Micro frontends defined as Kubernetes resources
  2. ContentConfiguration: Stored in ConfigMaps or inline in CRDs
  3. Dynamic Discovery: Portal fetches configurations from Extension Manager API
  4. Entity Sync: Account and Organization entities from Account Controller
  5. Authorization: Permission checks via Security Operator / OpenFGA

See Portal Mesh Integration for details on Platform Mesh-specific usage.

  • Portal Mesh Integration - How Platform Mesh uses OpenMFP
  • Extension Controller - Kubernetes operator for micro frontend management
  • Account Controller - Entity model and hierarchies

Further Resources

Official Documentation

NeoNephos Foundation

Repositories

Learning Resources

Community

  • Zulip Chat: Real-time discussions with OpenMFP community
  • Monthly Community Calls: Join NeoNephos community meetings
  • Mailing Lists: Subscribe for announcements and technical discussions

Open Questions / TODOs

  • [ ] Document performance benchmarks for large numbers of micro frontends
  • [ ] Add examples for custom Luigi authorization providers
  • [ ] Clarify update strategies when configuration schemas change
  • [ ] Document A/B testing patterns with feature toggles

Sources: