diff --git a/packages/docusaurus-module-type-aliases/src/index.d.ts b/packages/docusaurus-module-type-aliases/src/index.d.ts index 5cb44f06e402..126b3e5878c4 100644 --- a/packages/docusaurus-module-type-aliases/src/index.d.ts +++ b/packages/docusaurus-module-type-aliases/src/index.d.ts @@ -102,6 +102,10 @@ declare module '@theme/NotFound' { export default function NotFound(): JSX.Element; } +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 0b7a6425594f..d78953fdcd0c 100644 --- a/packages/docusaurus-plugin-content-docs/src/routes.ts +++ b/packages/docusaurus-plugin-content-docs/src/routes.ts @@ -226,6 +226,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 33c0f4c90a6d..88cebc261b3c 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 d757406bd09b..5822c2b23626 100644 --- a/packages/docusaurus/src/server/plugins/routeConfig.ts +++ b/packages/docusaurus/src/server/plugins/routeConfig.ts @@ -42,6 +42,14 @@ export function sortConfig( 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; }