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:
- Website: openmfp.org
- Documentation: openmfp.org/documentation
- NeoNephos YouTube: Video resources and presentations
- Blog: Updates and technical articles
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
Navigation Node Properties¶
| 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:
- Extracts
projectId=123from URL - Requests extensions with
entityType="project"for workspace scope - 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:
- Create application with any framework (Angular, React, Vue, etc.)
- Install Luigi Client library:
npm install @luigi-project/client - Add Luigi Client to application for context access and navigation
- Create
content-configuration.jsonin public/assets folder - Define navigation nodes, entity types, and visibility rules
2. Registration Phase¶
Kubernetes/Extension Manager (Platform Mesh scenario):
- Create Extension CRD with configuration URL
- Extension Manager validates configuration
- Configuration exposed via API endpoint
OR Standalone:
- Host configuration file at
https://my-app.com/assets/content-configuration.json - Register URL in portal configuration
3. Runtime Phase¶
OpenMFP Portal:
- Fetch: Portal requests all content configurations from Extension Manager API or direct URLs
- Validate: Checks configuration schema and permissions
- Merge: Combines all configurations into unified Luigi navigation tree
- Render: Builds navigation menu and routing table
- Route: User navigates to
/my-app→ Portal loads micro frontend in iframe - Context: Portal injects entity context (project ID, user info, etc.) into micro frontend
- 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¶
Step 1: Create Application¶
Step 2: Install Luigi 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)¶
- Open OpenMFP Portal
- Click user menu → Settings
- Navigate to Development tab
- Enable "Is Development Mode Active"
- Add configuration URL:
http://localhost:4200/assets/content-configuration.json - Save settings
- 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 contextproject- Visible within project context- Custom types for domain-specific hierarchies
2. Leverage i18n
- Always use
{{key}}syntax for labels - Provide translations in
textsarray - Include all supported locales
3. Design for Context
- Use
visibleForEntityContextto show features only where relevant - Implement
titleResolverfor dynamic, data-driven titles - Use JMESPath queries for complex visibility logic
4. Optimize Loading
- Set
preloadUrl: falsefor rarely-used features - Use
viewGroupto batch-load related micro frontends - Implement lazy loading in your micro frontend
5. Secure Iframes
- Specify minimal
requiredIFramePermissions - Use
sandboxattribute to restrict capabilities - Validate all context data received from portal
6. Handle Missing Configuration
- Provide
configurationHintandconfigurationLinkfor setup-required features - Use
configurationMissingto 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:
Pitfall 2: Context Assumptions¶
Validate Context Data
Problem: Assuming context properties exist without validation causes runtime errors.
Solution: Always validate context:
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:
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:
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:
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:
Inspect Context:
Check Configuration: Access configuration endpoint directly:
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:
- Extension CRDs: Micro frontends defined as Kubernetes resources
- ContentConfiguration: Stored in ConfigMaps or inline in CRDs
- Dynamic Discovery: Portal fetches configurations from Extension Manager API
- Entity Sync: Account and Organization entities from Account Controller
- Authorization: Permission checks via Security Operator / OpenFGA
See Portal Mesh Integration for details on Platform Mesh-specific usage.
Related Topics¶
- 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: