<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.
Keep collection dir outside the project in file paths
When a collection's dir was outside the project (e.g. ../content/docs), the leading ../ was dropped from info.fullPath and absolutePath, so getText('raw') failed with ENOENT.
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.
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.
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.
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.
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.
Download response bodies from the playground
The result panel of the API playground has a Download button for non-empty responses. The file is named from the Content-Disposition header, preferring filename* and without directories, otherwise response, which browsers complete with the extension of the media type.
For the browser to read Content-Disposition, cross-origin APIs have to list it in Access-Control-Expose-Headers, or be requested through the proxy.
Search your docs with Meilisearch, self-hosted or on Meilisearch Cloud.
toDocuments() and sync() from fumadocs-core/search/meilisearch export your pages and replace the documents of an index, old documents stay searchable until the new ones are indexed.
meilisearchClient() from fumadocs-core/search/client/meilisearch searches from the browser with useDocsSearch(), filtered by tag and locale.
Search your docs with Meilisearch, self-hosted or on Meilisearch Cloud.
toDocuments() and sync() from fumadocs-core/search/meilisearch export your pages and replace the documents of an index, old documents stay searchable until the new ones are indexed.
meilisearchClient() from fumadocs-core/search/client/meilisearch searches from the browser with useDocsSearch(), filtered by tag and locale.
feature search writes the path of the pre-rendered static.json into scripts/sync-content.ts, and the build script runs it without arguments.
On TanStack Start, the path follows the Nitro output: .output/public, .vercel/output/static with the vercel preset, or dist/client without Nitro.
Typesense documents are built in the static.json route, without a separate lib/export-search-indexes.ts.
Download response bodies from the playground
The result panel of the API playground has a Download button for non-empty responses. The file is named from the Content-Disposition header, preferring filename* and without directories, otherwise response, which browsers complete with the extension of the media type.
For the browser to read Content-Disposition, cross-origin APIs have to list it in Access-Control-Expose-Headers, or be requested through the proxy.
Search your docs with Meilisearch, self-hosted or on Meilisearch Cloud.
toDocuments() and sync() from fumadocs-core/search/meilisearch export your pages and replace the documents of an index, old documents stay searchable until the new ones are indexed.
meilisearchClient() from fumadocs-core/search/client/meilisearch searches from the browser with useDocsSearch(), filtered by tag and locale.
feature search writes the path of the pre-rendered static.json into scripts/sync-content.ts, and the build script runs it without arguments.
On TanStack Start, the path follows the Nitro output: .output/public, .vercel/output/static with the vercel preset, or dist/client without Nitro.
Typesense documents are built in the static.json route, without a separate lib/export-search-indexes.ts.
@fumadocs/base-ui@16.15.17
fumadocs-core@16.15.17
fumadocs-ui@16.15.17
Apply the Vite base path to the "View as Markdown" link
<ViewOptionsPopover /> linked to markdownUrl without the Vite base, so sites served under a base path got a 404. It now prefixes the URL like <MarkdownCopyButton /> does.
<MarkdownCopyButton /> copied the body of error responses, like a 404 page, and kept it cached until a full reload. Failed responses are rejected, and only successful ones are cached.
getPage() accepts both URI encoded and decoded slugs. React Router and TanStack Router pass decoded params, so pages with spaces or non-ASCII characters in their slugs were not found.
Render request bodies of unsupported media types read-only
A request body with a media type that has no adapter no longer throws. It is shown as usual, left out of code usages, and cannot be sent from the playground.
text/plain with parameters, like text/plain; charset=utf-8, is also handled like text/plain.
isMediaTypeSupported() is removed, use resolveMediaAdapter() instead.
A route handler to use as the single OAuth redirect URI of API playgrounds, instead of registering every page. Pass its URL to createOpenAPIPage({ oauthRedirectUrl }).
app/api/oauth/route.ts
import { openapi } from '@/lib/openapi';export const GET = openapi.createOAuthHandler();
The client credentials flow can send the client credentials in an HTTP Basic Authorization header, like the password flow (#3609).
Relative authorizationUrl and tokenUrl are resolved against the selected server, requestOAuthToken() requires a serverUrl for it (#3610).
The redirect_uri leaves out the fragment and query of the page, a fragment is not allowed in redirect URIs.
Multiple scopes are space-delimited, they were sent as a single scope joined by +.
Query params of authorizationUrl, like audience, are kept.
HTTP Basic client credentials are form-encoded, as RFC 6749 requires.
The implicit and authorization code flows send a random state, and keep the flow in sessionStorage instead of the URL: the client secret is no longer sent to the authorization server, the token URL resolves against the server selected when the flow starts, and only flows started in the same tab are accepted.
Skip response examples without a value
An Example Object can omit value, like when it uses externalValue, which crashed the operation page. These examples are skipped.
Examples with the OpenAPI 3.2 dataValue are rendered too.