diff --git a/packages/docusaurus-theme-openapi-docs/src/theme/ApiExplorer/MethodEndpoint/index.tsx b/packages/docusaurus-theme-openapi-docs/src/theme/ApiExplorer/MethodEndpoint/index.tsx
index 95fc62dc8..0c2b3bc0d 100644
--- a/packages/docusaurus-theme-openapi-docs/src/theme/ApiExplorer/MethodEndpoint/index.tsx
+++ b/packages/docusaurus-theme-openapi-docs/src/theme/ApiExplorer/MethodEndpoint/index.tsx
@@ -8,6 +8,7 @@
import React from "react";
import BrowserOnly from "@docusaurus/BrowserOnly";
+import ExecutionEnvironment from "@docusaurus/ExecutionEnvironment";
import { useTypedSelector } from "@theme/ApiItem/hooks";
function colorForMethod(method: string) {
@@ -38,6 +39,27 @@ export interface Props {
}
function MethodEndpoint({ method, path, context }: Props) {
+ // SSR-safe: During server-side rendering, render without Redux store access
+ // This fixes React 19 compatibility where useSelector fails during SSR
+ // because the Redux context is not properly propagated with react-redux v7.x
+ if (!ExecutionEnvironment.canUseDOM) {
+ return (
+ <>
+
+
+ {method === "event" ? "Webhook" : method.toUpperCase()}
+ {" "}
+ {method !== "event" && (
+
+ {`${path.replace(/{([a-z0-9-_]+)}/gi, ":$1")}`}
+
+ )}
+
+
+ >
+ );
+ }
+
let serverValue = useTypedSelector((state: any) => state.server.value);
let serverUrlWithVariables = "";
diff --git a/packages/docusaurus-theme-openapi-docs/src/theme/ApiItem/hooks.ts b/packages/docusaurus-theme-openapi-docs/src/theme/ApiItem/hooks.ts
index 3c015b978..eaf95e0b2 100644
--- a/packages/docusaurus-theme-openapi-docs/src/theme/ApiItem/hooks.ts
+++ b/packages/docusaurus-theme-openapi-docs/src/theme/ApiItem/hooks.ts
@@ -5,9 +5,46 @@
* LICENSE file in the root directory of this source tree.
* ========================================================================== */
+import { useState, useEffect } from "react";
import { TypedUseSelectorHook, useDispatch, useSelector } from "react-redux";
import type { RootState, AppDispatch } from "./store";
-export const useTypedDispatch = () => useDispatch();
-export const useTypedSelector: TypedUseSelectorHook = useSelector;
+// Safe wrapper for useDispatch that returns a no-op when Redux context is unavailable
+// This fixes React 19 compatibility where the Redux context may not be available
+// during SSR or initial hydration with react-redux v7.x
+export const useTypedDispatch = (): AppDispatch => {
+ try {
+ return useDispatch();
+ } catch (e) {
+ // Return a no-op dispatch function when Redux context is not available
+ return (() => {}) as unknown as AppDispatch;
+ }
+};
+
+// Safe wrapper for useSelector that returns undefined when Redux context is unavailable
+// This fixes React 19 compatibility where useSelector fails during SSR
+// because the Redux context is not properly propagated with react-redux v7.x
+export const useTypedSelector: TypedUseSelectorHook = ((
+ selector: (state: RootState) => unknown
+) => {
+ const [state, setState] = useState(undefined);
+ const [isReady, setIsReady] = useState(false);
+
+ // Try to get the Redux context
+ let reduxState: unknown = undefined;
+ try {
+ reduxState = useSelector(selector);
+ } catch (e) {
+ // Redux context not available - will return undefined
+ }
+
+ useEffect(() => {
+ if (reduxState !== undefined) {
+ setState(reduxState);
+ setIsReady(true);
+ }
+ }, [reduxState]);
+
+ return isReady ? state : reduxState;
+}) as TypedUseSelectorHook;
diff --git a/packages/docusaurus-theme-openapi-docs/src/theme/Schema/index.tsx b/packages/docusaurus-theme-openapi-docs/src/theme/Schema/index.tsx
index be9f9a289..f6ad639c0 100644
--- a/packages/docusaurus-theme-openapi-docs/src/theme/Schema/index.tsx
+++ b/packages/docusaurus-theme-openapi-docs/src/theme/Schema/index.tsx
@@ -128,6 +128,13 @@ interface SchemaProps {
const AnyOneOf: React.FC = ({ schema, schemaType }) => {
const key = schema.oneOf ? "oneOf" : "anyOf";
+ const schemaArray = schema[key];
+
+ // Handle empty oneOf/anyOf arrays - return null to avoid empty Tabs error
+ if (!schemaArray || !Array.isArray(schemaArray) || schemaArray.length === 0) {
+ return null;
+ }
+
const type = schema.oneOf
? translate({ id: OPENAPI_SCHEMA_ITEM.ONE_OF, message: "oneOf" })
: translate({ id: OPENAPI_SCHEMA_ITEM.ANY_OF, message: "anyOf" });
diff --git a/packages/docusaurus-theme-openapi-docs/src/theme/SchemaTabs/index.tsx b/packages/docusaurus-theme-openapi-docs/src/theme/SchemaTabs/index.tsx
index 12cc1167c..91f62c8fa 100644
--- a/packages/docusaurus-theme-openapi-docs/src/theme/SchemaTabs/index.tsx
+++ b/packages/docusaurus-theme-openapi-docs/src/theme/SchemaTabs/index.tsx
@@ -212,8 +212,37 @@ function TabsComponent(props: TabProps): React.JSX.Element {
);
}
-export default function SchemaTabs(props: TabProps): React.JSX.Element {
+export default function SchemaTabs(props: TabProps): React.JSX.Element | null {
const isBrowser = useIsBrowser();
+
+ // Filter out null/undefined children before sanitizing
+ const children = Array.isArray(props.children)
+ ? props.children.filter(Boolean)
+ : props.children
+ ? [props.children]
+ : [];
+
+ // Return null if no valid children to avoid "Tabs requires at least one TabItem" error
+ if (children.length === 0) {
+ return null;
+ }
+
+ let sanitizedChildren;
+ try {
+ sanitizedChildren = sanitizeTabsChildren(children);
+ } catch (e) {
+ // If sanitization fails (no valid TabItem children), return null
+ return null;
+ }
+
+ // Additional check - if sanitization returns empty/invalid, return null
+ if (
+ !sanitizedChildren ||
+ (Array.isArray(sanitizedChildren) && sanitizedChildren.length === 0)
+ ) {
+ return null;
+ }
+
return (
- {sanitizeTabsChildren(props.children)}
+ {sanitizedChildren}
);
}