DEV Community

Shashank Trivedi
Shashank Trivedi

Posted on

1

Webpack 5 Series part-3

Please look for the old part of the series to fully understand the concept.

Online E-Store Application

Let's build an Online Store application using micro-frontends for modularity. Each micro-frontend will represent a different part of the store, and they will share common libraries like React, a design system, and a shared utility library.

Goal:

  1. ProductList exposes a list of products that can be imported and used by other apps.
  2. Cart exposes functionality to add/remove products from the cart.
  3. Checkout uses the data from Cart and processes the checkout.

Image description

Configuration for Module Federation

  • Micro-Frontend 1: ProductList

Exposes the ProductList component for other micro-frontends to use.

// webpack.config.js (ProductList)
const { ModuleFederationPlugin } = require('webpack').container;

module.exports = {
  plugins: [
    new ModuleFederationPlugin({
      name: 'productListApp',
      filename: 'remoteEntry.js',
      exposes: {
        './ProductList': './src/ProductList',
      },
      shared: {
        react: { singleton: true, eager: true },
        'react-dom': { singleton: true, eager: true },
        // Share any other libraries like a UI library, e.g., Material-UI
      },
    }),
  ],
};
Enter fullscreen mode Exit fullscreen mode
  • Micro-Frontend 2: Cart

Exposes the Cart component, and it uses a shared state library (like Zustand) for cart management.

// webpack.config.js (Cart)
const { ModuleFederationPlugin } = require('webpack').container;

module.exports = {
  plugins: [
    new ModuleFederationPlugin({
      name: 'cartApp',
      filename: 'remoteEntry.js',
      exposes: {
        './Cart': './src/Cart',
      },
      shared: {
        react: { singleton: true, eager: true },
        'react-dom': { singleton: true, eager: true },
        zustand: { singleton: true }, // Zustand or Redux for shared state
      },
    }),
  ],
};

Enter fullscreen mode Exit fullscreen mode
  • Micro-Frontend 3: Checkout

Consumes the Cart and ProductList components to show a summary before checkout.

// webpack.config.js (Checkout)
const { ModuleFederationPlugin } = require('webpack').container;

module.exports = {
  plugins: [
    new ModuleFederationPlugin({
      name: 'checkoutApp',
      remotes: {
        productListApp: 'productListApp@http://localhost:3001/remoteEntry.js',
        cartApp: 'cartApp@http://localhost:3002/remoteEntry.js',
      },
      shared: {
        react: { singleton: true, eager: true },
        'react-dom': { singleton: true, eager: true },
      },
    }),
  ],
};
Enter fullscreen mode Exit fullscreen mode

Component Implementation:

  • Micro-Frontend 1: ProductList

Exposed by ProductList micro-frontend.

// src/ProductList.js (ProductList)
import React from 'react';

const products = [
  { id: 1, name: 'Product 1', price: 50 },
  { id: 2, name: 'Product 2', price: 75 },
];

const ProductList = () => (
  <div>
    <h2>Products</h2>
    <ul>
      {products.map(product => (
        <li key={product.id}>
          {product.name} - ${product.price}
        </li>
      ))}
    </ul>
  </div>
);

export default ProductList;
Enter fullscreen mode Exit fullscreen mode
  • Micro-Frontend 2: Cart

Exposed by Cart micro-frontend and manages state (e.g., using Zustand or Redux).

// src/Cart.js (Cart)
import React from 'react';
import create from 'zustand';

// Zustand store for managing the cart
const useCartStore = create(set => ({
  cart: [],
  addToCart: (product) => set(state => ({ cart: [...state.cart, product] })),
  removeFromCart: (product) =>
    set(state => ({ cart: state.cart.filter(item => item.id !== product.id) })),
}));

const Cart = () => {
  const { cart, addToCart, removeFromCart } = useCartStore();

  return (
    <div>
      <h2>Cart</h2>
      <ul>
        {cart.map(product => (
          <li key={product.id}>
            {product.name} - ${product.price}
            <button onClick={() => removeFromCart(product)}>Remove</button>
          </li>
        ))}
      </ul>
    </div>
  );
};

export default Cart;
Enter fullscreen mode Exit fullscreen mode
  • Micro-Frontend 3: Checkout

Consumes Cart and ProductList components, bringing everything together.

// src/Checkout.js (Checkout)
import React, { lazy, Suspense } from 'react';

const ProductList = lazy(() => import('productListApp/ProductList'));
const Cart = lazy(() => import('cartApp/Cart'));

const Checkout = () => (
  <div>
    <h1>Checkout</h1>
    <Suspense fallback={<div>Loading Products...</div>}>
      <ProductList />
    </Suspense>
    <Suspense fallback={<div>Loading Cart...</div>}>
      <Cart />
    </Suspense>
    <button>Proceed to Payment</button>
  </div>
);

export default Checkout;
Enter fullscreen mode Exit fullscreen mode

Steps to Run the App:

  • Run the Micro-Frontends:

Each micro-frontend (ProductList, Cart, Checkout) will be served on different ports (e.g., ProductList on localhost:3001, Cart on localhost:3002, and Checkout on localhost:3003).
You will need to set up each micro-frontend with Webpack dev server and run them individually.

  • Consume Remote Modules:

In the Checkout micro-frontend, we are dynamically importing the ProductList and Cart components from their respective remote micro-frontends.

  • Shared Dependencies:

Each micro-frontend shares React, React-DOM, and possibly other shared dependencies like a state library (e.g., Zustand) or a design system (Material-UI).

Heroku

This site is built on Heroku

Join the ranks of developers at Salesforce, Airbase, DEV, and more who deploy their mission critical applications on Heroku. Sign up today and launch your first app!

Get Started

Top comments (0)

Heroku

Build apps, not infrastructure.

Dealing with servers, hardware, and infrastructure can take up your valuable time. Discover the benefits of Heroku, the PaaS of choice for developers since 2007.

Visit Site

👋 Kindness is contagious

Explore a sea of insights with this enlightening post, highly esteemed within the nurturing DEV Community. Coders of all stripes are invited to participate and contribute to our shared knowledge.

Expressing gratitude with a simple "thank you" can make a big impact. Leave your thanks in the comments!

On DEV, exchanging ideas smooths our way and strengthens our community bonds. Found this useful? A quick note of thanks to the author can mean a lot.

Okay