Feature Hub

Feature Hub

  • API
  • GitHub

›Guides

Getting Started

  • Introduction
  • Motivation
  • Demos

Guides

  • Integrating the Feature Hub
  • Writing a Feature App
  • Writing a Feature Service
  • Sharing the Browser History
  • Sharing npm Dependencies
  • Custom Logging
  • Server-Side Rendering
  • Feature App in Feature App
  • Reducing the Bundle Size

Help

  • FAQ
Edit

Writing a Feature App

A Feature App is described by a consumer definition object. It consists of optional dependencies and optionalDependencies objects, and a create method:

const myFeatureAppDefinition = {
  dependencies: {
    featureServices: {
      'acme:some-feature-service': '^2.0.0',
    },
    externals: {
      react: '^16.7.0',
    },
  },

  optionalDependencies: {
    featureServices: {
      'acme:optional-feature-service': '^1.3.0',
    },
  },

  create(env) {
    // ...
  },
};

If a Feature App module is to be loaded asynchronously with the FeatureAppManager, it must provide a definition object as its default export:

export default myFeatureAppDefinition;

dependencies

The dependencies map can contain two types of required dependencies:

  1. With dependencies.featureServices all required Feature Services are declared. If one of those dependencies can't be fulfilled, the Feature App won't be created. This means the Feature App can be sure that those dependencies are always present when it is created.

    Feature Service dependencies are declared with their ID as key, and a semver version range as value, e.g. {'acme:some-feature-service': '^2.0.0'}. Since Feature Services only provide the latest minor version for each major version, a caret range should be used here. If instead an exact version or a tilde range is used, this will be coerced to a caret range by the Feature ServiceRegistry.

  2. With dependencies.externals all required external dependencies are declared. This may include shared npm dependencies that are provided by the integrator.

    External dependencies are declared with their external name as key, and a semver version range as value, e.g. {react: '^16.7.0'}.

optionalDependencies

The optionalDependencies.featureServices map contains all Feature Service dependencies for which the Feature App handles their absence gracefully. If one of those dependencies can't be fulfilled, the FeatureServiceRegistry will only log an info message.

Feature Service dependencies are declared with their ID as key, and a semver version range as value, e.g. {'acme:some-feature-service': '^2.0.0'}. Since Feature Services only provide the latest minor version for each major version, a caret range should be used here. If instead an exact version or a tilde range is used, this will be coerced to a caret range by the Feature ServiceRegistry.

