Build a replacement module

Ask about this Page
Copy for LLM
View as Markdown

Build a Module Federation external module that plugs into the InStore State module and replaces a built-in component of the InStore POS.

This page covers the setup that every replacement module needs. For the contract of the component you replace, see Implement the Cart module.

Prerequisites

Before you build a replacement module, make sure you have the following:

  • A webpack build that uses @module-federation/enhanced.
  • React 18 and React Router 6.
  • A host that serves your built files over HTTP or HTTPS, reachable from the browser that runs the POS.
  • Administration rights on the tenant, so that you can register the module. See Run POS API requests.

Configure Module Federation

Configure your module as an external module that the POS loads at runtime and as a host that consumes the InStore State module. Configure both roles in one ModuleFederationPlugin block:
webpack.config.jsJavascript
const {
  ModuleFederationPlugin,
} = require('@module-federation/enhanced/webpack');
const deps = require('./package.json').dependencies;

new ModuleFederationPlugin({
  name: 'instoreCart',
  filename: 'remoteEntry.js',
  remotes: {
    instoreState:
      'instoreState@https://{staticModulesHost}/InStore_State/remoteEntry.js',
  },
  exposes: {
    './CartApp': './src/App',
  },
  shared: {
    react: { singleton: true, requiredVersion: deps.react },
    'react-dom': { singleton: true, requiredVersion: deps['react-dom'] },
    'react-redux': { singleton: true, requiredVersion: false },
    '@reduxjs/toolkit': { singleton: true, requiredVersion: false },
    'react-router-dom': {
      singleton: true,
      requiredVersion: deps['react-router-dom'],
    },
  },
});
Replace {staticModulesHost} with the static modules host for your environment.
The sample uses the values for a Cart replacement, whose contract is described in Implement the Cart module. The name and the exposes key are yours to choose, and you register them as scope and module.
Every entry in shared must be a singleton. The InStore State module runs on React hooks and Redux, so a second copy of any of these libraries breaks it.

Three of these options become values that you register for the module:

Plugin optionExternal module field
exposes keymodule
filename, as served by your hosturl
namescope

Resolve types

The InStore State module publishes TypeScript types for everything it exposes. To resolve them, choose one of the following:

  • Map instoreState/* to the state module source in the paths section of your tsconfig.json.
  • Consume the types that the state module generates, through the dts.consumeTypes.remoteTypeUrls option of ModuleFederationPlugin.

Read shared POS state

The POS mounts its context providers around every route. Your module reads those contexts directly, and doesn't mount its own provider.

Every replacement module can read the following:

  • instoreState/locale provides useLocalizationContext, which returns the translation function t.
  • instoreState/configuration/context provides useConfigurationContext, which returns navigate.

Your module renders inside the routes of the POS, so define your own routes as relative paths.

Register the module

Build your module and serve the generated remoteEntry.js at a URL that the POS browser can reach. Register it for your tenant, then assign it to a location or to a workstation. For the record fields and for the way InStore chooses between assignments, see Replace POS UI modules.

Next steps

Use the following resources to continue developing your module: