Render Obsidian vaults directly through static or dynamic Fumadocs sources, with lazy in-memory compilation and local content hot reload. Remove the old generated-file and remark-plugin integrations.
Resolve URL-encoded relative file links against their decoded source paths.
Render Obsidian vaults directly through static or dynamic Fumadocs sources, with lazy in-memory compilation and local content hot reload. Remove the old generated-file and remark-plugin integrations.
Resolve URL-encoded relative file links against their decoded source paths.
Render Obsidian vaults directly through static or dynamic Fumadocs sources, with lazy in-memory compilation and local content hot reload. Remove the old generated-file and remark-plugin integrations.
Resolve URL-encoded relative file links against their decoded source paths.
Fix Base UI's use-sync-external-store breaking Vite dev servers
The Vite config is now derived from your project's own dependencies at startup, instead of being generated ahead of time against ours.
Builds keep the alias that replaces the shim with React, which is now applied only there. Pre-bundling is a dev server feature, and on builds a bundler that leaves the shim's require('react') in place gives it a second React instance whose hook dispatcher is null, breaking every component with a Base UI store.
@fumadocs/base-ui@16.14.2
fumadocs-core@16.14.2
fumadocs-ui@16.14.2
Fix crash in DynamicCodeBlock when options is undefined
DynamicCodeBlock read options.components unconditionally while building its Shiki config, even though options is optional on the public wrapper component. Passing an explicit options={undefined} (e.g. a value forwarded from another optional prop) crashed with Cannot read properties of undefined (reading 'components') instead of falling back to the default pre renderer.
options is now read with optional chaining, so an undefined options behaves the same as omitting the prop entirely.
@fumadocs/base-ui@16.14.1
fumadocs-core@16.14.1
fumadocs-ui@16.14.1
Fix unusable tokenizer search option
The search engine rejects a language alongside a custom tokenizer, since the tokenizer carries its own. Both search entry points supplied one unconditionally — createDB/createDBSimple through a destructuring default that language: undefined could not suppress, and createI18nSearchAPI by hardcoding multilingual after the spread — so passing tokenizer always threw NO_LANGUAGE_WITH_CUSTOM_TOKENIZER at index-build time. On i18n sources there was no call that worked at all.
language is now omitted when a tokenizer is present (from either tokenizer or components.tokenizer), and i18n servers no longer overwrite a caller-supplied language. Attaching a stemmer to the default multilingual segmentation works as documented:
The playground's Variables panel exposes every argument of an operation, but the generated query only declares the required ones. Setting an optional argument used to send a variable the document never references, which servers ignore — the request looked successful but ran without it.
The query's variable declarations now follow the panel: setting an argument declares it, unsetting removes it. Sending an operation declares whatever the panel has set, so a query edited by hand can no longer drop a value. Declarations and values written by hand are never rewritten.
Also fixes the playground's form editing the example rendered on the page, which made Reset restore edited values instead of the original ones.
Introduce @fumadocs/graphql
Generate API reference docs from your GraphQL schemas, similar to the OpenAPI/AsyncAPI integration.
It accepts SDL files (including extend type), SDL text, introspection results, and GraphQLSchema instances, and generates per-operation & per-type pages with arguments/fields, deprecations, custom directive callouts, and example queries/responses.
Highlights:
Playground: syntax-highlighted query editor with live validation, a typed variables form generated from argument types, per-endpoint header presets, and GraphQL-aware error display. Configurable via playground.url, allowUrlEdit, headers, fetcher and render.
Usage backlinks: type pages list where a type is returned, used as a field, or accepted as input.
Cross-linking out of the box: pass baseUrl (the baseUrl of your loader()) to staticSource() and type & operation references link to their pages automatically.
Request snippets: generated cURL & JavaScript tabs next to the example query.
Introduce @fumadocs/graphql
Generate API reference docs from your GraphQL schemas, similar to the OpenAPI/AsyncAPI integration.
It accepts SDL files (including extend type), SDL text, introspection results, and GraphQLSchema instances, and generates per-operation & per-type pages with arguments/fields, deprecations, custom directive callouts, and example queries/responses.
Highlights:
Playground: syntax-highlighted query editor with live validation, a typed variables form generated from argument types, per-endpoint header presets, and GraphQL-aware error display. Configurable via playground.url, allowUrlEdit, headers, fetcher and render.
Usage backlinks: type pages list where a type is returned, used as a field, or accepted as input.
Cross-linking out of the box: pass baseUrl (the baseUrl of your loader()) to staticSource() and type & operation references link to their pages automatically.
Request snippets: generated cURL & JavaScript tabs next to the example query.
@fumadocs/base-ui@16.14.0
fumadocs-core@16.14.0
fumadocs-ui@16.14.0
Replace Orama with ZBSearch, zero-config i18n search
The built-in search engine moved from @orama/orama to ZBSearch, a near drop-in successor. All module paths and APIs are unchanged, and search now works with every language out of the box: the new default multilingual mode uses Unicode word segmentation, so i18n search needs zero config.
import { createFromSource } from 'fumadocs-core/search/server';// no `localeMap`, no `@orama/tokenizers`, CJK includedexport const { GET } = createFromSource(source);
All locales now share a single search database — results are filtered by the locale of your pages at query time. Same for static mode:
oramaStaticClient → staticClient (old name kept as deprecated alias)
initOrama → initDB, it now creates a ZBSearch instance and is optional — the exported data restores the tokenizer on load
Deprecated
localeMap is no longer needed. It still works for language-specific stemming/stop-words and keeps the legacy per-locale databases when specified.
Notes for advanced usage
language, components, plugins and search options are now typed against ZBSearch instead of @orama/orama — custom tokenizers or plugins written for Orama must be swapped to their ZBSearch equivalents.
The exported static search data is now a ZBSearch database (i18n exports became a single unified database), so server and client should be on the same fumadocs-core version.
@orama/orama and @orama/tokenizers can be removed from your dependencies unless you use them directly. Orama Cloud integrations (fumadocs-core/search/orama-cloud) are unaffected.
Replace Orama with ZBSearch, zero-config i18n search
The built-in search engine moved from @orama/orama to ZBSearch, a near drop-in successor. All module paths and APIs are unchanged, and search now works with every language out of the box: the new default multilingual mode uses Unicode word segmentation, so i18n search needs zero config.
import { createFromSource } from 'fumadocs-core/search/server';// no `localeMap`, no `@orama/tokenizers`, CJK includedexport const { GET } = createFromSource(source);
All locales now share a single search database — results are filtered by the locale of your pages at query time. Same for static mode:
oramaStaticClient → staticClient (old name kept as deprecated alias)
initOrama → initDB, it now creates a ZBSearch instance and is optional — the exported data restores the tokenizer on load
Deprecated
localeMap is no longer needed. It still works for language-specific stemming/stop-words and keeps the legacy per-locale databases when specified.
Notes for advanced usage
language, components, plugins and search options are now typed against ZBSearch instead of @orama/orama — custom tokenizers or plugins written for Orama must be swapped to their ZBSearch equivalents.
The exported static search data is now a ZBSearch database (i18n exports became a single unified database), so server and client should be on the same fumadocs-core version.
@orama/orama and @orama/tokenizers can be removed from your dependencies unless you use them directly. Orama Cloud integrations (fumadocs-core/search/orama-cloud) are unaffected.
@fumadocs/base-ui@16.13.0
fumadocs-core@16.13.0
fumadocs-ui@16.13.0
Add hotkey for toggling light/dark mode
Press D to toggle between light and dark mode. It is ignored while typing in an editable element or when a dialog (e.g. search) is opened.
Customise it with the theme.hotKey option of <RootProvider />, or pass false to disable.
Respect quality values in isMarkdownPreferred
isMarkdownPreferred() previously returned true whenever a Markdown media type appeared anywhere
in Accept, ignoring how the client ranked it. A request for Accept: text/html;q=0.9, text/markdown;q=0.1
was served Markdown even though it clearly preferred HTML.
It now compares the client's highest quality value for a Markdown type against its highest value for
HTML, and only prefers Markdown when Markdown ranks at least as high. Wildcards (*/*, text/*)
count towards HTML, so Accept: */* keeps receiving HTML.
A tie still prefers Markdown, so agents sending Accept: text/html,text/markdown,text/plain,*/*;q=0.5
are unaffected.
The negotiation examples and templates now also set Vary: Accept on the negotiated Markdown
response, so shared caches key on the header the representation was selected by.
@fumadocs/base-ui@16.12.0
fumadocs-core@16.12.0
fumadocs-ui@16.12.0
Fix invalid list semantics in Home layout navbar
NavigationMenu.List in the Home layout header defaults to a <ul>, but its direct children (nav title link, link groups, control clusters) are not <li> elements, which is an accessibility violation (axe list, serious) on every page using HomeLayout. Render it as a <div> instead — the same intent as the Radix UI variant, which already renders its list as a non-<ul> element via asChild.
Introduce Glass Layout
A new layout for docs, a smooth, beautiful variant built around floating, translucent panels.
Don't force-mount inactive tab content by default
Styled Tabs previously kept every tab panel mounted in the DOM (hidden with display: none). Inactive panels are now unmounted by default, following the underlying primitive.
You can still opt back into keeping panels mounted per tab with forceMount (fumadocs-ui) or keepMounted (@fumadocs/base-ui) on Tab / TabsContent.
Open the tab containing a linked heading
When a tab's content stays mounted (forceMount / keepMounted), navigating to a URL hash that points to an element inside a tab — such as a Table of Contents link to a heading — now opens the tab it belongs to and scrolls to the target. This runs on both initial load and hashchange.