Nextra
Embed <OmniSpecRenderer> in a Nextra site. Nextra is
Next.js + MDX and React-based, so you embed the React component directly —
no Web Component needed.
When to use
Use this guide when you are building a Nextra docs site. Because Next.js server-renders (and statically generates) pages and the renderer is client-only, the one load-bearing requirement is that it must mount client-side only.
Install
npm install @apiboost/omnispec
React and ReactDOM are already provided by Next.js, so nothing else is required.
Minimal working example
Create a small client component and mount it only in the browser. There are two equivalent ways to keep it client-only under Next SSR.
Option A — a 'use client' component. Extract the renderer into its own
file marked 'use client', then import it into your MDX page:
// components/ApiReference.tsx
'use client'
import {OmniSpecRenderer} from '@apiboost/omnispec'
export default function ApiReference() {
return (
<div style={{height: '100vh'}}>
<OmniSpecRenderer spec="https://petstore3.swagger.io/api/v3/openapi.json" />
</div>
)
}
Option B — next/dynamic with ssr: false. Skip server rendering of the
component entirely:
// components/ApiReference.tsx
import dynamic from 'next/dynamic'
const OmniSpecRenderer = dynamic(
() => import('@apiboost/omnispec').then((m) => m.OmniSpecRenderer),
{ssr: false, loading: () => <div>Loading the API reference…</div>},
)
export default function ApiReference() {
return (
<div style={{height: '100vh'}}>
<OmniSpecRenderer spec="/openapi.json" />
</div>
)
}
A client-only mount is mandatory. The renderer relies on browser APIs (DOM,
fetch, IntersectionObserver) that do not exist during the build, so a plain
server-rendered import breaks next build.
Embedding in an MDX page
Import the wrapper component into any .mdx doc and render it like a normal
component:
import ApiReference from '../components/ApiReference'
# API Reference
<ApiReference />
Keep the wrapper as the client boundary (Option A or B above) so the MDX page itself stays a normal server-rendered doc.
Theme sync
Passing an explicit base ('light' | 'dark') puts the renderer in
controlled mode: it follows the value you give it and hides its own theme
toggle, so Nextra's header switcher stays the single source of truth. Nextra
uses next-themes under the hood,
so read the resolved theme with useTheme():
// components/ApiReference.tsx
'use client'
import {useTheme} from 'next-themes'
import {OmniSpecRenderer} from '@apiboost/omnispec'
export default function ApiReference() {
const {resolvedTheme} = useTheme() // 'light' | 'dark'
return (
<div style={{height: '100vh'}}>
<OmniSpecRenderer
spec="/openapi.json"
theme={{base: resolvedTheme === 'dark' ? 'dark' : 'light'}}
/>
</div>
)
}
Use resolvedTheme (not theme) so the system setting resolves to a concrete
light/dark. If you would rather not wire the hook, pass theme={{base: 'auto'}} to follow the system preference directly.
Sizing and the sidebar
The renderer ships its own sidebar navigation. Give its container a tall,
explicit height (height: 100vh) — the renderer fills its parent, and a short
container leaves it cramped. Because Nextra keeps its own doc sidebar on the
page, add layout="stacked" to drop the renderer's sidebar and avoid a double
sidebar:
<OmniSpecRenderer spec="/openapi.json" layout="stacked" />
Next steps
- Integrations overview — pick the right entry point
- App-framework recipes — more Next.js patterns
- Configuration — layouts, navigation, display modes
- Theming — design tokens and white-labeling
- API Reference — all props and types