@scalprum/react-core
v0.14.1
Published
React binding for @scalprum/core package.
Readme
@scalprum/react-core
React bindings for Scalprum module federation
The @scalprum/react-core package provides React-specific components, hooks, and utilities for building micro-frontend applications with Scalprum. It wraps the framework-agnostic @scalprum/core with an idiomatic React API.
Installation
npm install @scalprum/react-core @scalprum/core react react-domKey Features
- ScalprumProvider: Context provider for Scalprum configuration
- Component Loading: Declarative components for loading remote modules
- React Hooks: Modern hooks API for accessing remote modules
- Remote Hooks: Load and execute hooks from federated modules
- Prefetching Support: Built-in data prefetching capabilities
- Error Boundaries: Automatic error handling with self-repair
- TypeScript Support: Full type safety for remote modules
Quick Start
import { ScalprumProvider, ScalprumComponent } from '@scalprum/react-core';
const config = {
myApp: {
name: 'myApp',
manifestLocation: 'http://localhost:3001/plugin-manifest.json',
},
};
function App() {
return (
<ScalprumProvider config={config}>
<ScalprumComponent scope="myApp" module="MyComponent" fallback={<div>Loading...</div>} />
</ScalprumProvider>
);
}Core Components
ScalprumProvider
The root provider that initializes Scalprum and provides context to all child components.
import { ScalprumProvider } from '@scalprum/react-core';
function App() {
const config = {
remoteApp: {
name: 'remoteApp',
manifestLocation: 'http://localhost:3001/plugin-manifest.json',
},
};
const api = {
user: { id: '123', name: 'John' },
theme: 'dark',
};
return (
<ScalprumProvider config={config} api={api}>
{/* Your app */}
</ScalprumProvider>
);
}Props:
config: AppsConfig- Configuration for remote modulesapi?: T- Shared API context available to all modulespluginSDKOptions?- Optional plugin SDK configuration
ScalprumComponent
Declarative component for loading and rendering remote modules.
import { ScalprumComponent } from '@scalprum/react-core';
function Dashboard() {
return (
<ScalprumComponent
scope="analytics"
module="Dashboard"
fallback={<Skeleton />}
ErrorComponent={<ErrorBoundary />}
someProp="value" // Props are passed to remote component
/>
);
}Props:
scope: string- Remote container namemodule: string- Module name to loadimportName?: string- Specific export (default: 'default')fallback?- Loading fallback UIErrorComponent?- Custom error component- Additional props are forwarded to the remote component
Features:
- Automatic error boundaries
- Self-repair on cache errors
- Suspense integration
- Prefetch support
React Hooks
useScalprum
Access the Scalprum context and API.
import { useScalprum } from '@scalprum/react-core';
function MyComponent() {
const { config, api, initialized } = useScalprum();
if (!initialized) {
return <div>Initializing...</div>;
}
return <div>User: {api.user.name}</div>;
}
// Using optional selector for optimized re-renders
function OptimizedComponent() {
const api = useScalprum((state) => state.api);
return <div>User: {api.user.name}</div>;
}Parameters:
selector?: (state: ScalprumState) => T- Optional selector function to extract specific state
Returns:
config- Apps configurationapi- Shared API contextinitialized- Whether Scalprum is readypluginStore- Plugin store instance
useModule
Hook for loading remote modules programmatically.
import { useModule } from '@scalprum/react-core';
function WidgetContainer() {
// With @scalprum/remote-types configured, Widget's type comes from the remote declaration archive.
const Widget = useModule('widgets', './PieChart');
if (!Widget) {
return <div>Loading widget...</div>;
}
return <Widget data={chartData} />;
}Parameters:
scope: string- Remote container namemodule: string- Module namedefaultState?- Initial state while loadingimportName?: string- Export name (default: 'default')
Returns: The loaded module or defaultState
useLoadModule
Advanced hook for loading modules with more control.
import { useLoadModule } from '@scalprum/react-core';
function DataDisplay() {
const [DataTable, error] = useLoadModule(
{
scope: 'tables',
module: 'DataGrid',
},
undefined,
);
if (error) return <Error message={error.message} />;
if (!DataTable) return <Spinner />;
return <DataTable.default data={data} />;
}usePrefetch
Hook for prefetching data from remote modules.
import { usePrefetch } from '@scalprum/react-core';
function DataComponent() {
const { ready, data, error } = usePrefetch();
if (!ready) return <div>Loading data...</div>;
return <div>{JSON.stringify(data)}</div>;
}Remote Hooks
Scalprum supports loading and executing React hooks from federated modules, enabling advanced micro-frontend patterns.
RemoteHookProvider
The RemoteHookProvider is automatically included in ScalprumProvider - no additional setup required.
<ScalprumProvider config={config}>
{/* Remote hooks work automatically */}
<MyComponentUsingRemoteHooks />
</ScalprumProvider>useRemoteHook
Load and execute hooks from remote federated modules.
When @scalprum/remote-types is configured, use literal scope and module values. The generated declarations infer hook arguments and results; no local result interface or generic is needed.
import { useRemoteHook } from '@scalprum/react-core';
import { useMemo } from 'react';
function MyComponent() {
// IMPORTANT: Use useMemo when args contain objects/arrays
const args = useMemo(() => [{ initialValue: 0, step: 1 }], []);
const { hookResult, loading, error } = useRemoteHook({
scope: 'counter-app',
module: './useCounter',
args,
});
if (loading) return <div>Loading hook...</div>;
if (error) return <div>Error: {error.message}</div>;
return (
<div>
<p>Count: {hookResult?.count}</p>
<button onClick={hookResult?.increment}>+</button>
<button onClick={hookResult?.decrement}>-</button>
</div>
);
}Parameters:
scope: string- Federated module scopemodule: string- Module pathimportName?: string- Named export (optional)args?: any[]- Arguments to pass (must be memoized if containing objects/arrays)
Returns:
id: string- Unique hook instance IDloading: boolean- Loading stateerror: Error | null- Error if anyhookResult?: T- Hook execution result
Critical: When args contains objects or arrays, always use useMemo to prevent infinite re-renders.
useRemoteHookManager
Manage multiple remote hooks dynamically.
import { useRemoteHookManager } from '@scalprum/react-core';
import { useMemo } from 'react';
function DynamicHooksComponent() {
const manager = useRemoteHookManager();
const addCounter = () => {
const handle = manager.addHook({
scope: 'counter-app',
module: './useCounter',
args: [{ initialValue: 0, step: 1 }],
});
// Update args later
handle.updateArgs([{ initialValue: 10, step: 2 }]);
// Remove when done
// handle.remove();
};
const results = manager.hookResults;
return (
<div>
<button onClick={addCounter}>Add Counter Hook</button>
<div>Active hooks: {results.length}</div>
{results.map(({ id, hookResult, loading, error }) => (
<div key={id}>
{loading && <span>Loading...</span>}
{error && <span>Error: {error.message}</span>}
{hookResult && <span>Count: {hookResult.count}</span>}
</div>
))}
</div>
);
}Methods:
addHook(config)- Add a new remote hook, returns handlecleanup()- Remove all hooks (called automatically on unmount)hookResults- Results from all tracked hooks
Handle Methods:
remove()- Remove this specific hookupdateArgs(args)- Update hook arguments
For detailed remote hooks documentation, see:
Complete Example
import { ScalprumProvider, ScalprumComponent, useScalprum, useModule, useRemoteHook } from '@scalprum/react-core';
import { useMemo } from 'react';
// Configuration
const config = {
dashboard: {
name: 'dashboard',
manifestLocation: 'http://localhost:3001/plugin-manifest.json',
},
widgets: {
name: 'widgets',
manifestLocation: 'http://localhost:3002/plugin-manifest.json',
},
};
const api = {
user: { id: '123', name: 'John Doe' },
permissions: ['read', 'write'],
};
// Using declarative component
function DashboardView() {
return <ScalprumComponent scope="dashboard" module="MainDashboard" fallback={<div>Loading dashboard...</div>} />;
}
// Using hooks
function WidgetPanel() {
const { api } = useScalprum();
const ChartWidget = useModule('widgets', 'ChartComponent');
if (!ChartWidget) {
return <div>Loading widget...</div>;
}
return <ChartWidget user={api.user} />;
}
// Using remote hooks
function RemoteHookExample() {
const args = useMemo(() => [{ userId: '123' }], []);
const { hookResult, loading, error } = useRemoteHook({
scope: 'dashboard',
module: './useUserData',
args,
});
if (loading) return <div>Loading...</div>;
if (error) return <div>Error: {error.message}</div>;
return <div>Welcome, {hookResult?.name}!</div>;
}
// Main App
function App() {
return (
<ScalprumProvider config={config} api={api}>
<div>
<DashboardView />
<WidgetPanel />
<RemoteHookExample />
</div>
</ScalprumProvider>
);
}Error Handling
ScalprumComponent includes automatic error boundaries with self-repair:
import { ScalprumComponent } from '@scalprum/react-core';
function CustomError({ error, errorInfo }) {
return (
<div>
<h2>Failed to load component</h2>
<p>{error?.message}</p>
<details>{errorInfo?.componentStack}</details>
</div>
);
}
function App() {
return <ScalprumComponent scope="myApp" module="MyComponent" ErrorComponent={<CustomError />} />;
}Self-Repair Feature: If a component fails to load, ScalprumComponent automatically retries once with cache disabled.
TypeScript Support
Remote declaration archives are default source of types for known scopes and modules:
import { useModule, useRemoteHook } from '@scalprum/react-core';
function TypedExample() {
// Props and component type come from generated declarations.
const Widget = useModule('widgets', './Chart');
// Arguments and result come from generated declarations.
const { hookResult } = useRemoteHook({
scope: 'auth',
module: './useCurrentUser',
});
if (!Widget || !hookResult) return null;
return <Widget />;
}For a remote without generated declarations, explicit generics remain available as a fallback. Prefer generated declarations whenever the remote publishes them.
Build Tool Compatibility
This package works with:
- Webpack 5 with Module Federation plugin
- Rspack with Module Federation support
- Module Federation Runtime for any bundler
Prefetching
Components can export a prefetch function to load data before rendering:
// In remote module
export const prefetch = (api) => {
return fetch(`/api/data?user=${api.user.id}`).then((r) => r.json());
};
export default function MyComponent({ data }) {
// Component receives prefetched data
return <div>{data.value}</div>;
}// In host app
import { usePrefetch } from '@scalprum/react-core';
function DataComponent() {
const { ready, data, error } = usePrefetch();
if (!ready) return <div>Loading...</div>;
return <div>{data.value}</div>;
}Advanced Configuration
Plugin SDK Options
<ScalprumProvider
config={config}
pluginSDKOptions={{
pluginStoreFeatureFlags: {
disableStaticPlugins: false,
},
pluginLoaderOptions: {
transformPluginManifest: (manifest) => ({
...manifest,
loadScripts: manifest.loadScripts.map((s) => `${manifest.baseURL}${s}`),
}),
},
}}
>
{/* Your app */}
</ScalprumProvider>Custom Manifest Processing
<ScalprumComponent scope="myApp" module="MyComponent" processor={(manifest) => manifest.assets.js} />API Reference
Exports
// Components
export { ScalprumProvider } from './scalprum-provider';
export { ScalprumComponent } from './scalprum-component';
// Hooks
export { useScalprum } from './use-scalprum';
export { useModule } from './use-module';
export { useLoadModule } from './use-load-module';
export { usePrefetch } from './use-prefetch';
// Remote Hooks
export { useRemoteHook } from './use-remote-hook';
export { useRemoteHookManager } from './use-remote-hook-manager';
export { RemoteHookProvider } from './remote-hook-provider';
// Context
export { ScalprumContext } from './scalprum-context';
export { PrefetchContext } from './prefetch-context';
// Types
export * from './remote-hooks-types';Related Packages
@scalprum/core- Framework-agnostic core library@scalprum/build-utils- Build tools and NX executors@scalprum/react-test-utils- Testing utilities for React components
Shared Stores
Scalprum provides powerful event-driven state management for micro-frontends through shared stores. This allows multiple micro-frontend modules to share and synchronize state in real-time.
Quick Example
import { createSharedStore } from '@scalprum/core';
import { useGetState } from '@scalprum/react-core';
// Create a shared store (in a remote module)
const EVENTS = ['ADD_TODO', 'TOGGLE_TODO'] as const;
let todoStore = null;
const getTodoStore = () => {
if (!todoStore) {
todoStore = createSharedStore({
initialState: { todos: [] },
events: EVENTS,
onEventChange: (state, event, payload) => {
switch (event) {
case 'ADD_TODO':
return { todos: [...state.todos, payload.todo] };
case 'TOGGLE_TODO':
return {
todos: state.todos.map((t) => (t.id === payload.id ? { ...t, completed: !t.completed } : t)),
};
default:
return state;
}
},
});
}
return todoStore;
};
// Use in React components
export const useTodoStore = () => {
const store = getTodoStore();
const state = useGetState(store);
return {
todos: state.todos,
addTodo: (todo) => store.updateState('ADD_TODO', { todo }),
toggleTodo: (id) => store.updateState('TOGGLE_TODO', { id }),
};
};Key Features
- Event-Driven Updates: State changes triggered by named events
- Real-Time Synchronization: Changes propagate across all microfrontends instantly
- Performance Optimized:
useSubscribeStorefor selective subscriptions - Type-Safe: Full TypeScript support with generic types
- Singleton Pattern: Automatic state sharing across module boundaries
Complete Documentation
For comprehensive documentation including:
- API reference for
createSharedStore,useGetState,useSubscribeStore - Advanced patterns (async operations, performance optimization, persistence)
- Module federation setup and configuration
- TypeScript best practices
- Testing strategies
- Migration guides from Redux/Context
- Troubleshooting common issues
See the Complete Shared Stores Guide
Documentation
- Getting Started Guide
- Shared Stores Guide - Event-driven state management for microfrontends
- useRemoteHook Documentation
- useRemoteHookManager Documentation
- RemoteHookProvider Reference
- Remote Hook Types
License
Apache-2.0
