Webpack 5 introduced Module Federation — a game-changing API that lets separate applications share code at runtime. This is the foundation of micro-frontend architecture.
Module Federation: Share Code at Runtime
// webpack.config.js — Host Application
const { ModuleFederationPlugin } = require("webpack").container;
module.exports = {
plugins: [
new ModuleFederationPlugin({
name: "host",
remotes: {
dashboard: "dashboard@https://dashboard.example.com/remoteEntry.js",
analytics: "analytics@https://analytics.example.com/remoteEntry.js",
},
shared: ["react", "react-dom"],
}),
],
};
// Load remote component dynamically
const Dashboard = React.lazy(() => import("dashboard/Widget"));
const Analytics = React.lazy(() => import("analytics/Chart"));
// webpack.config.js — Remote Application
new ModuleFederationPlugin({
name: "dashboard",
filename: "remoteEntry.js",
exposes: {
"./Widget": "./src/components/DashboardWidget",
"./utils": "./src/utils/dataHelpers",
},
shared: {
react: { singleton: true, requiredVersion: "^18.0.0" },
"react-dom": { singleton: true },
},
});
Asset Modules: No Loaders Needed
module.exports = {
module: {
rules: [
{ test: /\.png$/, type: "asset/resource" }, // Emit file
{ test: /\.svg$/, type: "asset/inline" }, // Base64 inline
{ test: /\.txt$/, type: "asset/source" }, // Raw string
{ test: /\.jpg$/, type: "asset", parser: { // Auto (size threshold)
dataUrlCondition: { maxSize: 8 * 1024 }
}},
],
},
};
Persistent Caching
module.exports = {
cache: {
type: "filesystem",
buildDependencies: {
config: [__filename], // Invalidate on config change
},
version: `${process.env.NODE_ENV}-v1`,
},
};
// Second build: 10x faster
Custom Plugin API
class BuildStatsPlugin {
apply(compiler) {
compiler.hooks.done.tap("BuildStatsPlugin", (stats) => {
const { assets, time } = stats.toJson({ assets: true });
console.log(`Build completed in ${time}ms`);
console.log(`Assets: ${assets.length}`);
assets.forEach(a => console.log(` ${a.name}: ${(a.size / 1024).toFixed(1)}KB`));
});
compiler.hooks.emit.tapAsync("BuildStatsPlugin", (compilation, callback) => {
const manifest = {};
for (const [name, source] of compilation.assets) {
manifest[name] = source.size();
}
compilation.assets["build-manifest.json"] = {
source: () => JSON.stringify(manifest, null, 2),
size: () => JSON.stringify(manifest).length
};
callback();
});
}
}
Tree Shaking with Side Effects
// package.json
{
"sideEffects": ["*.css", "./src/polyfills.js"]
}
// webpack.config.js
module.exports = {
optimization: {
usedExports: true,
minimize: true,
splitChunks: {
chunks: "all",
cacheGroups: {
vendor: {
test: /[\/]node_modules[\/]/,
name: "vendors",
priority: -10,
},
},
},
},
};
Building data-driven micro-frontends? My Apify tools feed structured data into any frontend architecture.
Custom build solution? Email spinov001@gmail.com
Top comments (0)