diff --git a/packages/docusaurus-module-type-aliases/src/index.d.ts b/packages/docusaurus-module-type-aliases/src/index.d.ts index 5876e36484..1751e3c9e6 100644 --- a/packages/docusaurus-module-type-aliases/src/index.d.ts +++ b/packages/docusaurus-module-type-aliases/src/index.d.ts @@ -113,6 +113,10 @@ declare module '@theme/NotFound' { export default function NotFound(): ReactNode; } +declare module '@theme/NotFound/Content' { + export default function NotFoundContent(): JSX.Element; +} + declare module '@theme/Root' { import type {ReactNode} from 'react'; diff --git a/packages/docusaurus-plugin-content-docs/src/routes.ts b/packages/docusaurus-plugin-content-docs/src/routes.ts index cde7d88312..42609b909e 100644 --- a/packages/docusaurus-plugin-content-docs/src/routes.ts +++ b/packages/docusaurus-plugin-content-docs/src/routes.ts @@ -234,6 +234,14 @@ export async function buildAllRoutes( }), ), ); + // Add a catch-all route for 404 support if routeBasePath is "/". + // (see https://github.com/facebook/docusaurus/issues/9688) + if (param.options.routeBasePath === '/') { + subRoutes.push({ + path: '/*', + component: '@docusaurus/ComponentCreator', + }); + } // all docs routes are wrapped under a single parent route, this ensures // the theme layout never unmounts/remounts when navigating between versions diff --git a/packages/docusaurus/src/client/exports/ComponentCreator.tsx b/packages/docusaurus/src/client/exports/ComponentCreator.tsx index e0664bec5e..65b5e1aded 100644 --- a/packages/docusaurus/src/client/exports/ComponentCreator.tsx +++ b/packages/docusaurus/src/client/exports/ComponentCreator.tsx @@ -43,6 +43,24 @@ export default function ComponentCreator( }, }); } + if (path.endsWith('/*')) { + return Loadable({ + loading: Loading, + loader: () => import('@theme/NotFound/Content'), + modules: ['@theme/NotFound/Content'], + webpack: () => [require.resolveWeak('@theme/NotFound/Content')], + render(loaded, props) { + const NotFoundContent = loaded.default; + return ( + + + + ); + }, + }); + } const chunkNames = routesChunkNames[`${path}-${hash}`]!; // eslint-disable-next-line @typescript-eslint/no-explicit-any diff --git a/packages/docusaurus/src/server/plugins/routeConfig.ts b/packages/docusaurus/src/server/plugins/routeConfig.ts index 7832499ecc..c293730912 100644 --- a/packages/docusaurus/src/server/plugins/routeConfig.ts +++ b/packages/docusaurus/src/server/plugins/routeConfig.ts @@ -43,6 +43,14 @@ export function sortRoutes( return -1; } + // Wildcard also should get placed last + if (a.path.endsWith('/*')) { + return 1; + } + if (b.path.endsWith('/*')) { + return -1; + } + if (a.routes && !b.routes) { return 1; }