npm package: @grasp-labs/ds-web-microfrontends-common-lib
Description: Standardized Module Federation configuration for remote microfrontend applications. This library provides opinionated defaults and type-safe utilities for building microfrontends with Vite and React.
Latest version: 0.2.0

# @grasp-labs/ds-web-microfrontends-common-lib

Standardized Module Federation configuration for remote microfrontend applications. This library provides opinionated defaults and type-safe utilities for building microfrontends with Vite and React.

## Installation

```bash
npm install @grasp-labs/ds-web-microfrontends-common-lib
```

## Prerequisites

**Node.js:** >= 22

This library requires compatible peer dependencies. Install them in your microfrontend project, for example:

```bash
npm install -D react@19.2.3
```

Optionally, for Data Platform integration:

```bash
npm install -D @grasp-labs/ds-microfrontends-integration
```

Version requirements are defined in this package's `peerDependencies` - check [package.json](package.json) for current version ranges.

## Quick Start

### 1. Configure Vite

Create or update your [vite.config.ts](vite.config.ts):

```typescript
import { defineConfig } from "vite";
import react from "@vitejs/plugin-react";
import {
  createMicrofrontendsBase,
  dsFederation,
} from "@grasp-labs/ds-web-microfrontends-common-lib";
import { name } from "./package.json";

const MICROFRONTENDS_BASE = createMicrofrontendsBase(name);

export default defineConfig(({ mode }) => ({
  base: mode === "production" ? MICROFRONTENDS_BASE : "/",
  plugins: [react(), dsFederation(name)],
}));
```

### 2. Create Your App Component

Your [src/App.tsx](src/App.tsx) should export a React component with Routes (not render it):

```tsx
// src/App.tsx
import { Routes, Route } from "react-router";
import { HomePage } from "./layouts/HomePage";
import { SettingsPage } from "./layouts/SettingsPage";
import { InternalPage } from "./layouts/InternalPage";

function App() {
  return (
    <Routes>
      <Route path="/" element={<HomePage />} />
      <Route path="/settings" element={<SettingsPage />} />
      <Route path="/internal" element={<InternalPage />} />
    </Routes>
  );
}

export default App;
```

### 3. Define Navigation Configuration

Create [src/navigationConfig.ts](src/navigationConfig.ts):

```typescript
import type { NavigationConfig } from "@grasp-labs/ds-web-microfrontends-common-lib";

export const navigationConfig = {
  INDEX: {
    label: "Home",
    path: "/",
    icon: "database",
    type: "visible",
  },
  SETTINGS: {
    label: "Settings",
    path: "/settings",
    icon: "cogWheel",
    type: "visible",
  },
  INTERNAL: {
    label: "Internal Page",
    path: "/internal",
    type: "hidden",
  },
} satisfies NavigationConfig;
```

**Important:** The `navigationConfig.ts` file must remain a **bare TypeScript file** with no React imports or dependencies. This is critical because:

- The microfrontend app (src/App.tsx) is **lazily loaded** by the host application
- The host app needs **immediate access** to the routing configuration to build the navigation structure **before** any remote apps are loaded
- If navigationConfig imports React or other heavy dependencies, it would force those dependencies to load before navigation can be rendered, defeating the purpose of lazy loading

Keep this file lightweight - only export the configuration object with no side effects or dependencies.

## Default Module Federation Configuration

The `dsFederation` plugin automatically configures:

**Exposes:**

- `"."` → `"./src/App"` - Your main React component
- `"./navigationConfig"` → `"./src/navigationConfig"` - Navigation configuration

**Shared Dependencies:**

- `react` (singleton)
- `react-dom` (singleton)
- `react-router` (singleton)

**Public Path:** `microfrontends/${name}/`

## Advanced Usage

### Custom Module Federation Configuration

Override defaults by passing configuration to `dsFederation`:

```typescript
import { defineConfig } from "vite";
import react from "@vitejs/plugin-react";
import { dsFederation } from "@grasp-labs/ds-web-microfrontends-common-lib";

export default defineConfig({
  plugins: [
    react(),
    dsFederation("your-microfrontend-name", {
      exposes: {
        ".": "./src/App",
        "./navigationConfig": "./src/navigationConfig",
        "./CustomComponent": "./src/components/CustomComponent",
      },
      shared: {
        lodash: {
          singleton: true,
          requiredVersion: "^4.17.21",
        },
      },
    }),
  ],
});
```

### Using DATA_PLATFORM_SHARED_DEPS

For microfrontends that integrate with `@grasp-labs/ds-microfrontends-integration`:

```typescript
import {
  createModuleFederationConfig,
  DATA_PLATFORM_SHARED_DEPS,
} from "@grasp-labs/ds-web-microfrontends-common-lib";
import { federation } from "@module-federation/vite";

export default defineConfig({
  plugins: [
    react(),
    federation({
      ...createModuleFederationConfig("your-microfrontend-name", {
        shared: DATA_PLATFORM_SHARED_DEPS,
      }),
    }),
  ],
});
```

## API Reference

### Functions

#### `dsFederation(name, overrides?)`

Creates a configured Vite Module Federation plugin.

**Parameters:**

- `name` (string): The name of the microfrontend (typically from package.json)
- `overrides` (Partial<ModuleFederationOptions>, optional): Override default configuration

