Skip to content

Instantly share code, notes, and snippets.

@SurjitSahoo
Last active May 11, 2025 04:13
Show Gist options
  • Save SurjitSahoo/5081220bf06b20ac8f606bd413438f26 to your computer and use it in GitHub Desktop.
Save SurjitSahoo/5081220bf06b20ac8f606bd413438f26 to your computer and use it in GitHub Desktop.
vite-dynamic-micro-front-ends
  1. Install @originjs/vite-plugin-federation
  2. set build option in vite.config as target: esnext

NOTE: federation doesn work untill we build the exposer app, because vite doesn't generate the remoteEntry file until we build the app.

/* eslint-disable @typescript-eslint/naming-convention */
import React, { ComponentType, LazyExoticComponent, useEffect, useState } from 'react';
/**
* The following is a virtual module generated by vite-plugin-federation.
* It's not available statically, hence we need to declare it's types for TypeScript.
* It contains the methods to set and get remote components.
* The methods are used to load remote components dynamically.
*/
import {
__federation_method_getRemote as getRemote,
__federation_method_setRemote as setRemote,
__federation_method_wrapDefault as wrapDefault,
type RemoteComponentConfig,
} from 'virtual:__federation__';
interface DefaultModule {
default: ComponentType<unknown>;
}
async function loadRemoteComponent(remote: RemoteComponentConfig): Promise<DefaultModule> {
const { name, componentToImport, ...restConfig } = remote;
setRemote(name, restConfig);
const remoteModule = await getRemote(name, componentToImport);
return wrapDefault(remoteModule, true) as Promise<DefaultModule>;
}
type LazyComponent = LazyExoticComponent<ComponentType<unknown>>;
export function useFederatedComponent(remote: RemoteComponentConfig): {
isError: boolean;
Component: LazyComponent | null;
} {
// Key for tracking if the component is already loaded
const key = `${remote.url}-${remote.name}-${remote.from}-${remote.format}-${remote.componentToImport}`;
const [Component, setComponent] = useState<LazyComponent | null>(null);
const [isError, setIsError] = useState(false);
/**
* If the key changes, delete the current component.
* The updated component will be loaded in the next useEffect.
*/
useEffect(() => {
if (Component) setComponent(null);
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [key]);
useEffect(() => {
async function loadComp(): Promise<void> {
try {
const Comp = React.lazy(async () => loadRemoteComponent(remote));
setComponent(Comp);
setIsError(false);
} catch (err) {
// eslint-disable-next-line no-console
console.error('>>>>>>>>', err);
setIsError(true);
}
}
if (!Component) {
loadComp().catch(() => {});
}
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [key, Component]);
return { isError, Component };
}
/* eslint-disable @typescript-eslint/naming-convention */
declare module 'virtual:__federation__' {
interface RemoteConfig {
url: (() => Promise<string>) | string;
format: 'esm' | 'systemjs' | 'var';
from: 'vite' | 'webpack';
}
interface RemoteComponentConfig extends Omit<RemoteConfig, 'url'> {
url: string;
name: string;
componentToImport: string;
}
export function __federation_method_setRemote(name: string, config: RemoteConfig): void;
export function __federation_method_getRemote(name: string, exposedPath: string): Promise<unknown>;
export function __federation_method_unwrapDefault(unwrappedModule: unknown): Promise<unknown>;
export function __federation_method_ensure(remoteName: string): Promise<unknown>;
export function __federation_method_wrapDefault(module: unknown, need: boolean): Promise<unknown>;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment