The imports option of generateFiles() no longer splits the generated imports into one character per line. Each entry is written as one import { ... } from '...'; line.
<SchemaUIProvider /> from @fumadocs/json-schema/react/client holds the state of a Schema UI: the schemas opened from properties, the selected members of unions, and links to a property. useSchemaUI() reads and navigates it.
The Schema UI installed with Fumadocs CLI (api-docs/schema) is built on it, so the installed code is only UI.
Markdown and search records from the authored source
remarkLLMs() and remarkStructure() of fumadocs-core slice their Markdown from the authored source like Sätteri, instead of stringifying the syntax tree. It is much faster, Markdown is no longer escaped or reformatted, and local images keep their addresses. Re-sync your search indexes to pick up the new records.
Plugins record the Markdown of content they replace with replaceSource() or embedSource() from fumadocs-core/mdx-plugins/stringifier, in place of data._stringify, and add search records with data.structuredData. Nodes they insert otherwise are left out. A namespace limits an edit to the stringifiers of that namespace, like search for search records.
Search records are always Markdown, also in Sätteri. filterElement replaces stringify.filterElement, it chooses the JSX elements kept as HTML tags (File, TypeTable, Callout and Card by default) that search dialogs render as components. Other elements and links are replaced by their content, images are removed, and an element on one line is recorded like a paragraph.
Removed defaultStringifier() (use createStringifier()), StringifyOptions, allowedMdxAttributes, filterMdxAttributes, placeholder() (use mdxAsPlaceholder), and the stringify and mdast-util-to-markdown options of both plugins.
Included content goes through the plugins like the document's own, Sätteri includes .md files as Markdown.
<auto-files> shows its files in a code block, auto-type-table its props in a Markdown table, created by typeTableToMarkdown() from fumadocs-typescript.
remarkShow() and fileGenerator() of fumadocs-docgen record their Markdown, generators receive the file to do the same.
Records of fumadocs-obsidian resolve wikilinks, and leave comments, block IDs, callout markers and embeds out. Comments are removed before parsing, so they no longer split a paragraph.
Pages of fumadocs-python compile from their MDX, so they have Markdown and search records.
fumadocs-mdx, fumadocs-docgen and fumadocs-obsidian require fumadocs-core 16.17.0.
Fix images in descriptions, show the OAuth redirect URI
Images in Markdown of API pages, like the description of an operation, and in descriptions of <AutoTypeTable /> props no longer crash the page with "Element type is invalid".
The OAuth panel of the playground shows the redirect_uri of the authorization code and implicit flows, with a button to copy it: the oauthRedirectUrl route when set, otherwise the current page.
The Authorize row of OAuth schemes in the playground is one button, filling the row.
playground.authProviders handle security schemes: their rows, panels like signing in, and how values are sent. Omitted options come from the built-in providers.
Sign-ins leaving the page use useAuthRedirect().
HTTP schemes other than Basic default to their own prefix, like Token .
The username and password of HTTP Basic auth are remembered.
Markdown and search records from the authored source
remarkLLMs() and remarkStructure() of fumadocs-core slice their Markdown from the authored source like Sätteri, instead of stringifying the syntax tree. It is much faster, Markdown is no longer escaped or reformatted, and local images keep their addresses. Re-sync your search indexes to pick up the new records.
Plugins record the Markdown of content they replace with replaceSource() or embedSource() from fumadocs-core/mdx-plugins/stringifier, in place of data._stringify, and add search records with data.structuredData. Nodes they insert otherwise are left out. A namespace limits an edit to the stringifiers of that namespace, like search for search records.
Search records are always Markdown, also in Sätteri. filterElement replaces stringify.filterElement, it chooses the JSX elements kept as HTML tags (File, TypeTable, Callout and Card by default) that search dialogs render as components. Other elements and links are replaced by their content, images are removed, and an element on one line is recorded like a paragraph.
Removed defaultStringifier() (use createStringifier()), StringifyOptions, allowedMdxAttributes, filterMdxAttributes, placeholder() (use mdxAsPlaceholder), and the stringify and mdast-util-to-markdown options of both plugins.
Included content goes through the plugins like the document's own, Sätteri includes .md files as Markdown.
<auto-files> shows its files in a code block, auto-type-table its props in a Markdown table, created by typeTableToMarkdown() from fumadocs-typescript.
remarkShow() and fileGenerator() of fumadocs-docgen record their Markdown, generators receive the file to do the same.
Records of fumadocs-obsidian resolve wikilinks, and leave comments, block IDs, callout markers and embeds out. Comments are removed before parsing, so they no longer split a paragraph.
Pages of fumadocs-python compile from their MDX, so they have Markdown and search records.
fumadocs-mdx, fumadocs-docgen and fumadocs-obsidian require fumadocs-core 16.17.0.
Structured data records each table row as a Markdown table of its header row and itself, instead of a record per cell. Type tables from auto-type-table record a row for each prop instead of the TypeTable element. Re-sync your search indexes to pick them up.
remarkStructure() defaults types to table instead of tableCell.
tableRowToStructuredData() from fumadocs-core/search creates the structured data of a table row, typeTableToStructuredData() from fumadocs-typescript of a type table.
Search hooks group the rows of a table into a table item, <SearchDialogList /> renders it with Table, <SearchDialogListTable /> by default.
Algolia and Orama Cloud link to the anchor of every record, like the props of type tables.
Orama Cloud searches title, section and content by default, set params.properties to search other fields.
remarkStructure() of Sätteri records the data.structuredData of nodes in place of the nodes.
Markdown and search records from the authored source
remarkLLMs() and remarkStructure() of fumadocs-core slice their Markdown from the authored source like Sätteri, instead of stringifying the syntax tree. It is much faster, Markdown is no longer escaped or reformatted, and local images keep their addresses. Re-sync your search indexes to pick up the new records.
Plugins record the Markdown of content they replace with replaceSource() or embedSource() from fumadocs-core/mdx-plugins/stringifier, in place of data._stringify, and add search records with data.structuredData. Nodes they insert otherwise are left out. A namespace limits an edit to the stringifiers of that namespace, like search for search records.
Search records are always Markdown, also in Sätteri. filterElement replaces stringify.filterElement, it chooses the JSX elements kept as HTML tags (File, TypeTable, Callout and Card by default) that search dialogs render as components. Other elements and links are replaced by their content, images are removed, and an element on one line is recorded like a paragraph.
Removed defaultStringifier() (use createStringifier()), StringifyOptions, allowedMdxAttributes, filterMdxAttributes, placeholder() (use mdxAsPlaceholder), and the stringify and mdast-util-to-markdown options of both plugins.
Included content goes through the plugins like the document's own, Sätteri includes .md files as Markdown.
<auto-files> shows its files in a code block, auto-type-table its props in a Markdown table, created by typeTableToMarkdown() from fumadocs-typescript.
remarkShow() and fileGenerator() of fumadocs-docgen record their Markdown, generators receive the file to do the same.
Records of fumadocs-obsidian resolve wikilinks, and leave comments, block IDs, callout markers and embeds out. Comments are removed before parsing, so they no longer split a paragraph.
Pages of fumadocs-python compile from their MDX, so they have Markdown and search records.
fumadocs-mdx, fumadocs-docgen and fumadocs-obsidian require fumadocs-core 16.17.0.
remarkImage() with onError: 'hide' removes the right images when a paragraph has several, and Sätteri removes them from the Markdown too.
files code blocks of remarkMdxFiles() nest the entries under the last item of a folder (after └──), and trees indented with spaces only.
The remark and rehype plugins no longer slow down quadratically on large documents, like rehypeToc() taking 17 ms instead of 239 ms on a 5 MB page.
fumadocs-epub removes every unresolved image, the second of two adjacent ones was kept.
Sätteri: headings with expression props, like <Badge value={1} /> or local images, compile again, without these elements in their toc entry.
Sätteri: remarkStructure() records every node type listed in types, like fumadocs-core. A blockquote is one search record instead of two. In the Markdown, includes and type tables stay inside their list item or blockquote, heading IDs no longer break setext and closed ATX headings, and nested replacements no longer duplicate the source after them.
Fix images in descriptions, show the OAuth redirect URI
Images in Markdown of API pages, like the description of an operation, and in descriptions of <AutoTypeTable /> props no longer crash the page with "Element type is invalid".
The OAuth panel of the playground shows the redirect_uri of the authorization code and implicit flows, with a button to copy it: the oauthRedirectUrl route when set, otherwise the current page.
The Authorize row of OAuth schemes in the playground is one button, filling the row.
Markdown and search records from the authored source
remarkLLMs() and remarkStructure() of fumadocs-core slice their Markdown from the authored source like Sätteri, instead of stringifying the syntax tree. It is much faster, Markdown is no longer escaped or reformatted, and local images keep their addresses. Re-sync your search indexes to pick up the new records.
Plugins record the Markdown of content they replace with replaceSource() or embedSource() from fumadocs-core/mdx-plugins/stringifier, in place of data._stringify, and add search records with data.structuredData. Nodes they insert otherwise are left out. A namespace limits an edit to the stringifiers of that namespace, like search for search records.
Search records are always Markdown, also in Sätteri. filterElement replaces stringify.filterElement, it chooses the JSX elements kept as HTML tags (File, TypeTable, Callout and Card by default) that search dialogs render as components. Other elements and links are replaced by their content, images are removed, and an element on one line is recorded like a paragraph.
Removed defaultStringifier() (use createStringifier()), StringifyOptions, allowedMdxAttributes, filterMdxAttributes, placeholder() (use mdxAsPlaceholder), and the stringify and mdast-util-to-markdown options of both plugins.
Included content goes through the plugins like the document's own, Sätteri includes .md files as Markdown.
<auto-files> shows its files in a code block, auto-type-table its props in a Markdown table, created by typeTableToMarkdown() from fumadocs-typescript.
remarkShow() and fileGenerator() of fumadocs-docgen record their Markdown, generators receive the file to do the same.
Records of fumadocs-obsidian resolve wikilinks, and leave comments, block IDs, callout markers and embeds out. Comments are removed before parsing, so they no longer split a paragraph.
Pages of fumadocs-python compile from their MDX, so they have Markdown and search records.
fumadocs-mdx, fumadocs-docgen and fumadocs-obsidian require fumadocs-core 16.17.0.
remarkImage() with onError: 'hide' removes the right images when a paragraph has several, and Sätteri removes them from the Markdown too.
files code blocks of remarkMdxFiles() nest the entries under the last item of a folder (after └──), and trees indented with spaces only.
The remark and rehype plugins no longer slow down quadratically on large documents, like rehypeToc() taking 17 ms instead of 239 ms on a 5 MB page.
fumadocs-epub removes every unresolved image, the second of two adjacent ones was kept.
Sätteri: headings with expression props, like <Badge value={1} /> or local images, compile again, without these elements in their toc entry.
Sätteri: remarkStructure() records every node type listed in types, like fumadocs-core. A blockquote is one search record instead of two. In the Markdown, includes and type tables stay inside their list item or blockquote, heading IDs no longer break setext and closed ATX headings, and nested replacements no longer duplicate the source after them.
Markdown and search records from the authored source
remarkLLMs() and remarkStructure() of fumadocs-core slice their Markdown from the authored source like Sätteri, instead of stringifying the syntax tree. It is much faster, Markdown is no longer escaped or reformatted, and local images keep their addresses. Re-sync your search indexes to pick up the new records.
Plugins record the Markdown of content they replace with replaceSource() or embedSource() from fumadocs-core/mdx-plugins/stringifier, in place of data._stringify, and add search records with data.structuredData. Nodes they insert otherwise are left out. A namespace limits an edit to the stringifiers of that namespace, like search for search records.
Search records are always Markdown, also in Sätteri. filterElement replaces stringify.filterElement, it chooses the JSX elements kept as HTML tags (File, TypeTable, Callout and Card by default) that search dialogs render as components. Other elements and links are replaced by their content, images are removed, and an element on one line is recorded like a paragraph.
Removed defaultStringifier() (use createStringifier()), StringifyOptions, allowedMdxAttributes, filterMdxAttributes, placeholder() (use mdxAsPlaceholder), and the stringify and mdast-util-to-markdown options of both plugins.
Included content goes through the plugins like the document's own, Sätteri includes .md files as Markdown.
<auto-files> shows its files in a code block, auto-type-table its props in a Markdown table, created by typeTableToMarkdown() from fumadocs-typescript.
remarkShow() and fileGenerator() of fumadocs-docgen record their Markdown, generators receive the file to do the same.
Records of fumadocs-obsidian resolve wikilinks, and leave comments, block IDs, callout markers and embeds out. Comments are removed before parsing, so they no longer split a paragraph.
Pages of fumadocs-python compile from their MDX, so they have Markdown and search records.
fumadocs-mdx, fumadocs-docgen and fumadocs-obsidian require fumadocs-core 16.17.0.
data is the last successful search, its items have their content parsed into hastContent. error is set when the last search failed.
experimental_useSearch() creates a search hook from a memoized search function.
useDocsSearch() is deprecated.
useFlexsearchStatic() and useOramaCloudLegacySearch() are exported from the paths of their clients.
<SearchDialogList /> shows defaultItems without results.
useSearchList() is removed, useSearch() returns getActive(), setActive() and subscribeActive() for the active item.
<SearchDialogListItem /> renders a div instead of a button, table rows render their cells only.
renderMarkdown of <SearchDialogListItem /> renders content instead of hastContent. Without it, the string content of custom items is shown as text.
The templates of Fumadocs CLI and Create Fumadocs App use the new hooks.
Fix images in descriptions, show the OAuth redirect URI
Images in Markdown of API pages, like the description of an operation, and in descriptions of <AutoTypeTable /> props no longer crash the page with "Element type is invalid".
The OAuth panel of the playground shows the redirect_uri of the authorization code and implicit flows, with a button to copy it: the oauthRedirectUrl route when set, otherwise the current page.
The Authorize row of OAuth schemes in the playground is one button, filling the row.
data is the last successful search, its items have their content parsed into hastContent. error is set when the last search failed.
experimental_useSearch() creates a search hook from a memoized search function.
useDocsSearch() is deprecated.
useFlexsearchStatic() and useOramaCloudLegacySearch() are exported from the paths of their clients.
<SearchDialogList /> shows defaultItems without results.
useSearchList() is removed, useSearch() returns getActive(), setActive() and subscribeActive() for the active item.
<SearchDialogListItem /> renders a div instead of a button, table rows render their cells only.
renderMarkdown of <SearchDialogListItem /> renders content instead of hastContent. Without it, the string content of custom items is shown as text.
The templates of Fumadocs CLI and Create Fumadocs App use the new hooks.
Sync Mixedbread stores with sync()
sync() and toDocuments() from fumadocs-core/search/mixedbread upload a file for each page, whose chunks are its title, headings and paragraphs. Results link to their headings and render tables like other search integrations.
Stores synced with mxbai store sync need a re-sync, the frontmatter of pages is no longer read.
Tag filters match the tags of pages, locale filters results by language, and the limit of requests can only lower topK.
The Mixedbread template of Fumadocs CLI syncs a pre-rendered static.json with sync(), debounces searches by 300 ms, and reads the API key from MXBAI_API_KEY.
Fix images in descriptions, show the OAuth redirect URI
Images in Markdown of API pages, like the description of an operation, and in descriptions of <AutoTypeTable /> props no longer crash the page with "Element type is invalid".
The OAuth panel of the playground shows the redirect_uri of the authorization code and implicit flows, with a button to copy it: the oauthRedirectUrl route when set, otherwise the current page.
The Authorize row of OAuth schemes in the playground is one button, filling the row.
The aiChat panel turns visible as soon as it opens, so its input can take focus right away. It still hides only after the closing transition.
Collapse the sidebar with Ctrl + B
On desktop, press Ctrl + B (⌘ + B on macOS) to collapse or expand the sidebar, in layouts that support collapsing it. Like the theme hotkey, it is ignored while typing in an editable element, or when a dialog is opened.
Fix Spacious layout on Firefox mobile
When scrolling in Firefox, the top edge of the page panel moved away from the navbar and table of contents bar, ending up in the middle of the page or off screen. It now stays below the bars, without relying on CSS anchor positioning.
Ignore unknown values in tab groups
Tabs with a groupId, including code block tabs with tab-group, applied persisted or shared values even when none of their tabs matched, for example after renaming a tab, or when tabs in the same group have different items. They then showed no content until a tab was clicked. Such values are now ignored.
New package: @fumadocs/ai-chat
The Ask AI chat for AI SDK: pass the result of useChat() to AIChatProvider, then render AIChatPanel in the aiChat option of docs layouts. Questions rest at the top while answers stream below, and Markdown is rendered block by block, so unclosed syntax never shows.
The ai/* components of the CLI install its source along with the integration, and the generated layout uses AIChat, AIChatPanel, AIChatTrigger and useAIChat.
New package: @fumadocs/ai-chat
The Ask AI chat for AI SDK: pass the result of useChat() to AIChatProvider, then render AIChatPanel in the aiChat option of docs layouts. Questions rest at the top while answers stream below, and Markdown is rendered block by block, so unclosed syntax never shows.
The ai/* components of the CLI install its source along with the integration, and the generated layout uses AIChat, AIChatPanel, AIChatTrigger and useAIChat.
Schema UI in cards
Nested schemas open in a card instead of a popover, navigated with breadcrumbs on top of it. Request bodies and responses render in the card directly, a property opens its card below itself.
Panels slide in from the side they are navigated to, the page scrolls back to the breadcrumbs after navigating.
Breadcrumbs that don't fit collapse into a menu, keeping the first crumb and the ones closest to the current schema.
The union selector moves next to the breadcrumbs, the filter into the top of the card. In narrow cards, the filter takes a row of its own.
Rows are more compact, enum values and constraints are shown as tags.
The Schema UI client props accept selector, shown above the breadcrumbs, and actions, shown next to the filter.
On API pages:
The media type of request bodies and responses is selected in the top of their cards.
Authorization fields start with where they are sent, like header or query. The security requirement is selected in the top of their card.
Parameters and authorization are grouped in cards.
Responses are tabs of their status codes instead of accordions, a link to a response selects its tab and scrolls to it.
Callbacks open in a dialog instead of expanding in the page, with their full URL on top.
Runtime expressions in routes, like {$request.body#/url}, are highlighted as a whole.
TypeScript definitions are copied from a button in the top of the body and response cards.
Schema UI in cards
Nested schemas open in a card instead of a popover, navigated with breadcrumbs on top of it. Request bodies and responses render in the card directly, a property opens its card below itself.
Panels slide in from the side they are navigated to, the page scrolls back to the breadcrumbs after navigating.
Breadcrumbs that don't fit collapse into a menu, keeping the first crumb and the ones closest to the current schema.
The union selector moves next to the breadcrumbs, the filter into the top of the card. In narrow cards, the filter takes a row of its own.
Rows are more compact, enum values and constraints are shown as tags.
The Schema UI client props accept selector, shown above the breadcrumbs, and actions, shown next to the filter.
On API pages:
The media type of request bodies and responses is selected in the top of their cards.
Authorization fields start with where they are sent, like header or query. The security requirement is selected in the top of their card.
Parameters and authorization are grouped in cards.
Responses are tabs of their status codes instead of accordions, a link to a response selects its tab and scrolls to it.
Callbacks open in a dialog instead of expanding in the page, with their full URL on top.
Runtime expressions in routes, like {$request.body#/url}, are highlighted as a whole.
TypeScript definitions are copied from a button in the top of the body and response cards.
@fumadocs/base-ui@16.16.1
fumadocs-core@16.16.1
fumadocs-ui@16.16.1
Improve Spacious layout on mobile
The page sits in an inset panel on mobile too, while the navbar and table of contents bar stay on the outer surface.
The sidebar drawer floats as an inset panel, like the AI chat.
The navbar and table of contents bar stay below the <Banner />.
Like the Docs layout, --fd-docs-row-3 is the bottom of these bars, so API pages scroll their content below them.
Schema UI in cards
Nested schemas open in a card instead of a popover, navigated with breadcrumbs on top of it. Request bodies and responses render in the card directly, a property opens its card below itself.
Panels slide in from the side they are navigated to, the page scrolls back to the breadcrumbs after navigating.
Breadcrumbs that don't fit collapse into a menu, keeping the first crumb and the ones closest to the current schema.
The union selector moves next to the breadcrumbs, the filter into the top of the card. In narrow cards, the filter takes a row of its own.
Rows are more compact, enum values and constraints are shown as tags.
The Schema UI client props accept selector, shown above the breadcrumbs, and actions, shown next to the filter.
On API pages:
The media type of request bodies and responses is selected in the top of their cards.
Authorization fields start with where they are sent, like header or query. The security requirement is selected in the top of their card.
Parameters and authorization are grouped in cards.
Responses are tabs of their status codes instead of accordions, a link to a response selects its tab and scrolls to it.
Callbacks open in a dialog instead of expanding in the page, with their full URL on top.
Runtime expressions in routes, like {$request.body#/url}, are highlighted as a whole.
TypeScript definitions are copied from a button in the top of the body and response cards.
Schema UI in cards
Nested schemas open in a card instead of a popover, navigated with breadcrumbs on top of it. Request bodies and responses render in the card directly, a property opens its card below itself.
Panels slide in from the side they are navigated to, the page scrolls back to the breadcrumbs after navigating.
Breadcrumbs that don't fit collapse into a menu, keeping the first crumb and the ones closest to the current schema.
The union selector moves next to the breadcrumbs, the filter into the top of the card. In narrow cards, the filter takes a row of its own.
Rows are more compact, enum values and constraints are shown as tags.
The Schema UI client props accept selector, shown above the breadcrumbs, and actions, shown next to the filter.
On API pages:
The media type of request bodies and responses is selected in the top of their cards.
Authorization fields start with where they are sent, like header or query. The security requirement is selected in the top of their card.
Parameters and authorization are grouped in cards.
Responses are tabs of their status codes instead of accordions, a link to a response selects its tab and scrolls to it.
Callbacks open in a dialog instead of expanding in the page, with their full URL on top.
Runtime expressions in routes, like {$request.body#/url}, are highlighted as a whole.
TypeScript definitions are copied from a button in the top of the body and response cards.
@fumadocs/base-ui@16.16.0
fumadocs-core@16.16.0
fumadocs-ui@16.16.0
Place AI chat in layouts
Docs, Notebook, and Glass layouts accept an aiChat option, pass your chat as aiChat.panel and the layout places it beside the page on wide viewports, and floats it over the page on smaller ones.
Your chat component no longer needs layout-specific positioning, like targeting #nd-docs-layout or overriding --fd-right-width.
The ai feature of Fumadocs CLI now renders your docs layout from a client component at ai/layout.tsx in your components directory, which passes the installed chat to aiChat. It supports Docs, Notebook, Glass, and Spacious layouts, and only adds a floating trigger to the layouts without their own.
Export Glass layout as DocsLayout
Like other docs layouts, fumadocs-ui/layouts/glass exports DocsLayout, DocsLayoutProps, and DocsSlots. The GlassLayout names remain as aliases.
Introduce Spacious Layout
A less compact version of Docs Layout, the page sits in an inset panel beside the sidebar, with page-level actions at the top of the panel.
Use it from fumadocs-ui/layouts/spacious and fumadocs-ui/layouts/spacious/page, and import the styles from fumadocs-ui/css/generated/spacious.css.
Pass aiChat.panel to render your AI chat in the layout, docked beside the page on wide screens and floating over it on smaller ones.
Customize it with npx @fumadocs/cli customize, only available for Base UI.
The Chinese presets of @fumadocs/language include translations for its new strings.
Redesigned API playground
The playground moves into a dialog, opened from Try in Playground on the endpoint bar of API pages.
The URL bar selects the server, edits its variables, and fills path parameters inline.
Auth, parameters and the request body are edited in one panel, one row per field with its type in the input. Descriptions and constraints like ranges and defaults open from the info button next to the field name.
Nested objects and arrays open in their own panel, navigated with breadcrumbs. The current breadcrumb switches between the nested fields of its parent, and the body can be edited as JSON.
OAuth 2.0 authorization opens in the same panel instead of a nested dialog, with the flow, client credentials and scopes to request. When a flow redirects to authorize, the playground reopens with the token once it returns to the page.
The response shows beside the code usages with its status, duration, size and headers. Until a request is sent, the documented response examples are shown instead.
Send requests with Cmd/Ctrl + Enter.
components.CollapsiblePanel of the playground options is removed, with the DefaultCollapsiblePanel and CollapsiblePanelProps exports of fumadocs-openapi/ui/playground/client.
Extra props of <PlaygroundClient /> like className now go to its endpoint bar instead of a form, so form props like onSubmit are no longer accepted.
Headless API playground
usePlayground() from fumadocs-openapi/playground holds the state of an API playground: its form, auth and the request it sends. The playground installed with Fumadocs CLI is built on it, so the installed code is only UI.
Responses of createBrowserFetcher() carry duration, mediaType, fileName and text().
Parameters of useOperation() are typed as OperationParameter, with the name and in required by OpenAPI. Parameters without a name are skipped.
writeOnly and readOnly of <PlaygroundClient /> are deprecated, they default to the values for requests.
FormValues of fumadocs-openapi/ui/playground/client is deprecated.