Exposed Components¶
In the context of Module Federation, an exposed component is any part of a module that is made available for dynamic loading by the host application. Instead of bundling all components into the host at build time, Module Federation allows modules to declare which components can be imported at runtime. This mechanism enables HortiView to load modules and their features on demand, reducing initial load size and supporting a highly modular architecture.
HortiView itself is the shell, also called main application that uses module federation to load other modules on runtime, whenever they are needed.
Multiple Components¶
A single module can expose one or many components. The exposed components are defined in the module’s configuration (usually in the ModuleFederationPlugin setup) and referenced by unique keys. For example, a module might expose:
- A main component, which represents the primary UI or logic of the module - this component is your actual MODULE. The main solution, that a farmer uses within HortiView
- Additional components or utilities, which can be invoked by the platform for specific tasks or events - those components are mainly used for HortiView Events
This flexibility allows HortiView to treat modules not only as standalone features but also as providers of event components, that can be executed by HortiView itself on specific trigger conditions without the use to actually open the module.
{
"name": "_your_module_id",
"filename": "remoteEntry.js",
"exposes": {
"./ExposedComponentName": "./src/Template/RemoteModule",
"./TriggerOnPlatformLoaded": "./src/Template/events/TriggerOnPlatformLoaded/Remote"
}
}
mf-manifest.json¶
When providing more than one component, its required to provide an mf-manifest.json (that will be provided per default when using the ModuleTemplate).
This manifest acts as a registry for the platform, mapping component names to their remote locations. When the platform needs to render a module or execute a specific feature, it consults the manifest, retrieves the correct remote entry, and dynamically imports the exposed component.
The manifest-file will be the entry-point of a module and replace the default remoteEntry.js, that can only be used, when ONE component is exposed.
After building a module, there will be both files available in your build-folder:
The remoteEntry.js simply contains a minified version of the entry point to your components, similar a an index.html:
The mf-manifest.json serves as a blueprint for how components are exposed. It allows us to verify that your module is correctly configured, precache all relevant module files, and provides detailed insights into the structure of both main and event components:
{
"id": "_your_module_id",
"name": "_your_module_id",
"metaData": {
},
"shared": [
...
],
"remotes": [],
"exposes": [
{
"id": "_your_module_id:ExposedComponentName",
"name": "ExposedComponentName",
...
"path": "./ExposedComponentName"
},
{
"id": "_your_module_id:TriggerOnPlatformLoaded",
"name": "TriggerOnPlatformLoaded",
...
"path": "./TriggerOnPlatformLoaded"
}
]
}
Recommendation: Always Provide mf-manifest.json
It is recommended to always include the mf-manifest.json file, even if your module exposes only a main component. The manifest offers richer metadata and improved integration with the Module Federation shell compared to the default remoteEntry.js, ensuring better maintainability and platform compatibility.
Exposed Components¶
By convention, exposed components should provide a default export. This makes loading straightforward: the platform imports the component and calls its default export to render or execute it. Technically, the platform invokes (importedModule/exposedComponent).default() to initialize the component.
| RemoteModule.tsx | |
|---|---|
Additionally, HortiView supports exposing named functions within a component. In these cases, the platform invokes the specific function directly—such as (importedModule/exposedComponent).functionName()—rather than calling the default export. This pattern is particularly effective for lightweight operations or event-driven logic that do not require rendering a full UI component.
Only for event components
The exposing of functions is only support for the defined HortiView Events. For more information check out the HortiView Events Documentation
Named functions need to be provided in a special exposed component called "./Functions" to work.
In future there might be additional usecases based on this concept, like tiny modules and other.
Main Component¶
The main component is the standard entry point of the module. It is typically exposed under a name like ./ExposedComponentName and is considered the module’s primary interface. When a user navigates to a module in HortiView, the platform loads this main component by default. This ensures a consistent experience: every module has a clear entry point that represents its core functionality.
Event Components¶
Event Components are a special category of exposed components designed for event-driven functionality rather than full module rendering.
While the main component of a module represents its primary interface, Event Components allow the platform to trigger specific actions or workflows without loading the entire module UI. These components are typically lightweight and focus on tasks such as:
- Executing background logic
- Handling lifecycle events (e.g., OnPlatformLoaded)
- Responding to user or system actions that do not require a full page view
Event Components follow the same exposure mechanism as other components in Module Federation. They can be invoked by the platform using their exposed name, and they usually provide either:
- A default export, which the platform calls directly - this export needs to follow our name conventions for HortiView Events
- Named functions for granular control, allowing the platform to execute only the required logic - those also needs to follow our name conventions for HortiView Events to be supported by HortiView
In HortiView, this approach is used to implement HortiView Events, enabling modular, on-demand execution of features like data synchronization, analytics hooks, or mini-workflows. This design improves performance and flexibility by avoiding unnecessary UI rendering while still leveraging the dynamic loading capabilities of Module Federation.