**Returns:** Configured Vite plugin

**Example:**

```typescript
dsFederation("my-app", {
  exposes: {
    ".": "./src/App",
    "./utils": "./src/utils",
  },
});
```

#### `createModuleFederationConfig(name, overrides?)`

Generates the Module Federation configuration object without wrapping it in a Vite plugin.

**Parameters:**

- `name` (string): The name of the microfrontend
- `overrides` (Partial<ModuleFederationOptions>, optional): Override default configuration

**Returns:** `ModuleFederationOptions` object

**Example:**

```typescript
const config = createModuleFederationConfig("my-app");
```

#### `createMicrofrontendsBase(name)`

Generates the standardized public path for a microfrontend.

**Parameters:**

- `name` (string): The name of the microfrontend

**Returns:** `string` - Path in format `microfrontends/${name}/`

**Example:**

```typescript
createMicrofrontendsBase("data-sources"); // "microfrontends/data-sources/"
```

### Constants

#### `COMMON_SHARED_DEPS`

Standard shared dependencies for Module Federation. Versions are sourced from this package's `peerDependencies`.

```typescript
{
  react: { singleton: true, requiredVersion: "<version from peerDependencies>" },
  "react-dom": { singleton: true, requiredVersion: "<version from peerDependencies>" },
  "react-router": { singleton: true, requiredVersion: "<version from peerDependencies>" },
}
```

#### `DATA_PLATFORM_SHARED_DEPS`

Extended shared dependencies including Data Platform integration library.

```typescript
{
  ...COMMON_SHARED_DEPS,
  "@grasp-labs/ds-microfrontends-integration": {
    singleton: true,
    requiredVersion: "<version from peerDependencies>",
  },
}
```

### Type Definitions

#### `RouteConfig`

Defines a single route in the navigation system.

```typescript
type RouteConfig = {
  label: string; // Display name
  path: string; // Route path
  icon?: IconName; // Optional icon from @grasp-labs/ds-react-components
  type?: "hidden" | "visible"; // Visibility in navigation
};
```

#### `CategoryConfig`

Defines a navigation category with nested routes.

```typescript
type CategoryConfig = {
  label: string;
  icon?: IconName;
  type: "category";
  children: Record<string, RouteConfig | CategoryConfig>;
};
```

#### `NavigationItem`

Union type for navigation items.

```typescript
type NavigationItem = RouteConfig | CategoryConfig;
```

#### `NavigationConfig`

Type-safe navigation configuration. Requires an `INDEX` route.

```typescript
type NavigationConfig<TKeys extends string = string> = {
  INDEX: RouteConfig;
} & Record<TKeys, NavigationItem>;
```

**Example:**

```typescript
const navigationConfig = {
  INDEX: {
    label: "Data Sources",
    path: "/",
    icon: "database",
    type: "visible",
  },
  CATALOG: {
    label: "Catalog",
    icon: "folder",
    type: "category",
    children: {
      LIST: {
        label: "All Sources",
        path: "/catalog",
        icon: "list",
      },
      CREATE: {
        label: "Create",
        path: "/catalog/create",
        icon: "plus",
      },
    },
  },
} satisfies NavigationConfig;
```

#### `MicrofrontendExposes`

Type for Module Federation exposes configuration.

```typescript
type MicrofrontendExposes = {
  ".": string;
  "./navigationConfig": string;
} & Record<string, string>;
```

## Shared Dependencies

These dependencies are configured as Module Federation singletons, ensuring one instance across all microfrontends:

| Dependency                                  | Purpose                                         |
| ------------------------------------------- | ----------------------------------------------- |
| `react`                                     | UI library (singleton)                          |
| `react-dom`                                 | React DOM renderer (singleton)                  |
| `react-router`                              | Routing library (singleton)                     |
| `@grasp-labs/ds-microfrontends-integration` | Data Platform integration (singleton, optional) |

Singleton configuration prevents version conflicts across microfrontends. Version requirements are defined in this package's `peerDependencies` - check [package.json](https://github.com/grasp-labs/ds-web-microfrontends-common-lib/blob/main/package.json) for current requirements.

## Development

```bash
# Install dependencies
npm install

# Build the package
npm run build

# Watch mode for development
npm run dev

# Format code
npm run format

# Check formatting
npm run format:check
```

## Project Structure

```
src/
└── index.ts           # Module Federation configuration and type definitions
```

## Publishing

This package is published to npm as `@grasp-labs/ds-web-microfrontends-common-lib`.

Publishing is done via GitHub Actions workflow (`publish.yml`):

1. Go to Actions → "Version and Publish Package"
2. Click "Run workflow"
3. Select version type:
   - `patch` - Bug fixes (0.1.0 → 0.1.1)
   - `minor` - New features (0.1.0 → 0.2.0)
   - `major` - Breaking changes (0.1.0 → 1.0.0)
   - `beta` - Beta release with timestamp

The workflow automatically:
- Builds the package
- Bumps version and creates git tag (for release versions)
- Publishes to npm registry

## Contributing

1. Create a feature branch
2. Ensure `npm run check` passes (format, lint, typecheck)
3. Build successfully with `npm run build`
4. Create a pull request with semantic title (e.g., `feat:`, `fix:`, `chore:`)

## License

Apache-2.0