{"id":19380843,"url":"https://github.com/module-federation/nextjs-ssr","last_synced_at":"2025-10-29T23:54:04.591Z","repository":{"id":60535982,"uuid":"448491983","full_name":"module-federation/nextjs-ssr","owner":"module-federation","description":"Next.js Federated SSR over Software Streaming","archived":false,"fork":false,"pushed_at":"2023-12-15T14:58:50.000Z","size":446,"stargazers_count":28,"open_issues_count":5,"forks_count":5,"subscribers_count":4,"default_branch":"main","last_synced_at":"2025-10-28T16:46:04.370Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"JavaScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"other","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/module-federation.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null}},"created_at":"2022-01-16T07:58:04.000Z","updated_at":"2025-09-21T14:08:28.000Z","dependencies_parsed_at":"2023-12-15T15:54:16.633Z","dependency_job_id":null,"html_url":"https://github.com/module-federation/nextjs-ssr","commit_stats":{"total_commits":173,"total_committers":3,"mean_commits":"57.666666666666664","dds":"0.028901734104046284","last_synced_commit":"ca598943617f38bd5d391e28d8bb280b82e1cb1e"},"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/module-federation/nextjs-ssr","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/module-federation%2Fnextjs-ssr","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/module-federation%2Fnextjs-ssr/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/module-federation%2Fnextjs-ssr/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/module-federation%2Fnextjs-ssr/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/module-federation","download_url":"https://codeload.github.com/module-federation/nextjs-ssr/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/module-federation%2Fnextjs-ssr/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":281719940,"owners_count":26549881,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","status":"online","status_checked_at":"2025-10-29T02:00:06.901Z","response_time":59,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2024-11-10T09:14:58.957Z","updated_at":"2025-10-29T23:54:04.575Z","avatar_url":"https://github.com/module-federation.png","language":"JavaScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"## This Repo is being sunset in favor of a plugin that supports both SSR \u0026 CSR in one\n\nAll efforts are being directed to this PR: https://github.com/module-federation/nextjs-mf/pull/108\n\nThis repo exists as a reference point to contributors. Do not use it, it will not be maintained further.\n\nThis plugin uses the sidecar design, which has been killed in favor of childCompiler design used in nextjs-mf\n\n# Module Federation For Next.js, with SSR support\n\nThis plugin enables Module Federation on Next.js, both client-side and server-side.\n\nModule Federation on the server utilizes proprietary software, commonly known as \"Software Streaming\".\n\nThis is the only _stable_ and continuously supported solution for Module Federation on Next.js.\n\nIt is also the only Federated SSR solution in existence that is supported by the creator of Module Federation.\n\n---\n\nSoftware Streams have been tested extensively in other federated server applications, the underlying tech is proven to be reliable.\nHowever, software streams in Next.js is experimental, while in the beta phase.\n\nCompanies have used streams with next.js in the past - but those streaming plugins were based on a leaked alpha we created before Webpack 5 was released.\n\nThis is the first time the Federation Group has made its proprietary technology available to others. Our technology is the most stable in existence.\n\nThis is because of our proximity to the Webpack Foundation \u0026 deep understanding\n\n### Supports\n\n- next ^12.x.x\n- SSR \u0026 CSR\n\n---\n\n## Contents\n\n- [What's shared by default?](#whats-shared-by-default)\n- [Security](#important-note-about-security)\n- [Using the plugin](#using-the-plugin)\n- [Configuration Options](#configuration-options)\n- [Demo](#demo)\n- [Configuring Pages for SSR](#configuring-pages-for-ssr)\n- [Dynamic Routing between Applications](#dynamic-routing-between-applications)\n- [Exposing and Consuming Pages](#exposing-and-consuming-pages)\n- [Support and Maintenance](#support-and-maintenance)\n- [Contact](#contact)\n\n---\n\n## What's shared by default?\n\nUnder the hood we share some next internals automatically\nYou do not need to share these packages, sharing next internals yourself will cause errors.\n\n```js\nconst sharedDefaults = {\n  \"next/dynamic\": {\n    requiredVersion: false,\n    singleton: true,\n  },\n  \"styled-jsx\": {\n    requiredVersion: false,\n    singleton: true,\n  },\n  \"next/link\": {\n    requiredVersion: false,\n    singleton: true,\n  },\n  \"next/router\": {\n    requiredVersion: false,\n    singleton: true,\n  },\n  \"next/script\": {\n    requiredVersion: false,\n    singleton: true,\n  },\n  \"next/head\": {\n    requiredVersion: false,\n    singleton: true,\n  },\n};\n```\n\n## Important note about security!\n\nThis plugin creates a remote container for the server-side. By default, it is written to `_next/static/ssr`.\nIt is highly recommended that network access to `_next/static/ssr/*` is restricted to servers/machines inside the VPN or internal infrastructure.\n\nIf access to that route is not restricted, you could risk exposing server code to the public internet!\n\nSince the ssr directory is built for server-side, webpack will not tree-shake `process.browser` conditionals.\n\nIf you currently use `if(process.browser)` as a way to prevent private code or keys from showing up in bundled code, that will not work - this is becuse we are building both a client and server target and are exposing it via `static` directory which is accessible over networks.\n\n**Why would we put it in static?!?**\n\nThe goal of this software is to make federation \"just work\" with one single plugin and as little setup as possible.\n\nTo provide a built-in protected route would require additional setup and complexity, like middleware, or a custom server.\n\nHow assets are protected should be up to the consumer, who might use the CDN, NGIX, middleware to implement a restricted route.\n\n---\n\n## Using The Plugin\n\nI now support the top-level API as well as the low-level API\n\nFederated modules can be used in these various methods\n\nStatic, synchronous imports\n\n```js\nimport SomeComponent from \"next2/SomeComponent\";\n// OR\nconst SomeComponent = require(\"next2/ScomeComponent\");\n```\n\nThis plugin can be used for any piece of code, not just React Components.\n\nHooks, Middleware, Context, utilities, anything.\n\nAsync imports are recommended, whenever possible\n\n```js\nconst SampleComponent = dynamic(() =\u003e import(\"next2/SampleComponent\"));\n\n// alternatively the low-level api can be used as well\n// using the low-level api requires the remote to already be injected\nconst SampleComponent = dynamic(\n  () =\u003e window.next2.get(\"./sampleComponent\").then((factory) =\u003e factory()),\n  {\n    ssr: false,\n  }\n);\n```\n\nMake sure you are using `mini-css-extract-plugin@2` - version 2 supports resolving assets through `publicPath:'auto'`\n\n---\n\n## Configuration Options\n\n```js\nconst remotes = (isServer) =\u003e {\n  const location = isServer ? \"ssr\" : \"chunks\";\n  return {\n    next1: `next1@https://someapp.com/_next/static/${location}/remoteEntry.js`,\n  };\n};\nwithFederatedSidecar(\n  {\n    name: \"next2\",\n    filename: \"static/chunks/remoteEntry.js\",\n    exposes: {\n      \"./sampleComponent\": \"./components/sampleComponent.js\",\n    },\n    remotes,\n    shared: {\n      react: {\n        // Notice shared are NOT eager here\n        // we handle eager sharing inside the plugin\n        requiredVersion: false,\n        singleton: true,\n      },\n    },\n  },\n  {\n    ssr: true, // if you want to disable the server runtimes, set to false. This will mean client side only.\n    removePlugins: [\n      // optional\n      // these are the defaults\n      \"BuildManifestPlugin\",\n      \"ReactLoadablePlugin\",\n      \"DropClientPage\",\n      \"WellKnownErrorsPlugin\",\n      \"ModuleFederationPlugin\",\n      \"NextJsRequireCacheHotReloader\",\n      \"PagesManifestPlugin\",\n    ],\n    publicPath: \"auto\", // defaults to 'auto', is optional\n  }\n);\n```\n\n---\n\n## Demo\n\nYou can see it in action here: https://github.com/module-federation/module-federation-examples/tree/master/nextjs-ssr\n\n## How to add a sidecar for exposes to your next.js app\n\n1. Use `withFederatedSidecar` in your `next.config.js` of the app that you wish to expose modules from. We'll call this \"next2\".\n\n```js\n// next2\n// next.config.js\nconst { withFederatedSidecar } = require(\"@module-federation/nextjs-ssr\");\nconst withPlugins = require(\"next-compose-plugins\");\n\nconst remotes = (isServer) =\u003e {\n  const location = isServer ? \"ssr\" : \"chunks\";\n  return {\n    next1: `next1@https://someapp.com/_next/static/${location}/remoteEntry.js`,\n  };\n};\n\nconst nextConfig = {\n  // your original next.config.js export\n  // we attach next internals to share scope at runtime\n\n  webpack(config, options) {\n    const { webpack, isServer } = options;\n    config.module.rules.push({\n      test: [/_app.[jt]sx?/, /_document.[jt]sx?/],\n      loader: \"@module-federation/nextjs-ssr/lib/federation-loader.js\",\n    });\n\n    return config;\n  },\n};\n\nmodule.exports = withPlugins(\n  [\n    withFederatedSidecar({\n      name: \"next2\",\n      filename: \"static/chunks/remoteEntry.js\",\n      remotes: remotes,\n      exposes: {\n        \"./sampleComponent\": \"./components/sampleComponent.js\",\n      },\n      shared: {},\n    }),\n  ],\n  nextConfig\n);\n```\n\nConsuming/host applications you must at least add the loader to next.config.js, and ensure you have a [custom Next.js App](https://nextjs.org/docs/advanced-features/custom-app) `pages/_app.js` (or `.tsx`):\n\n```js\nmodule.exports = {\n  webpack(config, options) {\n    // we attach next internals to share scope at runtime\n    config.module.rules.push({\n      test: [/_app.[jt]sx?/, /_document.[jt]sx?/],\n      loader: \"@module-federation/nextjs-ssr/lib/federation-loader.js\",\n    });\n\n    return config;\n  },\n};\n```\n\n### Experiments\n\n**Chunk Flushing**\n\nChunk Flushing is the mechanism used to _flush_ dynamic imported chunks out of a render and into the HTML of a document.\nIf you want to SSR the `\u003cscript\u003e` tags of federated imports, reducing Round Trip Time (RTT), you can enable the following experiment\n\n1. Enable the flushChunk experiment via the plugin\n\n```js\nwithFederatedSidecar(\n  // normal MF config\n  {\n    name: \"next1\",\n    filename: \"static/chunks/remoteEntry.js\",\n    exposes: {},\n    remotes: {},\n    shared: {},\n  },\n  // sidecar specific options\n  {\n    experiments: {\n      flushChunks: true,\n    },\n  }\n);\n```\n\n2. Inside `_document.js` (or `.tsx`) do the following:\n\n```js\nimport Document, { Html, Main, NextScript } from \"next/document\";\nimport {\n  flushChunks,\n  ExtendedHead,\n} from \"@module-federation/nextjs-ssr/flushChunks\";\n\nclass MyDocument extends Document {\n  static async getInitialProps(ctx) {\n    const initialProps = await Document.getInitialProps(ctx);\n    const remotes = await flushChunks(process.env.REMOTES);\n\n    return {\n      ...initialProps,\n      remoteChunks: remotes,\n    };\n  }\n\n  render() {\n    return (\n      \u003cHtml\u003e\n        \u003cExtendedHead\u003e\n          {\" \"}\n          {/* this extends Head from next/document */}\n          \u003cmeta name=\"robots\" content=\"noindex\" /\u003e\n          {/* Object.values() MUST be called here, not in getInitialProps */}\n          {Object.values(this.props.remoteChunks)}\n        \u003c/ExtendedHead\u003e\n        \u003cbody className=\"bg-background-grey\"\u003e\n          \u003cMain /\u003e\n          \u003cNextScript /\u003e\n        \u003c/body\u003e\n      \u003c/Html\u003e\n    );\n  }\n}\n\nexport default MyDocument;\n```\n\n**Hot Reloadable Production Servers**\n\nWhen a remote is deployed, prod servers will hot reload. This solves the \"stuck\" modules problem once a federated module has been required.\n\nBy default, revalidation will purge require cache. If you want to perform any additional actions, you can do so in the `then` parameter.\n\n_You do not need to call any extra functions to hot reload_\n\nOptions:\n\n```js\n// these are the defaults\nrevalidate({\n  // revalidate remotes by polling\n  poll: false, // defualt false\n  // how ofter should it poll\n  pollFrequeny: 3000, // defaults to 3000ms\n});\n```\n\n2. Inside `_document.js` (or `.tsx`) do the following:\n\n```js\nimport Document, { Html, Main, NextScript } from \"next/document\";\nimport React from \"react\";\nimport {\n  revalidate,\n  flushChunks,\n  ExtendedHead,\n  DevHotScript,\n} from \"@module-federation/nextjs-ssr/flushChunks\";\n\nclass MyDocument extends Document {\n  static async getInitialProps(ctx) {\n    // could also be on \"close\"\n    ctx?.res?.on(\"finish\", () =\u003e {\n      revalidate().then((willReload) =\u003e {\n        // choose any additional steps you want to take.\n        // the promise will only resolve if remotes have changed and a hot reload needs to happen\n        if (process.env.NODE_ENV === \"development\" \u0026\u0026 willReload) {\n          setTimeout(() =\u003e {\n            // useful for dev or if you want to cold start lambdas.\n            process.exit(1);\n          }, 50);\n        }\n      });\n    });\n    const initialProps = await Document.getInitialProps(ctx);\n    const remotes = await flushChunks(process.env.REMOTES);\n\n    return {\n      ...initialProps,\n      remoteChunks: remotes,\n    };\n  }\n\n  render() {\n    return (\n      \u003cHtml\u003e\n        \u003cExtendedHead\u003e\n          \u003cmeta name=\"robots\" content=\"noindex\" /\u003e\n          {Object.values(this.props.remoteChunks)}\n        \u003c/ExtendedHead\u003e\n        \u003cDevHotScript /\u003e {/* useful in development mode if you terminate processes on hot reload */}\n        \u003cbody className=\"bg-background-grey\"\u003e\n          \u003cMain /\u003e\n          \u003cNextScript /\u003e\n        \u003c/body\u003e\n      \u003c/Html\u003e\n    );\n  }\n}\n\nexport default MyDocument;\n```\n\n---\n\n## Configuring Pages for SSR\n\nTo enable SSR for pages, you will need to create an async bootstrap layer for each page and the `_app` file.\n\nSince Next.js reads all the pages from the `pages` directory, your pages and `_app` file will be the bootstrap file for the real pages defined in another directory.\n\n1. Bootstrap the `_app` and pages:\n\n```js\n// pages/_app.js\nimport dynamic from \"next/dynamic\";\nconst page = import(\"../async-pages/_app\");\n\nconst Page = dynamic(() =\u003e import(\"../async-pages/_app\"));\nPage.getInitialProps = async (ctx) =\u003e {\n  const getInitialProps = (await page).default?.getInitialProps;\n  if (getInitialProps) {\n    return getInitialProps(ctx);\n  }\n  return {};\n};\nexport default Page;\n```\n\n```js\n// pages/index.js\nimport dynamic from \"next/dynamic\";\nconst page = import(\"../async-pages/index\");\n\nconst Page = dynamic(() =\u003e import(\"../async-pages/index\"));\nPage.getInitialProps = async (ctx) =\u003e {\n  const getInitialProps = (await page).default?.getInitialProps;\n  if (getInitialProps) {\n    return getInitialProps(ctx);\n  }\n  return {};\n};\nexport default Page;\n```\n\n2. Now, create a directory for the real `_app` and pages. Let's call this directory `async-pages`:\n\n```js\n// async-pages/_app.js\nfunction MyApp({ Component, pageProps }) {\n  return \u003cComponent {...pageProps} /\u003e;\n}\n\nexport default MyApp;\n```\n\n```js\n// async-pages/index.js\nimport Head from \"next/head\";\n\nconst Home = () =\u003e (\n  \u003c\u003e\n    \u003cHead\u003e\n      \u003ctitle\u003eHome\u003c/title\u003e\n    \u003c/Head\u003e\n    \u003cmain\u003e\n      \u003ch1\u003eHome\u003c/h1\u003e\n    \u003c/main\u003e\n  \u003c/\u003e\n);\n\nHome.getInitialProps = async (ctx) =\u003e {\n  return {};\n};\n\nexport default Home;\n```\n\n---\n\n## Dynamic Routing between applications\n\nTo enable dynamic routes from another Next.js application, you will need to create a `[...slug].js` file in the `pages` directory as the async bootstrap file and one in the `async-pages` directory to handle the `createFederatedCatchAll` method.\n\n```js\n// async-pages/[...slug].js\nimport { createFederatedCatchAll } from \"@module-federation/next-catchall\";\n\nconst ErrorComponent = () =\u003e {\n  return \u003ch1\u003eThere was an error trying to load route\u003c/h1\u003e;\n};\nconst NotFoundComponent = () =\u003e {\n  return \u003ch1\u003e4OH4 not found\u003c/h1\u003e;\n};\nexport default createFederatedCatchAll(\n  process.env.REMOTES,\n  ErrorComponent,\n  NotFoundComponent\n);\n```\n\n---\n\n## Exposing and Consuming Pages\n\nJust like exposing components and other modules, pages can also be exposed from one Next.js application and consumed in another.\n\nTo expose a page, you will need to define a `pages-map` in the remote app.\n\n1. Create a `pages-map.js` (or `.ts`) file in your project root.\n\n```js\n// next2\n// pages-map.js\nconst pagesMap = {\n  \"/\": \"./home\", // \"route\": module \"key\" you're exposing\n};\n\nexport default pagesMap;\n```\n\n2. In your `next.config.js` file, expose the page from the remote app.\n\n```js\n// next2\n// next.config.js\nwithFederatedSidecar(\n  // normal MF config\n  {\n    name: \"next2\",\n    filename: \"static/chunks/remoteEntry.js\",\n    exposes: {\n      \"./home\": \"./async-pages/index.js\",\n      \"./pages-map\": \"./pages-map.js\",\n    },\n    remotes: {},\n    shared: {\n      react: {\n        requiredVersion: false,\n        singleton: true,\n      },\n    },\n  },\n  // sidecar specific options\n  {\n    experiments: {\n      flushChunks: true,\n    },\n  }\n);\n```\n\nTo import a federated page from a remote, you will need to add a page to your app that represents the federated page.\n\nAdd the page to your `pages` directory:\n\n```js\n// pages/index.js\nimport dynamic from \"next/dynamic\";\nconst page = import(\"home/home\");\n\nconst Page = dynamic(() =\u003e import(\"home/home\"));\nPage.getInitialProps = async (ctx) =\u003e {\n  const getInitialProps = (await page).default?.getInitialProps;\n  if (getInitialProps) {\n    return getInitialProps(ctx);\n  }\n  return {};\n};\nexport default Page;\n```\n\nThen add the remote to the `next.config.js` file.\n\n---\n\n## Support and Maintenance\n\nThis software is maintained by the Module Federation Group.\nThe primary maintainer is ScriptedAlchemy - the creator of Module Federation \u0026 an official member of the Webpack Foundation\n\nWorldwide, there are only about 10 engineers capable of creating reliable and safe extensions of Module Federation.\n\nThe Federation Group provides 5 of the 10 engineers capable of creating solid implementations.\n\nAny Federation Package that is not from The Webpack Foundation or Module Federation Group should be used with caution!\n\nThis software is actively used in production at our place of employment. It must be maintained at all times otherwise our production applications will fail.\n\nWorries about reliability or contingency plans should not be a concern for end-users.\n\nIf this package fails, it will cost our employer millions of dollars a day. It will not be abandoned, it cannot be.\n\n#### What if ScriptedAlchemy is hit by a bus?\n\nWhile that would be unfortunate, the Federation Group is more than capable of maintaining this package.\n\nAll group members have full access to the organization and all its source code, registry authentication.\n\nScriptedAlchemy is the primary maintainer, not the only maintainer - this software is not dependent on \"one person\"\n\n---\n\n## Reliability Testing\n\nThis software undergoes significant QA, SRE, Security Audits, Performance testing with our employer.\n\nRUM data and other telemetry is heavily implemented and monitored closely.\n\nOur software is backed with the resources of a multi-billion dollar corporation.\n\n---\n\n## Contact\n\nIf you have any questions or need to report a bug\n\u003ca href=\"https://twitter.com/ScriptedAlchemy\"\u003e Reach me on Twitter @ScriptedAlchemy\u003c/a\u003e\n\nOr join this discussion thread: https://github.com/module-federation/module-federation-examples/discussions/1482\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmodule-federation%2Fnextjs-ssr","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fmodule-federation%2Fnextjs-ssr","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmodule-federation%2Fnextjs-ssr/lists"}