---
title: "API Docs Primitives"
description: "API Docs Primitives (@fumadocs/api-docs) provides the underlying schema parsing, type formatting, mocking, rendering, and navigation infrastructure shared across Fumadocs API documentation packages..."
last_updated: "2026-10-12T07:53:59.986642+00:00"
canonical_url: "https://www.doc0.dev/docs/e8872a72-1909-479c-b585-dc74c6e26726/technical/api-documentation/api-docs-primitives"
---

<details>
<summary>Relevant source files</summary>

The following files were used as context for generating this wiki page:

- [packages/api-docs/src/schema/sample.ts](https://github.com/blade47/fumadocs/blob/a9f988897e7dc4ef3d62b2865d80da2f5607701e/packages/api-docs/src/schema/sample.ts)
- [packages/api-docs/src/components/schema/client.tsx](https://github.com/blade47/fumadocs/blob/a9f988897e7dc4ef3d62b2865d80da2f5607701e/packages/api-docs/src/components/schema/client.tsx)
- [packages/openapi/src/ui/base.tsx](https://github.com/blade47/fumadocs/blob/a9f988897e7dc4ef3d62b2865d80da2f5607701e/packages/openapi/src/ui/base.tsx)
- [packages/openapi/src/ui/operation/index.tsx](https://github.com/blade47/fumadocs/blob/a9f988897e7dc4ef3d62b2865d80da2f5607701e/packages/openapi/src/ui/operation/index.tsx)
- [packages/api-docs/src/components/schema/index.tsx](https://github.com/blade47/fumadocs/blob/a9f988897e7dc4ef3d62b2865d80da2f5607701e/packages/api-docs/src/components/schema/index.tsx)
- [packages/asyncapi/src/ui/index.tsx](https://github.com/blade47/fumadocs/blob/a9f988897e7dc4ef3d62b2865d80da2f5607701e/packages/asyncapi/src/ui/index.tsx)
- [packages/api-docs/package.json](https://github.com/blade47/fumadocs/blob/a9f988897e7dc4ef3d62b2865d80da2f5607701e/packages/api-docs/package.json)
- [packages/openapi/src/server/index.tsx](https://github.com/blade47/fumadocs/blob/a9f988897e7dc4ef3d62b2865d80da2f5607701e/packages/openapi/src/server/index.tsx)
- [packages/cli/src/commands/shared.ts](https://github.com/blade47/fumadocs/blob/a9f988897e7dc4ef3d62b2865d80da2f5607701e/packages/cli/src/commands/shared.ts)
- [packages/api-docs/src/schema/index.ts](https://github.com/blade47/fumadocs/blob/a9f988897e7dc4ef3d62b2865d80da2f5607701e/packages/api-docs/src/schema/index.ts)
- [packages/openapi/src/utils/document/dereference.ts](https://github.com/blade47/fumadocs/blob/a9f988897e7dc4ef3d62b2865d80da2f5607701e/packages/openapi/src/utils/document/dereference.ts)
- [packages/openapi/src/utils/schema.ts](https://github.com/blade47/fumadocs/blob/a9f988897e7dc4ef3d62b2865d80da2f5607701e/packages/openapi/src/utils/schema.ts)
- [packages/asyncapi/src/ui/bindings/shared.tsx](https://github.com/blade47/fumadocs/blob/a9f988897e7dc4ef3d62b2865d80da2f5607701e/packages/asyncapi/src/ui/bindings/shared.tsx)
- [packages/openapi/src/utils/get-example-requests.ts](https://github.com/blade47/fumadocs/blob/a9f988897e7dc4ef3d62b2865d80da2f5607701e/packages/openapi/src/utils/get-example-requests.ts)
- [packages/asyncapi/src/server/index.tsx](https://github.com/blade47/fumadocs/blob/a9f988897e7dc4ef3d62b2865d80da2f5607701e/packages/asyncapi/src/server/index.tsx)
- [packages/openapi/src/ui/index.tsx](https://github.com/blade47/fumadocs/blob/a9f988897e7dc4ef3d62b2865d80da2f5607701e/packages/openapi/src/ui/index.tsx)
- [packages/openapi/src/ui/operation/response-tabs.tsx](https://github.com/blade47/fumadocs/blob/a9f988897e7dc4ef3d62b2865d80da2f5607701e/packages/openapi/src/ui/operation/response-tabs.tsx)
- [packages/api-docs/src/schema/to-string.ts](https://github.com/blade47/fumadocs/blob/a9f988897e7dc4ef3d62b2865d80da2f5607701e/packages/api-docs/src/schema/to-string.ts)
- [packages/asyncapi/src/utils/document/dereference.ts](https://github.com/blade47/fumadocs/blob/a9f988897e7dc4ef3d62b2865d80da2f5607701e/packages/asyncapi/src/utils/document/dereference.ts)
- [packages/openapi/src/types.ts](https://github.com/blade47/fumadocs/blob/a9f988897e7dc4ef3d62b2865d80da2f5607701e/packages/openapi/src/types.ts)
- [packages/python/src/components/index.tsx](https://github.com/blade47/fumadocs/blob/a9f988897e7dc4ef3d62b2865d80da2f5607701e/packages/python/src/components/index.tsx)
- [packages/openapi/src/scalar/client.tsx](https://github.com/blade47/fumadocs/blob/a9f988897e7dc4ef3d62b2865d80da2f5607701e/packages/openapi/src/scalar/client.tsx)
- [packages/asyncapi/src/utils/schema.ts](https://github.com/blade47/fumadocs/blob/a9f988897e7dc4ef3d62b2865d80da2f5607701e/packages/asyncapi/src/utils/schema.ts)
- [packages/openapi/css/preset.css](https://github.com/blade47/fumadocs/blob/a9f988897e7dc4ef3d62b2865d80da2f5607701e/packages/openapi/css/preset.css)
- [packages/openapi/src/index.ts](https://github.com/blade47/fumadocs/blob/a9f988897e7dc4ef3d62b2865d80da2f5607701e/packages/openapi/src/index.ts)
- [packages/base-ui/registry/index.ts](https://github.com/blade47/fumadocs/blob/a9f988897e7dc4ef3d62b2865d80da2f5607701e/packages/base-ui/registry/index.ts)
- [packages/openapi/package.json](https://github.com/blade47/fumadocs/blob/a9f988897e7dc4ef3d62b2865d80da2f5607701e/packages/openapi/package.json)
- [packages/api-docs/src/codegen.ts](https://github.com/blade47/fumadocs/blob/a9f988897e7dc4ef3d62b2865d80da2f5607701e/packages/api-docs/src/codegen.ts)
- [packages/api-docs/src/schema/bundle.ts](https://github.com/blade47/fumadocs/blob/a9f988897e7dc4ef3d62b2865d80da2f5607701e/packages/api-docs/src/schema/bundle.ts)
- [packages/api-docs/src/auto-anchor/index.ts](https://github.com/blade47/fumadocs/blob/a9f988897e7dc4ef3d62b2865d80da2f5607701e/packages/api-docs/src/auto-anchor/index.ts)
</details>

## Overview

API Docs Primitives (`@fumadocs/api-docs`) provides the underlying schema parsing, type formatting, mocking, rendering, and navigation infrastructure shared across Fumadocs API documentation packages like [OpenAPI Interactive UI](https://www.doc0.dev/docs/e8872a72-1909-479c-b585-dc74c6e26726/technical/api-documentation/openapi-interactive-ui) and [AsyncAPI Document Processing](https://www.doc0.dev/docs/e8872a72-1909-479c-b585-dc74c6e26726/technical/api-documentation/asyncapi-document-processing). By establishing a unified runtime foundation, it solves the fragmentation of handling complex JSON Schema specifications across different protocol adapters and frontend layouts. Key design decisions include synchronous reference resolution, recursive AST generation for hierarchical properties, and decoupled client-server component splitting that maintains interactive deep-linking and state preservation. It interfaces directly with specification document parsers and UI layout renderers to power interactive schema property exploration, code sample integration, and deep-linkable slug generation.

Sources: [packages/api-docs/package.json:1-45](https://github.com/blade47/fumadocs/blob/a9f988897e7dc4ef3d62b2865d80da2f5607701e/packages/api-docs/package.json#L1-L45), [packages/api-docs/src/components/schema/client.tsx:73-114](https://github.com/blade47/fumadocs/blob/a9f988897e7dc4ef3d62b2865d80da2f5607701e/packages/api-docs/src/components/schema/client.tsx#L73-L114), [packages/api-docs/src/components/schema/index.tsx:89-112](https://github.com/blade47/fumadocs/blob/a9f988897e7dc4ef3d62b2865d80da2f5607701e/packages/api-docs/src/components/schema/index.tsx#L89-L112)

## Package architecture and shared contracts

### Package exports and module layout

The `@fumadocs/api-docs` package exposes a modular export structure defined in `package.json`, mapping internal build outputs to explicit subpath imports for schema processing, interactive components, code generation, and utilities.

```json
"exports": {
  "./auto-anchor": "./dist/auto-anchor/index.js",
  "./auto-anchor/client": "./dist/auto-anchor/client.js",
  "./codegen": "./dist/codegen.js",
  "./components/accordion": "./dist/components/accordion.js",
  "./components/dialog": "./dist/components/dialog.js",
  "./components/input": "./dist/components/input.js",
  "./components/playground/inputs": "./dist/components/playground/inputs.js",
  "./components/playground/schema": "./dist/components/playground/schema.js",
  "./components/schema": "./dist/components/schema/index.js",
  "./components/schema/client": "./dist/components/schema/client.js",
  "./components/select": "./dist/components/select.js",
  "./components/select-tab": "./dist/components/select-tab.js",
  "./components/spinner": "./dist/components/spinner.js",
  "./i18n": "./dist/i18n.js",
  "./schema": "./dist/schema/index.js",
  "./schema/bundle": "./dist/schema/bundle.js",
  "./schema/dereference": "./dist/schema/dereference.js",
  "./schema/merge": "./dist/schema/merge.js",
  "./schema/ref": "./dist/schema/ref.js",
  "./schema/resolve-ref": "./dist/schema/resolve-ref.js",
  "./schema/sample": "./dist/schema/sample.js",
  "./schema/to-string": "./dist/schema/to-string.js",
  "./utils/id-to-title": "./dist/utils/id-to-title.js",
  "./utils/url": "./dist/utils/url.js",
  "./package.json": "./package.json",
  "./css/*": "./css/*"
},
```
Sources: [packages/api-docs/package.json:18-45](https://github.com/blade47/fumadocs/blob/a9f988897e7dc4ef3d62b2865d80da2f5607701e/packages/api-docs/package.json#L18-L45)

### Core type definitions and runtime contracts

The core schema architecture relies on utility types and type guards that handle JSON Schema definitions (following draft 2020-12) and reference stripping. `NoReference<T>` recursively eliminates `$ref` reference objects from arbitrary types, while `ParsedSchema` unions standard JSON Schema definitions with extension properties such as `x-playground-lazy` and discriminator mappings.

```typescript
import type { JSONSchema } from 'json-schema-typed/draft-2020-12';

type ReferenceObject = { $ref: string };

export type NoReference<T> = T extends (infer I)[]
  ? NoReference<I>[]
  : T extends ReferenceObject
    ? Exclude<T, ReferenceObject>
    : T extends object
      ? {
          [K in keyof T]: NoReference<T[K]>;
        }
      : T;

export type NoReferenceSwallow<T> = T extends ReferenceObject ? Exclude<T, ReferenceObject> : T;

export type ParsedSchema =
  | (JSONSchema & {
      'x-playground-lazy'?: boolean;
      discriminator?: {
        propertyName?: string;
        mapping?: Record<string, string>;
      };
    })
  | boolean;
```
Sources: [packages/api-docs/src/schema/index.ts:1-25](https://github.com/blade47/fumadocs/blob/a9f988897e7dc4ef3d62b2865d80da2f5607701e/packages/api-docs/src/schema/index.ts#L1-L25)

The `SchemaResolver` contract defines the shape for functions that take a `ParsedSchema` and return a shallowly dereferenced schema alongside an optional `$ref` string.

```typescript
export type SchemaResolver = (schema: ParsedSchema) => {
  /** shallowly dereferenced schema */
  dereferenced: ParsedSchema;
  /** the ref value (if dereferenced) */
  $ref?: string;
};
```
Sources: [packages/api-docs/src/schema/index.ts:27-32](https://github.com/blade47/fumadocs/blob/a9f988897e7dc4ef3d62b2865d80da2f5607701e/packages/api-docs/src/schema/index.ts#L27-L32)

> [!NOTE]
> `SchemaResolver` performs a shallow dereference step rather than a deep recursive unrolling, preserving reference boundaries until explicit traversal or sampling occurs.

### Bundling primitives and code generation registries

Schema bundling relies on re-exporting the `bundle` function directly from `@apidevtools/json-schema-ref-parser` to combine external JSON reference files into a unified document structure.

```typescript
export { bundle } from '@apidevtools/json-schema-ref-parser';
```
Sources: [packages/api-docs/src/schema/bundle.ts:1-1](https://github.com/blade47/fumadocs/blob/a9f988897e7dc4ef3d62b2865d80da2f5607701e/packages/api-docs/src/schema/bundle.ts#L1-L1)

To support [Code Sample Generators](https://www.doc0.dev/docs/e8872a72-1909-479c-b585-dc74c6e26726/technical/api-documentation/code-sample-generators), the package provides `CodeUsageGeneratorRegistry` and `createCodeUsageGeneratorRegistry`, managing language-specific code generators through registration, retrieval, inline definition, and removal operations.

```typescript
export interface CodeUsageGeneratorRegistry<Data = unknown, Context = unknown> {
  add: (id: string, generator: CodeUsageGenerator<Data, Context>) => void;
  get: (id: string) => CodeUsageGenerator<Data, Context> | undefined;
  addInline: (generator: InlineCodeUsageGenerator<Data, Context>) => void;
  /**
   * @returns if the generator is removed
   */
  remove: (id: string) => boolean;
  map: () => Map<string, CodeUsageGenerator<Data, Context>>;
}

/**
 * Generate code example for given programming language
 */
export interface CodeUsageGenerator<Data = unknown, Context = unknown> {
  generate: (data: Data, context: Context) => string;
  lang: string;
  label?: string;
}

/**
 * Generate code example for given programming language
 */
export interface InlineCodeUsageGenerator<Data = unknown, Context = unknown> {
  id?: string;
  lang: string;
  label?: string;
  /**
   * either:
   * - code
   * - a function imported from a file with "use client" directive
   * - false (disabled)
   */
  source?: string | CodeUsageGeneratorFn<Data, Context> | false;
}

export function createCodeUsageGeneratorRegistry<Data, Context>(
  inherit?: CodeUsageGeneratorRegistry<Data, Context>,
): CodeUsageGeneratorRegistry<Data, Context> {
```
Sources: [packages/api-docs/src/codegen.ts:1-39](https://github.com/blade47/fumadocs/blob/a9f988897e7dc4ef3d62b2865d80da2f5607701e/packages/api-docs/src/codegen.ts#L1-L39)

> [!IMPORTANT]
> When adding inline code usage generators via `addInline`, if the source property evaluates to falsy or `false`, the registry automatically deletes the generator entry matching the language or identifier.

## Synchronous schema dereferencing

### Dereferencing mechanics in OpenAPI documents

OpenAPI document processing relies on synchronous schema dereferencing via `dereferenceBundledDocument`, which converts a bundled specification document into a fully resolved structure devoid of local or external reference pointers. This function initializes a `Map<object, string>` instance to track original reference strings (`$ref`) mapped to their corresponding schema objects.

```typescript
export function dereferenceBundledDocument(bundled: Document): DereferencedDocument {
  const dereferenceMap = new Map<object, string>();

  return {
    bundled,
    dereferenced: dereferenceSync(bundled as JSONSchema, {
      setOriginalRef(schema, ref) {
        dereferenceMap.set(schema as object, ref);
      },
    }) as NoReference<Document>,
    getRawRef(obj: object) {
      return dereferenceMap.get(obj);
    },
  };
}
```
Sources: [packages/openapi/src/utils/document/dereference.ts:20-34](https://github.com/blade47/fumadocs/blob/a9f988897e7dc4ef3d62b2865d80da2f5607701e/packages/openapi/src/utils/document/dereference.ts#L20-L34)

The underlying dereference implementation uses `dereferenceSync` from `@apidevtools/json-schema-ref-parser` (re-exported via `@fumadocs/api-docs/schema/dereference`) cast to `JSONSchema` from `json-schema-typed/draft-2020-12`. During this traversal, the `setOriginalRef` callback intercepts each resolved schema node and populates the tracking map with `dereferenceMap.set(schema as object, ref)`, enabling downstream lookups of raw reference identifiers via `getRawRef(obj)`.

```typescript
export interface DereferencedDocument {
  /**
   * dereferenced document
   */
  dereferenced: NoReference<Document>;

  /**
   * Get raw $ref from dereferenced object
   */
  getRawRef: (obj: object) => string | undefined;

  bundled: Document;
}
```
Sources: [packages/openapi/src/utils/document/dereference.ts:6-18](https://github.com/blade47/fumadocs/blob/a9f988897e7dc4ef3d62b2865d80da2f5607701e/packages/openapi/src/utils/document/dereference.ts#L6-L18)

> [!NOTE]
> `DereferencedDocument` exposes three properties: `bundled` for the original specification, `dereferenced` typed as `NoReference<Document>` for the fully unrolled AST, and `getRawRef` to retrieve original reference strings from any dereferenced object instance.

### AsyncAPI document dereferencing contracts

AsyncAPI document processing adopts an identical architectural pattern for synchronous reference resolution through `packages/asyncapi/src/utils/document/dereference.ts`. It wraps `dereferenceSync` to process `AsyncAPIObject` definitions, maintaining a dedicated `dereferenceMap` to track source references across asynchronous message schemas and protocol components.

```typescript
export function dereferenceBundledDocument(bundled: AsyncAPIObject): DereferencedDocument {
  const dereferenceMap = new Map<object, string>();

  return {
    bundled,
    dereferenced: dereferenceSync(bundled as JSONSchema, {
      setOriginalRef(schema, ref) {
        dereferenceMap.set(schema as object, ref);
      },
    }) as NoReference<AsyncAPIObject>,
    getRawRef(obj: object) {
      return dereferenceMap.get(obj);
    },
  };
}
```
Sources: [packages/asyncapi/src/utils/document/dereference.ts:20-34](https://github.com/blade47/fumadocs/blob/a9f988897e7dc4ef3d62b2865d80da2f5607701e/packages/asyncapi/src/utils/document/dereference.ts#L20-L34)

The resulting `DereferencedDocument` interface for AsyncAPI returns `NoReference<AsyncAPIObject>` alongside the raw reference lookup utility and the original bundled specification object.

```typescript
export interface DereferencedDocument {
  /**
   * dereferenced document
   */
  dereferenced: NoReference<AsyncAPIObject>;

  /**
   * Get raw $ref from dereferenced object
   */
  getRawRef: (obj: object) => string | undefined;

  bundled: AsyncAPIObject;
}
```
Sources: [packages/asyncapi/src/utils/document/dereference.ts:6-18](https://github.com/blade47/fumadocs/blob/a9f988897e7dc4ef3d62b2865d80da2f5607701e/packages/asyncapi/src/utils/document/dereference.ts#L6-L18)

> [!WARNING]
> Both OpenAPI and AsyncAPI dereference pipelines cast their respective document types to `JSONSchema` during `dereferenceSync` invocation, requiring structural compatibility with JSON Schema Draft 2020-12 definitions.

### Reference bundling primitives

Underpinning both protocol dereferencing utilities, reference bundling relies on the core `bundle` export from `@apidevtools/json-schema-ref-parser` to aggregate distributed references into a cohesive self-contained specification file prior to synchronous traversal.

```typescript
export { bundle } from '@apidevtools/json-schema-ref-parser';
```
Sources: [packages/api-docs/src/schema/bundle.ts:1-1](https://github.com/blade47/fumadocs/blob/a9f988897e7dc4ef3d62b2865d80da2f5607701e/packages/api-docs/src/schema/bundle.ts#L1-L1)

> [!IMPORTANT]
> Bundling must precede synchronous dereferencing whenever specification files reference external file paths or network URIs, as `dereferenceSync` operates entirely in-memory on the consolidated document structure.

## Mock data sampling and traversal

### Sample options and type inference

Mock data generation converts JSON schema definitions into concrete payloads through `sample` and internal traversal routines. The configuration accepts specific flags defined in `JsonSchemaSampleOptions`, governing object traversal depths, schema property omissions, and pattern generation.

| Option | Type | Default | Purpose |
| :--- | :--- | :--- | :--- |
| `skipNonRequired` | boolean | — | Omits schema properties not marked as required |
| `skipReadOnly` | boolean | `false` | Omits properties designated as read-only |
| `skipWriteOnly` | boolean | — | Omits properties designated as write-only |
| `quiet` | boolean | — | Suppresses warnings during sampling |
| `enablePatterns` | boolean | — | Enables generation from regular expressions |
| `maxSampleDepth` | number | `15` | Limits recursion depth during traversal |

Sources: [packages/api-docs/src/schema/sample.ts:21-37](https://github.com/blade47/fumadocs/blob/a9f988897e7dc4ef3d62b2865d80da2f5607701e/packages/api-docs/src/schema/sample.ts#L21-L37)

When schemas omit explicit type declarations, `inferType` inspects validation keywords to deduce the expected data type. It maps attributes like `multipleOf`, `maximum`, `minimum`, `maxLength`, `pattern`, `items`, `properties`, and `required` to primitive types such as `'number'`, `'string'`, `'array'`, or `'object'`.

```typescript
function inferType(schema: SchemaDoc): string | null {
  if (schema.type !== undefined) {
    const t = schema.type;
    return Array.isArray(t) ? (t.length === 0 ? null : String(t[0])) : String(t);
  }

  const keywordTypes: Record<string, string> = {
    multipleOf: 'number',
    maximum: 'number',
    exclusiveMaximum: 'number',
    minimum: 'number',
    exclusiveMinimum: 'number',
    maxLength: 'string',
    minLength: 'string',
    pattern: 'string',
    items: 'array',
    maxItems: 'array',
    minItems: 'array',
    uniqueItems: 'array',
    additionalItems: 'array',
    maxProperties: 'object',
    minProperties: 'object',
    required: 'object',
    additionalProperties: 'object',
    properties: 'object',
    patternProperties: 'object',
    dependencies: 'object',
  };

  for (const [kw, ty] of Object.entries(keywordTypes)) {
    if (schema[kw] !== undefined) return ty;
  }
  return null;
}
```
Sources: [packages/api-docs/src/schema/sample.ts:51-84](https://github.com/blade47/fumadocs/blob/a9f988897e7dc4ef3d62b2865d80da2f5607701e/packages/api-docs/src/schema/sample.ts#L51-L84)

> [!NOTE]
> If a schema defines an array of types, `inferType` evaluates the first array element after checking for empty type arrays, returning `null` if no type determination is possible.

### Request example generation workflow

Request example generation uses `getExampleRequests` to construct client payloads from operation definitions, parameter lists, and request bodies. It extracts media types using `getPreferredType` and populates path, query, header, and cookie parameters by resolving examples or invoking `sample` on parameter schemas.

```typescript
export function getExampleRequests({
  path,
  method,
  ctx,
  operation,
  pathItem,
}: {
  path: string;
  pathItem: NoReference<PathItemObject>;
  method: HttpMethods;
  operation: NoReference<OperationObject>;
  ctx: RenderContext;
}): ExampleRequestItem[] {
  const requestBody = operation.requestBody;
  const media = requestBody?.content ? getPreferredType(requestBody.content) : null;
  const bodyOfType = media ? requestBody!.content![media] : null;
  const parameters = [...(operation.parameters ?? []), ...(pathItem.parameters ?? [])];

  if (bodyOfType?.examples) {
    const result: ExampleRequestItem[] = [];

    for (const [key, value] of Object.entries(bodyOfType.examples)) {
      const data = getRequestData({
        path,
        body: operation.requestBody,
        parameters,
        sampleKey: key,
        method,
      });

      result.push({
        id: key,
        name: value.summary || key,
        description: value.description,
        data,
        encoded: encodeRequestData(data, ctx.mediaAdapters, parameters),
      });
    }

    if (result.length > 0) return result;
  }

  const data = getRequestData({ path, body: operation.requestBody, method, parameters });
  return [
    {
      id: '_default',
      name: 'Default',
      description:
        typeof bodyOfType?.schema === 'object' ? bodyOfType.schema.description : undefined,
      data,
      encoded: encodeRequestData(data, ctx.mediaAdapters, parameters),
    },
  ];
}
```
Sources: [packages/openapi/src/utils/get-example-requests.ts:23-76](https://github.com/blade47/fumadocs/blob/a9f988897e7dc4ef3d62b2865d80da2f5607701e/packages/openapi/src/utils/get-example-requests.ts#L23-L76)

When processing request bodies, `getRequestData` checks for explicit examples or individual example values before falling back to programmatic sampling via `sample`. HTTP methods dictate the read-only and write-only sampling flags passed to the sampler.

```typescript
function getRequestData({
  method,
  path,
  parameters,
  sampleKey,
  body,
}: {
  path: string;
  sampleKey?: string;
  method: HttpMethods;
  parameters: NoReference<ParameterObject>[];
  body?: NoReference<RequestBodyObject>;
}): RawRequestData {
  const result: RawRequestData = {
    path: {},
    cookie: {},
    header: {},
    query: {},
    method: method,
  };

  for (const param of parameters) {
    let value = pickExample(param as never);

    if (value === undefined && param.required) {
      if (param.schema) {
        value = sample(param.schema as object);
      } else if (param.content) {
        const type = getPreferredType(param.content);
        const content = type ? param.content[type] : undefined;
        if (!content || !content.schema)
          throw new Error(
            `Cannot find "${param.name}" parameter info for media type "${type}" in ${path} ${method}`,
          );

        value = sample(content.schema as object);
      }
    }

    switch (param.in) {
      case 'cookie':
        result.cookie[param.name!] = value;
        break;
      case 'header':
        result.header[param.name!] = value;
        break;
      case 'query':
        result.query[param.name!] = value;
        break;
      default:
        result.path[param.name!] = value;
    }
  }

  if (body?.content) {
    const type = getPreferredType(body.content);
    if (!type) throw new Error(`Cannot find body schema for ${path} ${method}: missing media type`);
    result.bodyMediaType = type as RawRequestData['bodyMediaType'];
    const bodyOfType = body.content[type];

    if (bodyOfType.examples && sampleKey) {
      result.body = bodyOfType.examples[sampleKey].value;
    } else if (bodyOfType.example) {
      result.body = bodyOfType.example;
    } else {
      result.body = sample((bodyOfType?.schema ?? {}) as object, {
        skipReadOnly: method !== 'get',
        skipWriteOnly: method === 'get',
        skipNonRequired: true,
      });
    }
  }

  return result;
}
```
Sources: [packages/openapi/src/utils/get-example-requests.ts:78-152](https://github.com/blade47/fumadocs/blob/a9f988897e7dc4ef3d62b2865d80da2f5607701e/packages/openapi/src/utils/get-example-requests.ts#L78-L152)

> [!WARNING]
> For HTTP `GET` requests, `getRequestData` explicitly configures `skipReadOnly: false` and `skipWriteOnly: true`, while mutating these flags inversely for methods that transmit write payloads.

## Schema string conversion and formatting

### Format flags and string conversion logic

The `schemaToString` utility transforms parsed schema objects into readable string representations. It accepts an optional `SchemaResolver` function and formatting flags defined in `FormatFlags`.

```typescript
export enum FormatFlags {
  None = 0,
  UseAlias = 1 << 0,
}

export function schemaToString(
  value: ParsedSchema,
  resolver?: SchemaResolver,
  flags: FormatFlags = FormatFlags.None,
): string {
```
Sources: [packages/api-docs/src/schema/to-string.ts:3-12](https://github.com/blade47/fumadocs/blob/a9f988897e7dc4ef3d62b2865d80da2f5607701e/packages/api-docs/src/schema/to-string.ts#L3-L12)

| Constant | Value | Purpose |
| :--- | :--- | :--- |
| `FormatFlags.None` | `0` | Default formatting without special alias resolution |
| `FormatFlags.UseAlias` | `1` | Resolves schema titles or reference identifiers |

Sources: [packages/api-docs/src/schema/to-string.ts:3-6](https://github.com/blade47/fumadocs/blob/a9f988897e7dc4ef3d62b2865d80da2f5607701e/packages/api-docs/src/schema/to-string.ts#L3-L6)

> [!NOTE]
> When `FormatFlags.UseAlias` is active, `schemaToString` checks for a `schema.title` property or parses the trailing segment of a raw `$ref` string before falling back to type-level serialization.

### Type resolution and composition rules

The internal `run` function handles boolean schemas, returning `'any'` for `true` and `'never'` for `false`. Array types are formatted recursively into `array<T>` structures where inner item types are evaluated with alias resolution enabled.

```typescript
if (schema.type === 'array')
  return `array<${schema.items ? run(schema.items, flags | FormatFlags.UseAlias) : 'unknown'}>`;

```
Sources: [packages/api-docs/src/schema/to-string.ts:59-61](https://github.com/blade47/fumadocs/blob/a9f988897e7dc4ef3d62b2865d80da2f5607701e/packages/api-docs/src/schema/to-string.ts#L59-L61)

Composed schemas like `oneOf`, `anyOf`, and `allOf` utilize a helper `union` function to aggregate distinct member types. When both `oneOf` and `anyOf` are present on a schema, they combine with an intersection operator.

```typescript
if (schema.oneOf && schema.anyOf) {
  return `(${union(schema.oneOf, ' | ', flags)}) & (${union(schema.anyOf, ' | ', flags)})`;
}
```
Sources: [packages/api-docs/src/schema/to-string.ts:62-64](https://github.com/blade47/fumadocs/blob/a9f988897e7dc4ef3d62b2865d80da2f5607701e/packages/api-docs/src/schema/to-string.ts#L62-L64)

> [!WARNING]
> Binary string formats (`schema.type === 'string'` and `schema.format === 'binary'`) are explicitly converted to the `'file'` type keyword rather than retaining their underlying schema representation.

## Interactive schema renderer components

### Server and client component tree generation

The interactive schema renderer architecture is split between server-side generation utilities and client-side tree navigation components. The server entry point invokes `generateSchemaUI` to build a normalized schema record (`SchemaUIGeneratedData`) containing a root reference pointer `$root` and a dictionary of dereferenced types `refs`. During this phase, property visibility flags like `readOnly` and `writeOnly` dictate which fields are included in object property lists, while `scanRefs` recursively traverses object properties, array items, and union types.

```tsx
export interface SchemaUIGeneratedData {
  $root: string;
  refs: Record<string, SchemaData>;
}

export function Schema({
  client,
  root,
  readOnly,
  writeOnly,
  resolver,
  showExample,
  renderMarkdown,
}: SchemaUIOptions) {
  const translations = useTranslations().translations;
  const generated = useMemo(() => {
    return generateSchemaUI({
      root,
      resolver,
      readOnly,
      writeOnly,
      showExample,
      renderMarkdown,
      translations,
    });
  }, [root, readOnly, writeOnly, resolver, showExample, renderMarkdown, translations]);

  return <SchemaUI {...client} generated={generated} />;
}

export function generateSchemaUI({
  root,
  resolver,
  renderMarkdown,
  readOnly = false,
  writeOnly = false,
  showExample = false,
  translations = {},
}: Omit<SchemaUIOptions, 'client'> & {
  translations?: Partial<Record<string, string>>;
}): SchemaUIGeneratedData {
```
Sources: [packages/api-docs/src/components/schema/index.tsx:84-124](https://github.com/blade47/fumadocs/blob/a9f988897e7dc4ef3d62b2865d80da2f5607701e/packages/api-docs/src/components/schema/index.tsx#L84-L124)

The generated schema record is passed into the `SchemaUI` client component, which manages the active navigation path (`PathItemType[]`) and anchor state. When rendered via AsyncAPI or OpenAPI bindings, the execution flows through specific integration wrappers that resolve schema identifiers and translation contexts.

```mermaid
sequenceDiagram
    participant AsyncAPI as createAsyncAPIPage
    participant OpenAPI as createOpenAPIPageBase
    participant SchemaComp as Schema
    participant GenUI as generateSchemaUI
    AsyncAPI->>SchemaComp: invokes Schema
    OpenAPI->>SchemaComp: invokes Schema
    SchemaComp->>GenUI: generates SchemaUIGeneratedData
```

Sources: [packages/api-docs/src/components/schema/client.tsx:73-114](https://github.com/blade47/fumadocs/blob/a9f988897e7dc4ef3d62b2865d80da2f5607701e/packages/api-docs/src/components/schema/client.tsx#L73-L114), [packages/asyncapi/src/ui/index.tsx:197-203](https://github.com/blade47/fumadocs/blob/a9f988897e7dc4ef3d62b2865d80da2f5607701e/packages/asyncapi/src/ui/index.tsx#L197-L203), [packages/openapi/src/ui/base.tsx:118-125](https://github.com/blade47/fumadocs/blob/a9f988897e7dc4ef3d62b2865d80da2f5607701e/packages/openapi/src/ui/base.tsx#L118-L125)

1. `createAsyncAPIPage` or `createOpenAPIPageBase` initializes the rendering context and delegates to the `Schema` component.
2. `Schema` executes `generateSchemaUI` to produce the normalized reference dictionary and root identifier.
3. `SchemaUI` mounts the client state provider, initializing the path stack with the root schema reference and root anchor ID.

Sources: [packages/api-docs/src/components/schema/client.tsx:83-114](https://github.com/blade47/fumadocs/blob/a9f988897e7dc4ef3d62b2865d80da2f5607701e/packages/api-docs/src/components/schema/client.tsx#L83-L114), [packages/api-docs/src/components/schema/index.tsx:89-112](https://github.com/blade47/fumadocs/blob/a9f988897e7dc4ef3d62b2865d80da2f5607701e/packages/api-docs/src/components/schema/index.tsx#L89-L112), [packages/asyncapi/src/ui/index.tsx:197-203](https://github.com/blade47/fumadocs/bin/../src/ui/index.tsx), [packages/openapi/src/ui/base.tsx:118-125](https://github.com/blade47/fumadocs/blob/a9f988897e7dc4ef3d62b2865d80da2f5607701e/packages/openapi/src/ui/base.tsx#L118-L125)

> [!NOTE]
> `SchemaUI` registers an `useEffect` hook that inspects `window.location.search` for a `path` parameter and `s-highlight` slug, automatically decoding the traversal stack and scrolling the target property container into view upon initial hydration.

### Schema property visibility and filtering

Object schema property generation incorporates strict filtering rules based on visibility flags and property types. When iterating over schema properties, pattern properties, and additional properties, `generateSchemaUI` evaluates each candidate property against visibility criteria.

```tsx
if (schema.type === 'object') {
  const out: SchemaData = {
    type: 'object',
    props: [],
    ...base(schema),
  };
  refs[id] = out;

  const { properties = {}, patternProperties, additionalProperties } = schema;
  const props = Object.entries(properties);
  if (patternProperties) props.push(...Object.entries(patternProperties));

  for (const [key, prop] of props) {
    if (!prop || !isVisible(prop)) continue;
    const $type = getSchemaId(prop);
    scanRefs($type, prop);
    out.props.push({
      $type,
      name: key,
      required: schema.required?.includes(key) ?? false,
    });
  }

  if (additionalProperties && isVisible(additionalProperties)) {
    const $type = getSchemaId(additionalProperties);
    scanRefs($type, additionalProperties);

    out.props.push({
      $type,
      name: '[key: string]',
      required: false,
    });
  }
  return;
```
Sources: [packages/api-docs/src/components/schema/index.tsx:351-384](https://github.com/blade47/fumadocs/blob/a9f988897e7dc4ef3d62b2865d80da2f5607701e/packages/api-docs/src/components/schema/index.tsx#L351-L384)

| Property Type / Field | Inclusion Condition | Default Behavior |
| :--- | :--- | :--- |
| Standard Properties (`properties`) | `prop` is defined and `isVisible(prop)` returns `true` | Evaluated and pushed to object properties |
| Pattern Properties (`patternProperties`) | Appended to property entries when present | Evaluated alongside standard properties |
| Additional Properties (`additionalProperties`) | `additionalProperties` is truthy and visible | Appended as `[key: string]` with `required: false` |

Sources: [packages/api-docs/src/components/schema/index.tsx:359-383](https://github.com/blade47/fumadocs/blob/a9f988897e7dc4ef3d62b2865d80da2f5607701e/packages/api-docs/src/components/schema/index.tsx#L359-L383)

> [!WARNING]
> If a property schema evaluates to invisible under current `readOnly` or `writeOnly` configurations, it is skipped entirely during reference scanning, preventing hidden data fields from populating client popovers and generated type trees.

## Navigation anchoring and UI controls

### Anchor slug generation and prefixing

Fumadocs api-docs uses `github-slugger` to produce deep-link anchor slugs for schema components and properties. The `anchorSegments` utility function accepts an arbitrary list of string segments, applying `slug()` mapping to each non-empty segment unless prefixed with a null character (`\0`), which forces the segment to be preserved verbatim.

```typescript
import { slug } from 'github-slugger';

/** slugify segments, append `\0` to keep segment as-is */
export function anchorSegments(...segs: string[]): string {
  const mapped: string[] = [];
  for (const seg of segs) {
    if (seg.startsWith('\0')) {
      mapped.push(seg.slice(1));
    } else if (seg.length > 0) {
      mapped.push(slug(seg));
    }
  }
  return mapped.join('.');
}
```
Sources: [packages/api-docs/src/auto-anchor/index.ts:1-14](https://github.com/blade47/fumadocs/blob/a9f988897e7dc4ef3d62b2865d80da2f5607701e/packages/api-docs/src/auto-anchor/index.ts#L1-L14)

| Function / Helper | Signature | Description |
| :--- | :--- | :--- |
| `anchorSegments` | `(...segs: string[]) => string` | Slugifies segments, joining them with dots (`.`), while verbatim segments are unmasked via `\0` prefix slicing. |
| `anchorIdStartsWith` | `(id1: string, id2: string) => boolean` | Evaluates hierarchical prefix matches between anchor IDs, returning `true` if `id1` equals `id2` or starts with `id2.`. |

Sources: [packages/api-docs/src/auto-anchor/index.ts:4-21](https://github.com/blade47/fumadocs/blob/a9f988897e7dc4ef3d62b2865d80da2f5607701e/packages/api-docs/src/auto-anchor/index.ts#L4-L21)

> [!NOTE]
> When `SchemaUI` mounts, it registers its root identifier using `useAnchorId([name])`. It tracks `ExcludedFromAutoAnchor` caches to ensure initial URL deep-link parameters (`path` and `s-highlight`) trigger smooth scrolling into view only on the first hydration pass.

### Interactive popover state and scroll management

The interactive schema renderer uses a React context provider (`StateContextType`) to manage the current traversal path (`path` array of `PathItemType` objects), root anchor ID, and popover triggers. When users click nested object types, `renderTypeInfoTrigger` controls a Radix UI `Popover` whose open state depends on whether the path length exceeds 1, matching the target reference and name while the root container is not closed.

```tsx
interface PathItemType {
  name: string;
  $ref: string;
  scrollTop?: number;
  /** property name of highlighted field, only applicable for objects */
  highlighted?: string;
  tabValues?: string[];
  /** popover state, only applicable for root */
  closed?: boolean;
}

interface StateContextType {
  rootId: string;
  /** the first tiem will always be the root item */
  path: PathItemType[];
  setPath: (path: PathItemType[]) => void;
  generated: SchemaUIGeneratedData;
  renderTypeInfoTrigger: (props: {
    pathName: string;
    $ref: string;
    children: ReactNode;
  }) => ReactNode;
}
```
Sources: [packages/api-docs/src/components/schema/client.tsx:34-56](https://github.com/blade47/fumadocs/blob/a9f988897e7dc4ef3d62b2865d80da2f5607701e/packages/api-docs/src/components/schema/client.tsx#L34-L56)

> [!WARNING]
> Popover elements attach a `popoverRef` callback that preserves container scroll positions (`scrollTop`) across nested view switches, enforcing a dynamic minimum height of `200px` (or higher based on client height calculations) and autofocusing inputs marked with `data-object-search-input`.

### Protocol bindings and helper controls

AsyncAPI protocol binding UI modules share a common set of layout primitives, formatting utilities, and summary builders to render message headers, channel targets, and operation properties consistently.

```tsx
export function formatBindingScalar(value: unknown): string {
  if (value === null || value === undefined) return '';
  if (typeof value === 'boolean') return value ? 'Yes' : 'No';
  if (typeof value === 'number') return String(value);
  if (typeof value === 'string') return value;
  if (Array.isArray(value)) {
    return value.map((item) => formatBindingScalar(item)).join(', ');
  }

  return JSON.stringify(value);
}

export function hasBindingFields(
  binding: Record<string, unknown>,
  ignore: string[] = ['bindingVersion'],
): boolean {
  return Object.keys(binding).some(
    (key) => !ignore.includes(key) && !key.startsWith('x-') && binding[key] !== undefined,
  );
}

export function joinBindingSummary(
  ...parts: (string | undefined | null | false)[]
): string | undefined {
  const filtered = parts.filter((part): part is string => Boolean(part));
  return filtered.length > 0 ? filtered.join(' · ') : undefined;
}
```
Sources: [packages/asyncapi/src/ui/bindings/shared.tsx:24-50](https://github.com/blade47/fumadocs/blob/a9f988897e7dc4ef3d62b2865d80da2f5607701e/packages/asyncapi/src/ui/bindings/shared.tsx#L24-L50)

| Utility Function | Parameter Type | Return Type | Behavior |
| :--- | :--- | :--- | :--- |
| `formatBindingScalar` | `unknown` | `string` | Converts booleans to `'Yes'` / `'No'`, serializes arrays recursively, or falls back to `JSON.stringify`. |
| `hasBindingFields` | `Record<string, unknown>, string[]` | `boolean` | Verifies object keys exist while ignoring fields like `'bindingVersion'` and `x-*` extensions. |
| `joinBindingSummary` | `(string \| undefined \| null \| false)[]` | `string \| undefined` | Filters falsy items and joins summary segments using `' · '` separators. |

Sources: [packages/asyncapi/src/ui/bindings/shared.tsx:24-50](https://github.com/blade47/fumadocs/blob/a9f988897e7dc4ef3d62b2865d80da2f5607701e/packages/asyncapi/src/ui/bindings/shared.tsx#L24-L50)

> [!TIP]
> Components such as `BindingGroup`, `BindingFieldRow`, and `BindingScalarValue` automatically adapt boolean state badges (rendering emerald styles for true values and muted styles for false) and wrap scalar codes inside responsive definition lists (`<dl>`).

## Related

- [OpenAPI Document Generation](https://www.doc0.dev/docs/e8872a72-1909-479c-b585-dc74c6e26726/technical/api-documentation/openapi-document-generation)
- [AsyncAPI Document Processing](https://www.doc0.dev/docs/e8872a72-1909-479c-b585-dc74c6e26726/technical/api-documentation/asyncapi-document-processing)


## Sitemap

See the full [sitemap](https://www.doc0.dev/docs/e8872a72-1909-479c-b585-dc74c6e26726/llms.txt) for all pages in this wiki.
