Add OpenAPI Docs to a Next.js App
The recurring error people hit trying to embed any browser-only OpenAPI viewer in Next.js is some
variant of window is not defined or a hydration mismatch — because App Router renders components on the
server by default, and GetMan’s script needs document and window to mount. The fix is the same one
Next.js uses for any browser-only widget: isolate it in a client component.
App Router
Create the client component — note the 'use client' directive at the top, which is what tells Next.js
this component (and only this one) should skip server rendering:
// app/api-docs/ApiDocs.tsx
'use client';
import { useEffect, useRef } from 'react';
export function ApiDocs({ specUrl }: { specUrl: string }) {
const containerRef = useRef<HTMLDivElement>(null);
useEffect(() => {
const script = document.createElement('script');
script.src = 'https://cdn.getman.dev/latest/getman-ui.js';
script.onload = () => {
window.GetMan.launch(containerRef.current, { url: specUrl });
};
document.head.appendChild(script);
}, [specUrl]);
return <div ref={containerRef} style={{ height: '100vh' }} />;
}
Then use it from an ordinary (server) page component — the page itself doesn’t need 'use client',
only the leaf component that touches the DOM does:
// app/api-docs/page.tsx
import { ApiDocs } from './ApiDocs';
export default function Page() {
return <ApiDocs specUrl="https://your-api.com/openapi.json" />;
}
Pages Router
The same ApiDocs component works unchanged under pages/ — the Pages Router doesn’t use React Server
Components, so there’s no server/client split to think about and the 'use client' directive is simply
ignored if you leave it in.
// pages/api-docs.tsx
import { ApiDocs } from '../components/ApiDocs';
export default function ApiDocsPage() {
return <ApiDocs specUrl="https://your-api.com/openapi.json" />;
}
Why this avoids the mount error
useEffect only runs in the browser, after hydration — so the script tag is never injected during
server rendering, and there’s nothing for Next.js to try to hydrate mismatched against. If you see the
error anyway, double-check that the DOM-touching code isn’t sitting directly in the component body (it
needs to be inside useEffect) and that the component is actually marked 'use client'.
This is the same class of fix used in the plain React guide and the Vue/Nuxt guide — any framework with server rendering needs the DOM-touching code deferred until after mount.
Try it with your own spec
Paste any OpenAPI or Swagger URL and see it rendered live — no signup, no install.
Open the explorer