Relevant source files
The following files were used as context for generating this wiki page:
- packages/openapi/src/ui/operation/index.tsx
- packages/openapi/src/ui/base.tsx
- packages/asyncapi/src/ui/operation/index.tsx
- packages/api-docs/src/components/schema/client.tsx
- packages/asyncapi/src/ui/index.tsx
- packages/openapi/src/server/index.tsx
- packages/openapi/src/ui/operation/usage-tabs.tsx
- packages/openapi/src/scalar/client.tsx
- packages/openapi/src/ui/operation/request-tabs.tsx
- packages/openapi/src/ui/index.tsx
- packages/api-docs/src/components/schema/index.tsx
- packages/openapi/src/ui/operation/response-tabs.tsx
- packages/asyncapi/src/ui/api-page.tsx
- packages/openapi/src/scalar/index.tsx
- packages/asyncapi/src/ui/bindings/protocols/http.tsx
- packages/asyncapi/src/server/index.tsx
- packages/openapi/src/index.ts
- packages/asyncapi/src/ui/bindings/shared.tsx
- packages/asyncapi/src/ui/contexts/api.tsx
- packages/asyncapi/src/ui/bindings/accordion-bindings.tsx
- packages/openapi/src/ui/components/markdown.tsx
- packages/openapi/src/ui/contexts/api.tsx
- packages/openapi/src/utils/get-example-requests.ts
- packages/openapi/src/ui/components/codeblock.tsx
- packages/asyncapi/src/ui/components/markdown.tsx
- packages/openapi/src/ui/operation/context.tsx
- packages/openapi/src/types.ts
- packages/openapi/src/requests/media/encode.ts
- packages/radix-ui/src/components/ui/popover.tsx
Overview
OpenAPI Interactive UI provides a component-based documentation rendering layer built on top of parsed OpenAPI and Swagger specifications, parsing Markdown processors, server state providers, and customizable page layouts into interactive API documentation pages. Sources: packages/openapi/src/ui/base.tsx, packages/openapi/src/server/index.tsx, packages/openapi/src/ui/index.tsx
By leveraging API Docs Primitives and Code Sample Generators, the system parses endpoint operations, request bodies, response status codes, and security schemes into rich interactive cards, tabbed request and response viewers, and copy-enabled code blocks. Sources: packages/openapi/src/ui/operation/index.tsx, packages/openapi/src/ui/operation/request-tabs.tsx, packages/openapi/src/ui/operation/response-tabs.tsx
Page Creation and Runtime Context
Initialization and Rendering Pipeline
OpenAPI documentation pages initialize their rendering contexts, Markdown processors, and server state by invoking base factory functions that construct client components. The primary initialization sequence proceeds through the following call chain: createOpenAPIPageBase → Schema → SchemaUI.
createOpenAPIPageBaseestablishes the base React component, sets up the remark and rehype Markdown processing pipeline withremarkGfmandremarkRehype, dereferences bundled documents usingdereferenceBundledDocument, and configures theRenderContext. Sources: packages/openapi/src/ui/base.tsxSchemareceives the resolved schema and user options, invokesgenerateSchemaUIto compute schema hierarchies and reference tables, and passes the generated data to the client component. Sources: packages/api-docs/src/components/schema/index.tsxSchemaUIestablishes anchor identifiers usinguseAnchorId, tracks object inspection navigation paths via component state, and hydrates interactive popovers or object property trees. Sources: packages/api-docs/src/components/schema/client.tsx
Sources: packages/openapi/src/ui/base.tsx:38-98, packages/api-docs/src/components/schema/index.tsx:89-112, packages/api-docs/src/components/schema/client.tsx:83-184
Note
createOpenAPIPageBase accepts an optional shiki factory for syntax highlighting alongside custom options for generating TypeScript definitions via compile from @fumari/json-schema-ts. Sources: packages/openapi/src/ui/base.tsx
Server State and Context Providers
Server state management is coordinated through ServerProvider and RenderContextProvider, which manage active API servers, URL variables, and local storage persistence. The context infrastructure relies on specific React contexts to supply runtime options across operation cards and schema renderers.
| Context Provider | Hook | Purpose |
|---|---|---|
RenderContextProvider | useRenderContext() | Supplies the global RenderContext containing schema references, proxy URLs, Shiki options, and code usage generators. |
ServerProvider | useServerContext() | Manages active server selection, server variables, and persists selected configurations in local storage. |
Sources: packages/openapi/src/ui/contexts/api.tsx:20-45, packages/openapi/src/ui/base.tsx:32-32
Warning
Both useRenderContext and useServerContext throw an error if called outside of their respective provider hierarchy, requiring components to be nested under <ApiProvider /> or the corresponding provider wrapper. Sources: packages/openapi/src/ui/contexts/api.tsx
Tip
ServerProvider initializes the active server configuration using storageKeyPrefix (defaulting to fumadocs-openapi- when using useStorageKey) to prevent state conflicts when multiple OpenAPI instances are mounted on the same origin. Sources: packages/openapi/src/ui/contexts/api.tsx, packages/openapi/src/ui/index.tsx
Interactive Operation Cards
Structural Decomposition and Operation Rendering
The Operation component serves as the core layout engine for individual HTTP endpoint cards and webhooks. It accepts properties defining the operation structure, including path, method, operation, pathItem, type, and headingLevel. When showTitle is enabled, the component computes the endpoint title using operation.summary, pathItem.summary, a humanized version of operation.operationId via idToTitle, or falls back to the raw path. If operation.deprecated is true alongside a title, a yellow status badge is rendered; when titles are hidden, deprecation warnings render as an inline callout.
export function Operation({
type = 'operation',
path,
operation,
pathItem,
method,
showTitle,
showDescription,
headingLevel = 2,
}: {
type?: 'webhook' | 'operation';
path: string;
method: HttpMethods;
operation: NoReference<OperationObject>;
pathItem: NoReference<PathItemObject>;
showTitle?: boolean;
showDescription?: boolean;
headingLevel?: number;
}) {
const t = useTranslations({ note: 'operation page' });
const ctx = useRenderContext();
const {
schema: { dereferenced },
} = ctx;
const body = operation.requestBody;
let headNode: ReactNode = null;
const operationDescription = operation.description ?? pathItem.description;
const descriptionNode = showDescription && operationDescription && (
<Markdown md={operationDescription} />
);
let bodyNode: ReactNode = null;
let authNode: ReactNode = null;
let responseNode: ReactNode = null;
let callbacksNode: ReactNode = null;
const exampleRequests = useMemo(
() => getExampleRequests({ path, operation, method, pathItem, ctx }),
[ctx, operation, method, pathItem, path],
);
if (showTitle) {
const title =
operation.summary ||
pathItem.summary ||
(operation.operationId ? idToTitle(operation.operationId) : path);
headNode = (
<div className="flex gap-2 items-center justify-between">
<Heading id={title} depth={headingLevel} className="my-0!">
{title}
</Heading>
{operation.deprecated && (
<Badge color="yellow" className="text-xs not-prose">
{t('Deprecated')}
</Badge>
)}
</div>
);
headingLevel++;
} else if (operation.deprecated) {
headNode = <Callout type="warn" title={t('Deprecated')} className="mt-0!" />;
}
Sources: packages/openapi/src/ui/operation/index.tsx:42-104
Warning
Media types defined within operation.requestBody are validated against registered media adapters via isMediaTypeSupported(type, ctx.mediaAdapters). If an unsupported media type is encountered, an immediate error is thrown: Media type ${type} is not supported (in ${path}). Sources: packages/openapi/src/ui/operation/index.tsx
Authentication Schemes and Security Contexts
Operations evaluate security requirements by checking operation.security with a fallback to dereferenced.security. Empty security object entries are filtered out. When securities are present on standard operations (type === 'operation'), they populate a tabbed selection interface driven by SelectTabs, mapping each security definition entry to its configured scopes and credential schemes retrieved from dereferenced.components?.securitySchemes.
const securities = (operation.security ?? dereferenced.security ?? []).filter(
(v) => Object.keys(v).length > 0,
);
if (type === 'operation' && securities.length > 0) {
const securitySchemes = dereferenced.components?.securitySchemes;
const items = securities.map((security, i) => {
return {
value: String(i),
label: (
<div className="flex flex-col text-xs min-w-0">
{Object.entries(security).map(([key, scopes]) => (
<code key={key} className="truncate">
<span className="font-medium">{key}</span>{' '}
{scopes.length > 0 && (
<span className="text-fd-muted-foreground">{scopes.join(', ')}</span>
)}
</code>
))}
</div>
),
};
});
authNode = (
<SelectTabs defaultValue={items[0].value}>
<div className="flex items-start justify-between gap-2 mt-10">
<Heading id="authorization" depth={headingLevel} className="my-0!">
{t('Authorization')}
</Heading>
{items.length > 1 ? (
<SelectTabTrigger items={items} />
) : (
<div className="not-prose">{items[0].label}</div>
)}
</div>
{securities.map((security, i) => (
<SelectTab key={i} value={items[i].value}>
{Object.entries(security).map(([key, scopes]) => {
const scheme = securitySchemes?.[key];
if (!scheme) return;
return <AuthScheme key={key} scheme={scheme} scopes={scopes} />;
})}
</SelectTab>
))}
</SelectTabs>
);Sources: packages/openapi/src/ui/operation/index.tsx:208-255
Note
The Markdown component delegates parsing to processMarkdown(md) from useRenderContext() wrapped in a useMemo hook, supporting custom Markdown component overrides or direct return values from renderMarkdown. Sources: packages/openapi/src/ui/components/markdown.tsx
Operation Context and State Management
The OperationProvider component wraps interactive operation components, maintaining local state for selected examples and dispatching updates through a listener registry (ExampleUpdateListener[]). When setExample or setExampleData is invoked, registered listeners receive raw request payload data and encoded request representations to synchronize preview tabs and code generators.
export function OperationProvider({
route,
examples,
defaultExampleId,
children,
}: {
route: string;
examples: ExampleRequestItem[];
defaultExampleId?: string;
children: ReactNode;
}) {
const [example, setExample] = useState(() => defaultExampleId ?? examples.at(0)?.id);
const listeners = useRef<ExampleUpdateListener[]>([]);
return (
<OperationContext
value={useMemo(
() => ({
example,
route,
setExample(newKey: string) {
const example = examples.find((example) => example.id === newKey);
if (!example) return;
setExample(newKey);
for (const listener of listeners.current) {
listener(example.data, example.encoded);
}
},
examples,
setExampleData(data, encoded) {
for (const item of examples) {
if (item.id === example) {
// persistent changes
item.data = data;
item.encoded = encoded;
break;
}
}
for (const listener of listeners.current) {
listener(data, encoded);
}
},
removeListener(listener) {
listeners.current = listeners.current.filter((item) => item !== listener);
},
addListener(listener) {
// initial call to listeners to ensure their data is the latest
// this is necessary to avoid race conditions between `useEffect()`
const active = examples.find((item) => item.id === example)!;
listener(active.data, active.encoded);
listeners.current.push(listener);
},
}),
[example, route, examples],
)}
>
{children}
</OperationContext>
);
}Sources: packages/openapi/src/ui/operation/context.tsx:20-82
Tip
OperationProvider calls all registered listeners immediately upon registration in addListener using the currently active example configuration, preventing race conditions with asynchronous useEffect hooks. Sources: packages/openapi/src/ui/operation/context.tsx
Interactive Schema Viewer Popovers
Call-Chain Execution Walkthrough
Schema inspection relies on an anchored popover architecture defined across shared packages. The trace order begins at page initialization and terminates at the popover container:
createOpenAPIPageBasebuilds the render context and passes the resolved document schema. Sources: packages/openapi/src/ui/base.tsxSchemareceivesroot,resolver, andrenderMarkdownoptions, memoizing generated schema structures. Sources: packages/api-docs/src/components/schema/index.tsxSchemaUIestablishes anchor identifiers viauseAnchorIdand manages the current path inspection stack. Sources: packages/api-docs/src/components/schema/client.tsxPopoverContentrenders the floating inspection container using accessible primitives from Radix UI Primitives. Sources: packages/radix-ui/src/components/ui/popover.tsx
Sources: packages/openapi/src/ui/base.tsx:38-127, packages/api-docs/src/components/schema/index.tsx:89-112, packages/api-docs/src/components/schema/client.tsx:83-185, packages/radix-ui/src/components/ui/popover.tsx:10-30
Note
ExcludedFromAutoAnchor tracks parsed root identifiers in a global Set<string> to prevent infinite loops when matching hash parameters in URL search strings against active component paths. Sources: packages/api-docs/src/components/schema/client.tsx
State Management and Popover Configuration
The interactive schema viewer utilizes shared state context (StateContextType) to track root identifiers, traversal paths, and generated schema references. The table below outlines the core properties managed by path items during interactive inspection.
| Property | Type | Description |
|---|---|---|
name | string | The property or field name being inspected. |
$ref | string | Reference identifier pointing to the schema definition. |
scrollTop | number (optional) | Scroll position preserved across popover transitions. |
highlighted | string (optional) | Property name of the highlighted field for object structures. |
tabValues | string[] (optional) | Active tab values for nested representations. |
closed | boolean (optional) | Explicit popover closure state for the root item. |
Sources: packages/api-docs/src/components/schema/client.tsx:34-56
Tip
When PopoverContent mounts or updates, popoverRef calculates a minimum height threshold using element.clientHeight + 2 and restores the prior scrollTop coordinate from the active path node. Sources: packages/api-docs/src/components/schema/client.tsx
Request Parameter Encoding
Execution Walkthrough and Sequence
Request parameters and query strings are prepared during the initial rendering cycle of each operation component OpenAPI Interactive UI. The serialization workflow follows a strict execution path starting from the component and passing through request generation, encoding wrappers, and specific parameter serializers:
Operationinitiates parameter collection fromoperation.parametersandpathItem.parameters, passing them along with context togetExampleRequests. Sources: packages/openapi/src/ui/operation/index.tsxgetExampleRequestscallsgetRequestDatato construct raw request objects via example picking or schema sampling, and then invokesencodeRequestData. Sources: packages/openapi/src/utils/get-example-requests.tsencodeRequestDataloops over parameter locations (cookie,query,header,path), resolves media encoders, and dispatches parameters to type-specific formatters. Sources: packages/openapi/src/requests/media/encode.tsserializeQueryParameterinspects the parameter style and explosion rules, processingspaceDelimited,pipeDelimited,deepObject, arrays, and objects into final query representations. Sources: packages/openapi/src/requests/media/encode.ts
Sources: packages/openapi/src/ui/operation/index.tsx:77-80, packages/openapi/src/utils/get-example-requests.ts:45-74, packages/openapi/src/requests/media/encode.ts
Warning
When deepObject style is evaluated for query parameters, nested array behaviors are explicitly treated as undefined by wrapping values with Array.isArray(v) ? v : [String(v)] to prevent unexpected formatting edge cases. Sources: packages/openapi/src/requests/media/encode.ts
Parameter Serialization Strategies
The encoding module handles distinct parameter locations and styles according to OpenAPI specifications. The table below lists the serialization rules and style handlers implemented in the encoder.
| Parameter Type / Style | Explode Default | Behavior Description |
|---|---|---|
path / label | false | Prefixes the encoded string with a period (.) using comma or dotted separators. |
path / matrix | false | Prefixes parameters with semicolon specifiers (;name=) for URI path matrix variables. |
query / spaceDelimited | false | Joins array elements using a single space character ( ) when explosion is disabled. |
query / pipeDelimited | false | Joins array elements using a pipe character (` |
query / deepObject | true | Expands nested object properties into bracketed query keys (param[key]=value). |
header / simple | false | Joins array and object values using comma delimiters based on the explode flag. |
Sources: packages/openapi/src/requests/media/encode.ts:83-252
Tip
If a parameter defines custom media adapters via its content field, getMediaEncoder resolves and executes the matching adapter rather than falling back to standard style serialization routines. Sources: packages/openapi/src/requests/media/encode.ts
Request and Response Tabs
Request Tab Rendering and Data Display
The RequestTabs component checks whether an operation defines a requestBody object before rendering. If a request body is present, it delegates to renderRequestTabs, falling back to renderRequestTabsDefault. When multiple example items exist, it wraps them in a Fumadocs UI Tabs component using each item's unique identifier as its value key. For single items, it renders the item directly, and when no examples are found, it outputs an empty state text.
Sources: packages/openapi/src/ui/operation/request-tabs.tsx:30-97
The RequestTabsItem component extracts requestData from each example item and populates a map of display names for parameter categories and bodies.
| Request Data Field | Display Label / Source |
|---|---|
body | Request Body (with badge showing requestData.bodyMediaType) |
cookie | Cookie Parameters |
header | Header Parameters |
query | Query Parameters |
path | Path Parameters |
Sources: packages/openapi/src/ui/operation/request-tabs.tsx:106-121
Each available parameter or body category renders inside an accordion item containing a syntax-highlighted JSON code block.
Sources: packages/openapi/src/ui/operation/request-tabs.tsx:131-148
Note
If a request data category contains no keys or evaluates to falsy, RequestTabsItem skips rendering that accordion section entirely. Sources: packages/openapi/src/ui/operation/request-tabs.tsx
Response Tabs and Schema Sampling
The ResponseTabs component iterates over all response objects defined on an operation, extracting preferred media types and generating example payloads using schema sampling or pre-defined examples.
Sources: packages/openapi/src/ui/operation/response-tabs.tsx:53-103
| Property | Type | Description |
|---|---|---|
code | string | HTTP response code (e.g., 200, 400). |
response | NoReference<ResponseObject> | The underlying OpenAPI response specification object. |
mediaType | string | null | Preferred media type resolved via getPreferredType. |
examples | ResponseExample[] | Array of generated or defined response example items. |
Sources: packages/openapi/src/ui/operation/response-tabs.tsx:20-33
Response tabs render as a group of tabs keyed by HTTP status code. When a response defines multiple examples, they are organized within a single-selection accordion component defaulting to the first index.
Sources: packages/openapi/src/ui/operation/response-tabs.tsx:115-159
Warning
If a response object specifies neither explicit examples nor a schema, response tab generation yields an empty array, causing ResponseTabs to return null. Sources: packages/openapi/src/ui/operation/response-tabs.tsx
Client Code Blocks and Usage Generation
The UsageTabs component initializes a code usage generator registry using createCodeUsageGeneratorRegistry, appending inline generators from context samples and x-codeSamples extensions on the operation object.
Sources: packages/openapi/src/ui/operation/usage-tabs.tsx:30-90
The ClientCodeBlock component abstracts syntax highlighting by checking render context overrides before falling back to DynamicCodeBlock with Shiki initialization.
Sources: packages/openapi/src/ui/components/codeblock.tsx:8-21
Alternative Scalar Client Integration
The withScalar Wrapper and Client Component
Fumadocs supports delegating endpoint interaction and testing to the @scalar/api-client-react library by wrapping page generation options inside withScalar. The function requires @scalar/api-client-react to be installed in the project, automatically importing its style sheet from @scalar/api-client-react/style.css and loading the client component lazily.
Sources: packages/openapi/src/scalar/client.tsx:10, packages/openapi/src/scalar/index.tsx:5-10
| Option Property | Return / Action | Description |
|---|---|---|
playground.provider | props.children | Bypasses default playground provider wrappers to use Scalar context. |
playground.render | <Client method={method} path={path} spec={ctx.schema.bundled} /> | Renders the lazy-loaded ScalarPlayground client component for each endpoint. |
Sources: packages/openapi/src/scalar/index.tsx:15-23
Note
The withScalar function overrides playground.provider to pass children directly and assigns playground.render to mount ScalarPlayground with the bundled OpenAPI specification schema. Sources: packages/openapi/src/scalar/index.tsx
Playground Rendering and Execution
The ScalarPlayground component renders an interactive card containing an HTTP method label, the request path, and a submit trigger button that opens the Scalar client modal with the specified path and method.
Sources: packages/openapi/src/scalar/client.tsx:12-53
const client = useApiClient({
configuration: {
content: spec as never,
},
});Sources: packages/openapi/src/scalar/client.tsx:24-28
The component utilizes useTheme from fumadocs-ui/provider/base to resolve the current application theme and synchronize light or dark mode styling for the Scalar client wrapper div once mounted on the client.
Sources: packages/openapi/src/scalar/client.tsx
Warning
ScalarPlayground is marked with 'use client' and relies on useState and useEffect hooks to track mounting status before applying resolved theme mode class names to avoid server-side hydration mismatches. Sources: packages/openapi/src/scalar/client.tsx
Related
On This Page
OverviewPage Creation and Runtime ContextInitialization and Rendering PipelineServer State and Context ProvidersInteractive Operation CardsStructural Decomposition and Operation RenderingAuthentication Schemes and Security ContextsOperation Context and State ManagementInteractive Schema Viewer PopoversCall-Chain Execution WalkthroughState Management and Popover ConfigurationRequest Parameter EncodingExecution Walkthrough and SequenceParameter Serialization StrategiesRequest and Response TabsRequest Tab Rendering and Data DisplayResponse Tabs and Schema SamplingClient Code Blocks and Usage GenerationAlternative Scalar Client IntegrationThe withScalar Wrapper and Client ComponentPlayground Rendering and ExecutionRelated