@ecommerce-platform/shared-layout

v1.0.3

Published

Shared layout components for ecommerce micro-frontends - provides Navbar, Footer, and Layout with intelligent navigation for standalone and MFE modes

Readme

@ecommerce-platform/shared-layout

Shared layout components for ecommerce micro-frontends. Provides Navbar, Footer, and Layout components with intelligent navigation that works in both standalone and MFE (Module Federation) modes.

Features

  • Intelligent Navigation: Automatically detects standalone vs MFE mode and redirects users appropriately
  • Host Fallback: In standalone mode, tries to navigate to host app first, then falls back to standalone MFE if host is unavailable
  • Consistent UI: Same layout and styling across all micro-frontends
  • Flexible Props: Accepts external data (basket, categories) for maximum flexibility

Installation

npm install @ecommerce-platform/shared-layout

Usage

Basic Setup

import { Layout, configureNavigation } from '@ecommerce-platform/shared-layout';
import { BrowserRouter, Routes, Route } from 'react-router-dom';

// Configure navigation (optional, defaults work for most cases)
configureNavigation({
  hostUrl: 'http://localhost:4200',
  currentAppName: 'store', // e.g., 'store', 'checkout', 'account'
  standalonePorts: {
    store: 4201,
    checkout: 4202,
    account: 4203,
  },
});

function App() {
  return (
    <BrowserRouter>
      <Routes>
        <Route path="/" element={<Layout appName="store" />}>
          <Route index element={<YourComponent />} />
        </Route>
      </Routes>
    </BrowserRouter>
  );
}

With Basket Data

import { Layout } from '@ecommerce-platform/shared-layout';
import { useBasket } from './services/basket';

function App() {
  const { items, itemCount, isLoading } = useBasket();
  
  // Map your basket items to CartItem format
  const cartItems = items.map(item => ({
    id: item.productId,
    name: item.productName,
    price: item.price,
    quantity: item.quantity,
    image: item.imageFile || '',
  }));

  return (
    <Layout
      appName="store"
      basketCount={itemCount}
      cartItems={cartItems}
      isLoading={isLoading}
      onRemoveCartItem={(id) => {
        // Handle remove cart item
      }}
    >
      <YourRoutes />
    </Layout>
  );
}

With Categories

import { Layout } from '@ecommerce-platform/shared-layout';
import { useCategories } from './services/categories';

function App() {
  const { categories, isLoading } = useCategories();
  
  return (
    <Layout
      appName="store"
      categories={categories.map(cat => ({
        id: cat.id,
        name: cat.name,
        icon: cat.icon,
        path: cat.path,
      }))}
      categoriesLoading={isLoading}
    >
      <YourRoutes />
    </Layout>
  );
}

Standalone Mode Detection

The package automatically detects if it's running in standalone mode by checking:

  1. If window.__MFE_HOST__ is defined (set by host when loading MFE)
  2. If the current port matches a standalone MFE port

Navigation Behavior

  • In MFE mode (loaded by host): Uses React Router navigation normally
  • In standalone mode:
    1. First tries to navigate to host app (e.g., http://localhost:4200/checkout)
    2. If host is unavailable, falls back to standalone MFE (e.g., http://localhost:4202/checkout)

Custom Navigation

import { useNavigate } from '@ecommerce-platform/shared-layout';

function MyComponent() {
  const navigate = useNavigate('store'); // Pass app name for fallback
  
  const handleClick = () => {
    // Will try host first, then fallback to standalone if needed
    navigate('/checkout');
  };
  
  return <button onClick={handleClick}>Go to Checkout</button>;
}

Theme Configuration

The package includes a complete Ant Design theme configuration that matches the host app:

import { themeConfig } from '@ecommerce-platform/shared-layout';
import { ConfigProvider } from 'antd';

function App() {
  return (
    <ConfigProvider theme={themeConfig}>
      <YourApp />
    </ConfigProvider>
  );
}

The theme includes:

  • Brand colors (primary gradient: #667eea to #764ba2)
  • Typography (Poppins font family)
  • Spacing, shadows, and border radius
  • Component-specific styles (Button, Input, Card, Layout, Typography)

API Reference

Layout

Main layout component that wraps your application.

Props:

  • appName?: string - Current app name for navigation fallback
  • basketCount?: number - Number of items in basket
  • cartItems?: CartItem[] - Cart items for preview
  • isLoading?: boolean - Loading state for cart data
  • onRemoveCartItem?: (id: string) => void - Callback when removing item
  • categories?: Array<{id: string; name: string; icon?: string; path: string}> - Categories for dropdown
  • categoriesLoading?: boolean - Loading state for categories
  • hideNavbar?: boolean - Hide navbar (default: false)
  • hideFooter?: boolean - Hide footer (default: false)

Navbar

Standalone navbar component.

Props: Same as Layout (except hideNavbar and hideFooter)

Footer

Standalone footer component.

Props:

  • appName?: string - Current app name for navigation fallback

configureNavigation(config)

Configure navigation settings.

Config:

  • hostUrl?: string - Host app URL (default: http://localhost:4200)
  • currentAppName?: string - Current MFE app name
  • standalonePorts?: Record<string, number> - Map of app names to ports

useNavigate(appName?)

Custom navigation hook that handles redirects intelligently.

isStandaloneMode()

Check if running in standalone mode.

Examples

Store App (Standalone)

// store/src/app.tsx
import { Layout, configureNavigation } from '@ecommerce-platform/shared-layout';

configureNavigation({
  currentAppName: 'store',
});

export function App() {
  return (
    <BrowserRouter>
      <Routes>
        <Route path="/" element={<Layout appName="store" />}>
          <Route path="*" element={<StoreRoutes />} />
        </Route>
      </Routes>
    </BrowserRouter>
  );
}

Checkout App (Standalone)

// checkout/src/app.tsx
import { Layout, configureNavigation } from '@ecommerce-platform/shared-layout';

configureNavigation({
  currentAppName: 'checkout',
});

export function App() {
  return (
    <BrowserRouter>
      <Routes>
        <Route path="/" element={<Layout appName="checkout" />}>
          <Route path="*" element={<CheckoutRoutes />} />
        </Route>
      </Routes>
    </BrowserRouter>
  );
}

License

MIT