JSPM

  • Created
  • Published
  • Downloads 21
  • Score
    100M100P100Q92077F
  • License MIT

React utilities for dynamically loading federated React modules from react-federation-service

Package Exports

  • @pawells/react-federation-core
  • @pawells/react-federation-core/client
  • @pawells/react-federation-core/components
  • @pawells/react-federation-core/context
  • @pawells/react-federation-core/errors
  • @pawells/react-federation-core/hooks
  • @pawells/react-federation-core/loaders

Readme

@pawells/react-federation-core

npm License

React client library for consuming a Module Federation registry service. Provides a context provider, hooks, and components for discovering, loading, and rendering federated remote modules inside a React application.

Installation

yarn add @pawells/react-federation-core

Setup

Wrap your application with FederationProvider and supply a FederationConfig.

import { FederationProvider, ScriptInjectionLoader } from '@pawells/react-federation-core';

function Root() {
    return (
        <FederationProvider
            config={{
                serviceUrl: 'https://api.example.com/api/v1',
                auth: { bearerTokenFn: () => getAccessToken() },
            }}
        >
            <App />
        </FederationProvider>
    );
}

FederationConfig

Option Type Default Description
serviceUrl string Required. Base URL of the federation service including version path, e.g. https://api.example.com/api/v1
auth FederationAuthConfig Authentication credentials (see below)
requestTimeout number 10000 HTTP request timeout in milliseconds
loader ModuleLoader ScriptInjectionLoader Module loader strategy (see Loaders section)

FederationAuthConfig — exactly one of:

{ bearerToken: string }                          // static token
{ bearerTokenFn: () => string | Promise<string> } // dynamic token (e.g. from useAuth)
{ apiKey: string }                               // API key header

Loaders

The loader determines how remote entry files are fetched and how modules are extracted from the container.

Loader Use when
ScriptInjectionLoader Remote is built with Webpack 5 Module Federation (default). Injects a <script> tag and reads the container from window[scope].
ViteModuleLoader Remote is built with Vite + vite-plugin-federation. Polls window[scope] after script injection.
NativeEsmLoader Remote is a plain ES module with no Webpack or Vite federation runtime. Uses native import().
import { FederationProvider, ViteModuleLoader } from '@pawells/react-federation-core';

<FederationProvider config={{ serviceUrl: '...', loader: new ViteModuleLoader() }}>
    <App />
</FederationProvider>

ScriptInjectionLoader accepts an optional { scriptLoadTimeout?: number } (default 10000 ms).

Components

RemoteComponent

Convenience component that loads and renders a federated module with built-in Suspense and error boundary handling.

import { RemoteComponent } from '@pawells/react-federation-core';

<RemoteComponent
    moduleId="ui-module@1.0.0"
    exposedPath="./Button"
    componentProps={{ label: 'Click me' }}
    fallback={<Spinner />}
    errorFallback={<p>Failed to load module</p>}
/>
Prop Type Default Description
moduleId string Required. Module ID ("name@version") or module name for the latest version
exposedPath string Required. Exposed path, e.g. "./Button"
componentProps Record<string, unknown> Props forwarded to the loaded component
fallback React.ReactNode null Rendered while the module is loading (Suspense fallback)
errorFallback ReactNode | (error, reset) => ReactNode 'Failed to load remote module' Rendered when loading fails
onError (error, info) => void Called when an error is caught by the boundary

RemoteErrorBoundary

Class-based error boundary for remotely-loaded modules. Place it outside the Suspense boundary that wraps lazy remote components. Supports a render-function fallback that receives the error and a reset callback.

import { RemoteErrorBoundary } from '@pawells/react-federation-core';

<RemoteErrorBoundary
    fallback={(error, reset) => (
        <div>
            <p>Error: {error.message}</p>
            <button onClick={reset}>Retry</button>
        </div>
    )}
>
    <Suspense fallback={<Spinner />}>
        <LazyRemoteButton />
    </Suspense>
</RemoteErrorBoundary>

Hooks

useFederatedModule

Loads a federated module and returns a stable React.lazy component reference. Metadata and component references are cached in the FederationContext to prevent duplicate requests and Suspense resets.

import { useFederatedModule } from '@pawells/react-federation-core';

function RemoteButtonWrapper() {
    const LazyButton = useFederatedModule('ui-module@1.0.0', './Button');

    return (
        <Suspense fallback={<Spinner />}>
            <LazyButton label="Click me" />
        </Suspense>
    );
}

useFederationRegistry

Searches and lists modules in the federation registry. Supports filtering, pagination, and manual re-fetching.

import { useFederationRegistry } from '@pawells/react-federation-core';

const { modules, total, isLoading, error, refetch, stats, fetchStats } =
    useFederationRegistry({ query: 'header', limit: 20 });
Option Type Description
query string Full-text search query
type string Filter by module type
limit number Max results per page
offset number Pagination offset
immediate boolean Fetch on mount. Defaults to true

useFederationContext

Returns the raw FederationContextValue (client, loader, caches) from the nearest FederationProvider. Throws if called outside a provider. Useful when building custom loader or registry integrations.

Error handling

All errors thrown by the library extend FederationError and carry a machine-readable code property.

Class When thrown
FederationError Base class for all library errors
ModuleLoadError The remote entry loaded but the exposed module could not be retrieved
ModuleNotFoundError The module ID was not found in the registry or has no remoteEntry
ServiceConnectionError The federation service is unreachable or returned a non-2xx response
import { ModuleLoadError, ModuleNotFoundError } from '@pawells/react-federation-core';

try {
    // ...
} catch (error) {
    if (error instanceof ModuleNotFoundError) {
        console.warn('Module not registered:', error.message);
    } else if (error instanceof ModuleLoadError) {
        console.error('Load failed:', error.message, error.cause);
    }
}

Failed module entries are automatically removed from the component cache so the next render retries the load.