Skip to content

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.

hvconfig.json
{
  "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:

remoteEntry.js
var _your_module_id;(()=>{"use strict";var __webpack_modules_....

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:

mf-manifest.json
{
  "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
    const App = (customProps: Readonly<BaseProps>) => {
        return (
            <ModuleComponentThatUsesCustomProps props={customProps}>
        );
    };

    export default App;
By adhering to this approach, your module will consistently receive the necessary platform properties and be embedded as intended within HortiView.

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:

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.