Note:
Optional external dependencies (i.e. optionalDependencies.externals) are not yet supported (see #245).

create

The create method takes the single argument env, which has the following properties:

  1. config — A config object that is provided by the integrator1:

    const myFeatureAppDefinition = {
      create(env) {
        const {foo} = env.config;
    
        // ...
      },
    };
    
  2. featureServices — An object of required Feature Services that are semver-compatible with the declared dependencies in the Feature App definition:

    const myFeatureAppDefinition = {
      dependencies: {
        featureServices: {
          'acme:some-feature-service': '^2.0.0',
        },
      },
    
      create(env) {
        const someFeatureService =
          env.featureServices['acme:some-feature-service'];
    
        someFeatureService.foo(42);
    
        // ...
      },
    };
    
  3. featureAppId — The ID that the integrator1 has assigned to the Feature App instance. This ID is used as a consumer ID for binding the required Feature Services to the Feature App.

  4. featureAppName — The name that the integrator1 might have assigned to the Feature App. This name is used as a consumer name for binding the required Feature Services to the Feature App. In contrast to the featureAppId, the name must not be unique. It can be used by required Feature Services for display purposes, logging, looking up Feature App configuration meta data, etc.

  5. baseUrl — A base URL to be used for referencing the Feature App's own resources. It is only set in the env if the integrator1 has defined a baseUrl on the corresponding FeatureAppLoader or FeatureAppContainer.

  6. done — A callback that the integrator1 might have defined. A short-lived Feature App can call this function when it has completed its task, thus giving the integrator1 a hint, that it can be removed. For example, if the Feature App was opened in a layer, the integrator1 could close the layer when done() is called.

The return value of the create method can vary depending on the integration solution used. Assuming the @feature-hub/react package is used, a Feature App can be either a React Feature App or a DOM Feature App.

ownFeatureServiceDefinitions

A Feature App can also register its own Feature Services by declaring ownFeatureServiceDefinitions:

import {myFeatureServiceDefinition} from './my-feature-service';
const myFeatureAppDefinition = {
  dependencies: {
    featureServices: {
      'acme:my-feature-service': '^1.0.0',
    },
  },

  ownFeatureServiceDefinitions: [myFeatureServiceDefinition],

  create(env) {
    const myFeatureService = env.featureServices['acme:my-feature-service'];

    // ...
  },
};

This technique allows teams to quickly get Feature Apps off the ground, without being dependent on the integrator. However, as soon as other teams need to use this Feature Service, it should be published and included in the global set of Feature Services by the integrator.

Note:
If the Feature Service to be registered has already been registered, the new Feature Service is ignored and a warning is emitted.

Implementing a Feature App for an Integrator That Uses React

The @feature-hub/react package defines two interfaces, ReactFeatureApp and DomFeatureApp. A Feature App that implements one of these interfaces can be placed on a web page using the FeatureAppLoader or FeatureAppContainer components.

React Feature App

A React Feature App definition's create method returns a Feature App instance with a render method that itself returns a ReactNode:

const myFeatureAppDefinition = {
  create(env) {
    return {
      render() {
        return <div>Foo</div>;
      },
    };
  },
};

Note:
Since this element is directly rendered by React, the standard React lifecyle methods can be used (if render returns an instance of a React ComponentClass).

DOM Feature App

A DOM Feature App allows the use of other frontend technologies such as Vue.js or Angular, although it is placed on a web page using React. Its definition's create method returns a Feature App instance with an attachTo method that accepts a DOM container element:

const myFeatureAppDefinition = {
  create(env) {
    return {
      attachTo(container) {
        container.innerText = 'Foo';
      },
    };
  },
};

The attachTo method of a DOM Feature App can optionally return a "detach" function. This function is invoked when the Feature App is removed from the DOM, allowing it to perform necessary cleanup tasks (e.g. unsubscribing from events). This behavior is similar to how the useEffect hook works in React.

const myFeatureAppDefinition = {
  create(env) {
    return {
      attachTo(container) {
        const app = Vue.createApp({
          template: '<div>Hello world!</div>',
        });
        app.mount(container);

        return function () {
          // unsubscribe from events
          window.removeEventListener('resize', handleResize);

          // or unmount apps
          app.unmount();
        };
      },
    };
  },
};

Loading UIs provided by the React Integrator

Both kinds of Feature Apps can specify a loading stage for Feature Apps, which are used to allow an integrator1 to hide an already rendered Feature App visually and display a custom loading UI instead. This feature is for client-side rendering only.

A Feature App can declare this loading stage by passing a Promise in the object returned from their create function with the key loadingPromise. Once the promise resolves, the loading is considered done. If it rejects, the Feature App will be considered as crashed, and the integrator1 can use the rejection payload to display a custom Error UI.

const myFeatureAppDefinition = {
  create(env) {
    const dataPromise = fetchDataFromAPI();

    return {
      loadingPromise: dataPromise,

      render() {
        return <App dataPromise={dataPromise}>;
      }
    };
  }
};

Note:
If you want the rendered App to control when it is done loading, you can pass the promise resolve and reject functions into the App using your render method. An example for this is implemented in the react-loading-ui demo.

Note:
If a similar loading stage (after rendering started) is needed for server-side rendering, for example to wait for a data layer like a router to resolve all dependencies, it can be implemented using the @feature-hub/async-ssr-manager's scheduleRerender API.

Implementing a Feature App for an Integrator That Uses Web Components

If the targeted integrator is using the @feature-hub/dom package, a Feature App needs to implement the DomFeatureApp interface that the package defines. Since this interface is compatible with the DomFeatureApp interface defined by @feature-hub/react, this Feature App will also be compatible with an integrator that uses React.

A DOM Feature App allows the use of arbitrary frontend technologies such as Vue.js, Angular, or React, and is placed on the web page using Web Components. The Feature App will automatically be enclosed in its own shadow DOM. Its definition's create method returns a Feature App instance with an attachTo method that accepts a DOM container element:

const myFeatureAppDefinition = {
  create(env) {
    return {
      attachTo(container) {
        container.innerText = 'Foo';
      },
    };
  },
};

Bundling a Feature App

For the FeatureAppManager to be able to load Feature Apps from a remote location, Feature App modules must be bundled. The module type of a Feature App bundle must be chosen based on the provided module loaders of the integrators it intends to be loaded into.

Client Bundles

Out of the box, the Feature Hub provides two client-side module loaders.

AMD Module Loader

To build an AMD Feature App module bundle, any module bundler can be used that can produce an AMD or UMD bundle.

How npm dependencies can be shared using AMD is described in the "Sharing npm Dependencies" guide.

Webpack Module Federation Loader

To build a federated module, Webpack must be used as module bundler.

Here is an example of a Webpack config for a federated Feature App module:

module.exports = {
  entry: {}, // intentionally left empty
  output: {
    filename: 'some-federated-feature-app.js',
    publicPath: 'auto',
  },
  plugins: [
    new webpack.container.ModuleFederationPlugin({
      name: '__feature_hub_feature_app_module_container__',
      exposes: {
        featureAppModule: path.join(__dirname, './some-feature-app'),
      },
    }),
  ],
};

How npm dependencies can be shared using Webpack Module Federation is described in the "Sharing npm Dependencies" guide.

There are two important naming conventions a Feature App's Webpack config must follow:

  1. The name of the remote Feature App module container must be '__feature_hub_feature_app_module_container__'.
  2. The Feature App module (containing the Feature App definition as default export) must be exposed by the container as featureAppModule.

Server Bundles

To build a CommonJS Feature App module bundle for server-side rendering, any module bundler can be used that can produce a CommonJS or UMD bundle. The target of this bundle must be Node.js.

How npm dependencies can be shared on the server is described in the "Sharing npm Dependencies" guide.


  1. The "integrator" in this case can also be another Feature App.
Last updated on 1/27/2025 by Feature Hub CI
← Integrating the Feature HubWriting a Feature Service →
  • dependencies
  • optionalDependencies
  • create
  • ownFeatureServiceDefinitions
  • Implementing a Feature App for an Integrator That Uses React
    • React Feature App
    • DOM Feature App
    • Loading UIs provided by the React Integrator
  • Implementing a Feature App for an Integrator That Uses Web Components
  • Bundling a Feature App
    • Client Bundles
    • Server Bundles
Copyright (c) 2018-2025 Accenture Song Build Germany GmbH