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;
}