Plugin-Host Composition Patterns
Framework M supports a highly flexible, multi-package UI architecture. This guide explains how to compose independently developed applications into a unified user experience, using a reference example of a business suite.
The Reference Suite (Example)
In this guide, we use the following example structure. Note that these are independent repositories/packages, not part of the framework core:
business-m: The "Host Shell". Provides the main layout, navigation, and workspace.wms,finance,people: "Plugins". Provide specific business logic and UI modules.
Zero-Cliff Progression
You can start simple and scale up to complex micro-frontends (MFEs) as your project grows.
1. The Default (Level 0)
When you build a basic app like wms, you don't need to build a custom UI host.
- Action: Install
framework-m-standard. - Result: Your app contributes DocTypes and backend logic. The UI is served by the default Desk shell bundled with Framework M.
2. Custom Branding & Shell (The business-m Pattern)
If you need a custom brand, sidebar, or global navigation (like business-m), you override the host shell.
- Backend: Register a
framework_m.shellentry point. - Frontend: Create a standalone React application that provides the shell.
# business-m: pyproject.toml
[project.entry-points."framework_m.shell"]
business_m = "business_m.frontend:shell"
The framework will now serve the static/ content from business-m as the main entry point (/desk/) instead of the default Desk.
3. Progressive MFE Scaffolding
As the suite grows, individual modules (wms, finance) can be scaffolded to contribute to the business-m shell without being part of the same repository.
- Action: Use
m new:app --with-frontendfor each plugin. - Scaffolding: This creates a "Universal" structure:
src/plugin.config.ts: Defines routes, menus, and components contributed to the shell.vite.config.mfe.ts: Configures the app to build as a Module Federation remote.
Pattern Replicated: Backend to Frontend
The relationship in the UI mirrors the backend exactly:
| Layer | Backend Pattern | UI Pattern |
|---|---|---|
| Host | business-m (litestar app + shell EP) | Main React App (MFE Host) |
| Plugin | wms (DocTypes + frontend EP) | MFE Remote (Federated into Host) |
4. Discovery & Composition
The "Glue" is the discovery API. The host shell (e.g., business-m) does not hardcode its plugins. On load, it calls:
GET /api/v1/frontend/remotes
The framework scans for all framework_m.frontend entry points (from wms, finance, etc.) and tells the shell where to find their remoteEntry.js.
CI/CD Pattern for App Developers
To ensure your MFE remotes are built correctly and compatible with the host, use the following CI/CD pattern in your application repository.
Template: build-mfe-assets (GitLab CI)
build-mfe-assets:
stage: build
image: node:20 # Use appropriate node version
script:
- cd frontend
- pnpm install
- pnpm build:mfe
artifacts:
paths:
- src/{{app_name}}/static/mfe/
expire_in: 1 week
verify-mfe-remote:
stage: test
needs: [build-mfe-assets]
script:
- pip install framework-m-studio # Install CLI
- m verify:mfe {{app_name}} --path src/{{app_name}}/static/mfe/
Building the Wheel
When building your Python wheel, ensure the static/mfe directory is included. Using hatchling (default):
# pyproject.toml
[tool.hatch.build.targets.wheel.force-include]
"src/{{app_name}}/static/mfe" = "{{app_name}}/static/mfe"
This pattern ensures that your app can be served via the Level 3 (PyPI) deployment method immediately upon installation, while still being ready for Level 5 (CDN) by syncing the static/mfe directory to your CDN.