What a monolith can serve

Every mounted app's routes have to fit in one tree, and a path can only belong to one of them. Most distributions never run into that — a mount name is a whole path segment, and apps rarely claim each other's — but the ones that do cannot be a monolith at all.

An app at the site root

An app mounted as www would serve /www/about. A marketing site wants /about, so give it the root:

withMonolith(config, {
  www: { package: '@fairgarden/www', prefix: '/' },
  id: '@fairgarden/id',
})

www then supplies / and /about while id stays at /id/login. Its paths are not prefixed at all, and its files sit alongside the monolith's own rather than under a directory of their own — so its layout.tsx becomes the monolith's root layout.

That last part means the monolith cannot also have one, and only one app can take the root.

Collisions are refused, not resolved

Before anything is mounted, withMonolith works out what each app would serve and refuses when two of them, or an app and the monolith, want the same path:

These apps cannot be served from one deployment:
  - "www" and "id" both serve src/app/id (routes).
A path can only belong to one of them. Rename a mount, or deploy the apps
separately — a subdomain each — rather than as a monolith.

It covers routes, static assets and Pages Router api routes, and it runs before any file is written — a colliding mount would otherwise quietly replace whatever was there.

Finding this out at build time is the point. An app that cannot share a path tree is one to deploy on its own, a subdomain each; a distribution scaffolded with --separate is exactly that shape.

Links are not prefixed for you

basePath is not used, so an app's own hrefs are not rewritten. A monolith deployment therefore only works on a wildcard domain or a subdomain, and an app that builds absolute paths has to prefix them itself — see createLink and mountPrefix.

Extending next/link is the alternative, and may be needed anyway for custom localization and similar.