Relevant source files
The following files were used as context for generating this wiki page:
- packages/preview/src/lib/source/storage.ts
- packages/content/src/index.ts
- packages/local-md/src/index.ts
- packages/core/src/source/loader.ts
- packages/mdx/src/runtime/server.ts
- packages/content-collections/src/index.ts
- packages/content/src/runtime.ts
- packages/asyncapi/src/server/index.tsx
- packages/mdx/src/loaders/meta.ts
- packages/core/src/source/dynamic.ts
- packages/openapi/src/server/index.tsx
- packages/preview/src/lib/source/index.ts
- packages/local-md/src/storage.ts
- packages/mdx/src/runtime/dynamic.ts
- packages/mdx/src/webpack/meta.ts
- packages/core/src/source/storage/content.ts
- packages/mdx/src/plugins/index-file.ts
- packages/sanity/src/index.ts
- packages/preview/src/pages/...slugs.tsx
- packages/openapi/src/utils/document/load.ts
- packages/core/src/source/client/index.tsx
- packages/content-collections/src/configuration.ts
- packages/mdx/src/loaders/mdx/index.ts
- packages/obsidian/src/build-storage.ts
- packages/core/src/source/source.ts
- packages/core/src/source/plugins/slugs.ts
- packages/python/fumapy/mksource/init.py
- packages/story/src/webpack/story.ts
- packages/mdx/src/core.ts
- packages/mdx/src/loaders/mdx/remark-include.ts
Overview
Fumadocs source loaders ingest, normalize, and index document collections from diverse sources like local disk storage, Content Collections, and remote OpenAPI or AsyncAPI specs into a unified virtual file system. They expose public contracts and virtual file definitions via source and StaticSource, mapping files to typed page and metadata objects.
Sources: packages/core/src/source/source.ts:1-114, packages/core/src/source/storage/content.ts:1-42
The core loader pipeline parses frontmatter, handles internationalization directories or extension codes, applies loader plugins for slugs and builds hierarchical page trees and search indices. Sources: packages/core/src/source/loader.ts:268-315, packages/core/src/source/storage/content.ts:50-126, packages/core/src/source/plugins/slugs.ts:15-42
Through dynamic loaders and client-side deserialization hooks, Fumadocs supports runtime revalidation, hot reloading, and preloaded remote document integration. Sources: packages/core/src/source/dynamic.ts:27-119, packages/core/src/source/client/index.tsx:14-60, packages/local-md/src/index.ts:84-208
Unified source interface and contracts
Core data structures and virtual file contracts
Fumadocs source loaders rely on a shared type architecture defined by SourceUnion, distinguishing between StaticSource and DynamicSource objects via guard functions isStaticSource and isDynamicSource. Static sources maintain an array of VirtualFile items directly, whereas dynamic sources supply an asynchronous or synchronous builder function files: () => Awaitable<VirtualFile<Config>[]> alongside an optional configure hook accepting a DynamicLoader.
Sources: packages/core/src/source/source.ts:5-21, packages/core/src/source/source.ts:172-178
Every virtual file extends BaseVirtualFile, holding a virtualized relative path and an optional absolute path on disk. Depending on its type discriminant, a file resolves to either a VirtualPage carrying slugs and typed pageData, or a VirtualMeta carrying typed metaData.
Sources: packages/core/src/source/source.ts:48-78
| Property / Type | Field Type | Purpose & Description |
|---|---|---|
path | string | Virtualized path relative to the content directory (e.g. docs/page.mdx). |
absolutePath | string (optional) | Absolute path of the underlying file on disk. |
type | 'page' | 'meta' | Discriminant property identifying whether the virtual file is a page or metadata file. |
slugs | string[] (optional) | Explicit route segments assigned to a virtual page. |
data | PageData | MetaData | Payload containing frontmatter attributes, titles, icons, or navigation controls. |
Sources: packages/core/src/source/source.ts:52-78
Note
The multiple helper utility is deprecated in favor of passing a record of source objects directly to the main loader() function, which automatically scopes namespaces and injects type tags.
Sources: packages/core/src/source/source.ts:80-82
Metadata schemas and builder utilities
The base metadata structures define standard properties supported across documentation nodes. MetaData configures folder navigation and display parameters such as icon, title, root, pages, pagesIndex, defaultOpen, collapsible, and description. PageData configures individual document attributes including icon, title, description, and structuredData (which accepts search index payloads or asynchronous generators).
Sources: packages/core/src/source/source.ts:28-46
export function source<Page extends PageData, Meta extends MetaData>(config: {
pages: VirtualPage<Page>[];
metas: VirtualMeta<Meta>[];
}): StaticSource<{
pageData: Page;
metaData: Meta;
}> {
return {
files: [...config.pages, ...config.metas],
};
}
interface SourceUpdater<Config extends SourceConfig> {
files: <Page extends PageData, Meta extends MetaData>(
fn: (files: VirtualFile<Config>[]) => (VirtualPage<Page> | VirtualMeta<Meta>)[],
) => SourceUpdater<{
pageData: Page;
metaData: Meta;
}>;
page: <V extends PageData>(
fn: (page: VirtualPage<Config['pageData']>) => VirtualPage<V>,
) => SourceUpdater<{
pageData: V;
metaData: Config['metaData'];
}>;
meta: <V extends MetaData>(
fn: (meta: VirtualMeta<Config['metaData']>) => VirtualMeta<V>,
) => SourceUpdater<{
pageData: Config['pageData'];
metaData: V;
}>;
build: () => StaticSource<Config>;
}
/**
* update a source object in-place.
*/
export function update<Config extends SourceConfig>(
source: StaticSource<Config>,
): SourceUpdater<Config> {
return {
files(fn) {
source.files = fn(source.files);
return this as SourceUpdater<never>;
},
page(fn) {
for (let i = 0; i < source.files.length; i++) {
const file = source.files[i];
if (file.type === 'page') source.files[i] = fn(file);
}
return this as SourceUpdater<never>;
},
meta(fn) {
for (let i = 0; i < source.files.length; i++) {
const file = source.files[i];
if (file.type === 'meta') source.files[i] = fn(file);
}
return this as SourceUpdater<never>;
},
build() {
return source;
},
};
}Sources: packages/core/src/source/source.ts:104-170
External adapters and runtime transformation
External integrations bridge third-party content providers into the standard Fumadocs source contract. The Content Collections Adapter maps allDocs and allMetas arrays into VirtualPage and VirtualMeta structures via createMDXSource, binding _meta.filePath directly to file paths.
Sources: packages/content-collections/src/index.ts:4-35
Similarly, runtime content stores convert compiled MDX collections and metadata stores into standard StaticSource objects through toFumadocsSource and docsStore. Frontmatter properties are merged directly into page data objects alongside absolute path tracking.
Sources: packages/content/src/runtime.ts:12-56
Warning
When integrating custom runtime stores, ensure frontmatter properties do not overwrite core VirtualPage discriminatory fields (type, path, absolutePath) during object spreading.
Sources: packages/content/src/runtime.ts:23-32
In-memory filesystem and content storage
Storage construction and plugin transformation
The createContentStorageBuilder function transforms raw static or multi-namespace content inputs into an indexed virtual file system. When internationalization is enabled, parser routines strip locale codes from file paths based on either directory structures or filename extensions, mapping entries into a shared fileMap indexed by target language or an EmptyLang symbol.
Sources: packages/core/src/source/storage/content.ts:43-93
export function createContentStorageBuilder(loaderConfig: ResolvedLoaderConfig) {
const { input, plugins, i18n } = loaderConfig;
let parser: (normalizedPath: string) => [string, (string | string[])?];
if (!i18n) {
parser = (path) => [path];
} else if (i18n.parser === 'dir') {
const langSet = new Set(i18n.languages);
parser = (path) => {
const [locale, ...segs] = path.split('/');
if (!locale || segs.length === 0) return [path];
if (langSet.has(locale)) return [segs.join('/'), locale];
if (locale === '$') return [segs.join('/'), i18n.languages];
return [path];
};
} else {
const langSet = new Set(i18n.languages);
parser = (path) => {
const segs = path.split('/');
const base = segs.pop();
if (!base) return [path];
const parts = base.split('.');
if (parts.length < 3) return [path];
const [locale] = parts.splice(parts.length - 2, 1);
segs.push(parts.join('.'));
if (langSet.has(locale)) return [segs.join('/'), locale];
if (locale === '$') return [segs.join('/'), i18n.languages];
return [path];
};
}Sources: packages/core/src/source/storage/content.ts:50-87
During the scanning phase, input files are categorized as either page or metadata files. Each file receives a normalized path, format descriptor, source type, and underlying data payload before being inserted into the storage map. Sources: packages/core/src/source/storage/content.ts:94-126
| Storage File Type | Format Discriminant | Required Properties | Optional Properties |
|---|---|---|---|
ContentStoragePageFile | 'page' | format, path, slugs, data | type, absolutePath |
ContentStorageMetaFile | 'meta' | format, path, data | type, absolutePath |
Sources: packages/core/src/source/storage/content.ts:17-41
Note
When evaluating language parsers, if a file path segment equals $, the parser maps the file across all configured internationalization languages simultaneously.
Sources: packages/core/src/source/storage/content.ts:64-83
Once files are indexed within fileMap, makeStorage instantiates a FileSystem instance for a given locale. It populates the filesystem with corresponding storage entries and executes any registered plugin storage transformation hooks.
Sources: packages/core/src/source/storage/content.ts:134-146
function makeStorage(locale: string | typeof EmptyLang, inherit?: ContentStorage) {
const storage = new FileSystem(inherit) as ContentStorage;
for (const [storageKey, file] of fileMap.get(locale) ?? []) {
storage.write(storageKey, file);
}
const context = { storage };
for (const plugin of plugins) {
plugin.transformStorage?.(context);
}
return storage;
}Sources: packages/core/src/source/storage/content.ts:134-146
Dynamic loader resolution and caching
Sources: packages/core/src/source/dynamic.ts:52-79, packages/core/src/source/source.ts:172-174
The dynamicLoader factory handles runtime content resolution, source configuration, and memoization for dynamic and static documentation sources. During initialization, configureSources iterates over input sources and executes v.configure?.(dynamicLoader) when an input matches dynamic source criteria.
Sources: packages/core/src/source/dynamic.ts:40-50, packages/core/src/source/dynamic.ts:116-116
The resolution pipeline follows a strict call chain: dynamicLoader invokes resolveSources, which calls resolveSource on each source object, delegating to isStaticSource for type discrimination.
Sources: packages/core/src/source/dynamic.ts:52-79
async function resolveSources(skipCache = false): Promise<ResolvedSource> {
if (isStaticSource(input) || isDynamicSource(input)) {
return resolveSource(input, skipCache);
}
const entries = await Promise.all(
Object.entries(input).map(async ([k, v]) => [k, await resolveSource(v, skipCache)]),
);
return Object.fromEntries(entries);
}Sources: packages/core/src/source/dynamic.ts:52-62
Warning
Bypassing cache via skipCache = true forces resolveSource to re-execute v.files() and overwrite existing entries in sourceCache, discarding previously memoized virtual file arrays.
Sources: packages/core/src/source/dynamic.ts:64-79
Source equality is evaluated by isEqual, which checks reference identity for static sources or compares key counts and strict reference equality for multi-source records.
Sources: packages/core/src/source/dynamic.ts:121-133
function isEqual(a: ResolvedSource, b: ResolvedSource): boolean {
if (isStaticSource(a) && isStaticSource(b)) {
return a === b;
}
if (!isStaticSource(a) && !isStaticSource(b)) {
const aKeys = Object.keys(a);
const bKeys = Object.keys(b);
return aKeys.length === bKeys.length && aKeys.every((k) => a[k] === b[k]);
}
return false;
}Sources: packages/core/src/source/dynamic.ts:121-133
Loader configuration, plugin pipeline, and execution walkthrough
Sources: packages/core/src/source/dynamic.ts:90-93, packages/core/src/source/loader.ts:287-301, packages/core/src/source/loader.ts:474-499
The execution walkthrough for building plugins and resolving configurations traces through dynamicLoader → loader → resolveConfig. When dynamicLoader computes its cache via get(), it invokes loader() with the resolved source and options. The loader function immediately delegates to resolveConfig(), which merges base settings, slugs options, icons, and plugins into a unified ResolvedLoaderConfig.
Sources: packages/core/src/source/dynamic.ts:27-119, packages/core/src/source/loader.ts:287-499
export function loader<I extends ResolvedInput, I18n extends I18nConfig | undefined = undefined>(
...args:
| [
LoaderOptions & {
source: I;
},
]
| [I, LoaderOptions]
): LoaderOutput<{
meta: GenerateMeta<I>;
page: GeneratePage<I>;
i18n: I18n;
}> {
const loaderConfig =
args.length === 2 ? resolveConfig(args[0], args[1]) : resolveConfig(args[0].source, args[0]);Sources: packages/core/src/source/loader.ts:287-301
Note
resolveConfig unpacks base loader configurations and validates internationalization parameters before content storage builder initialization.
Sources: packages/core/src/source/loader.ts:474-479
The slugs plugin (slugsPlugin) inspects files via storage.getFiles(), processing non-page items or files with pre-existing slugs. When a custom slugFn is absent, it identifies index files via isIndex, pushing them to an index queue to prevent collision conflicts between dir/index.mdx and dir.mdx.
Sources: packages/core/src/source/plugins/slugs.ts:15-35
transformStorage({ storage }) {
const indexFiles: string[] = [];
const taken = new Set<string>();
for (const path of storage.getFiles()) {
const file = storage.read(path);
if (!file || file.format !== 'page' || file.slugs) continue;
const customSlugs = slugFn?.(file);
// for custom slugs function, don't handle conflicting cases like `dir/index.mdx` vs `dir.mdx`
if (customSlugs === undefined && isIndex(path)) {
indexFiles.push(path);
continue;
}
file.slugs = customSlugs ?? getSlugs(path);
const key = file.slugs.join('/');
if (taken.has(key)) throw new Error(`Duplicated slugs: ${key}`);
taken.add(key);
}Sources: packages/core/src/source/plugins/slugs.ts:22-41
Warning
Duplicate slug keys detected across storage entries trigger an immediate error throw via if (taken.has(key)) throw new Error(...).
Sources: packages/core/src/source/plugins/slugs.ts:38-40
Path segregation filters out empty directory names and folder groups matching GroupRegex (/^\(.+\)$/). Any attempt to use a folder group directly inside a file name throws an error.
Sources: packages/core/src/source/plugins/slugs.ts:69-85
export function getSlugs(file: string): string[] {
const dir = dirname(file);
const name = basename(file, extname(file));
const slugs: string[] = [];
for (const seg of dir.split('/')) {
// filter empty names and file groups like (group_name)
if (seg.length > 0 && !GroupRegex.test(seg)) slugs.push(encodeURI(seg));
}
if (GroupRegex.test(name)) throw new Error(`Cannot use folder group in file names: ${file}`);
if (name !== 'index') {
slugs.push(encodeURI(name));
}
return slugs;Sources: packages/core/src/source/plugins/slugs.ts:74-90
Local markdown and disk storage
File scanning and storage implementation
Sources: packages/local-md/src/storage.ts:23-56, packages/local-md/src/storage.ts:110-126
Local markdown collections are managed by createStorage, which scans content directories using tinyglobby with configurable include patterns. Discovered files are batched into chunks of 100 items (CHUNK_SIZE) and processed concurrently via buildFile. Files are discriminated by extension: .json files are parsed as metadata collections, while .md and .mdx files are processed for frontmatter and body content.
Sources: packages/local-md/src/storage.ts:23-56, packages/local-md/src/storage.ts:110-126
async getPages() {
const files = await glob(include, {
cwd: dir,
});
const chunks: Promise<($Page | $Meta | undefined)[]>[] = [];
for (let i = 0; i < files.length; i += CHUNK_SIZE) {
const promises: Promise<$Page | $Meta | undefined>[] = [];
const L = Math.min(files.length, i + CHUNK_SIZE);
for (let j = i; j < L; j++) {
promises.push(buildFile(files[j]));
}
chunks.push(Promise.all(promises));
}
const pages: $Page[] = [];
const metas: $Meta[] = [];
for await (const chunk of chunks) {
for (const item of chunk) {
if (!item) continue;
if ('frontmatter' in item) pages.push(item);
else metas.push(item);
}
}
return { pages, metas };
},Sources: packages/local-md/src/storage.ts:110-138
Warning
If building a file throws an error, the catch block logs the failure to the console and deletes the corresponding absolute path entry from filesCache.
Sources: packages/local-md/src/storage.ts:62-66
Frontmatter parsing and schema validation
async function md(absolutePath: string, file: string): Promise<$Page> {
const content = await fs.readFile(absolutePath, 'utf-8');
const parsed = parseFrontmatter(content);
const frontmatterResult = await frontmatterSchema['~standard'].validate(parsed.data);
if (frontmatterResult.issues) {
const message = frontmatterResult.issues
.map((issue) => (issue.path ? `${issue.path}: ${issue.message}` : issue.message))
.join('\n');
throw new Error(`invalid frontmatter in "${absolutePath}": ${message}`);
}
return {
path: file,
absolutePath,
content: parsed.content,
frontmatter: frontmatterResult.value,
};
}Sources: packages/local-md/src/storage.ts:68-86
Markdown and MDX files are read from disk and processed with parseFrontmatter to separate content from metadata headers. The extracted frontmatter object is validated against standard schemas using frontmatterSchema['~standard'].validate(). If validation fails, issues are mapped into a detailed multi-line error string reporting path and message specifics.
Sources: packages/local-md/src/storage.ts:68-86
| Extension | Target Interface | Validation Schema | Parser Utility |
|---|---|---|---|
.md, .mdx | RawPage | frontmatterSchema | parseFrontmatter |
.json | RawMeta | metaSchema | JSON.parse |
Sources: packages/local-md/src/storage.ts:10-21, packages/local-md/src/storage.ts:72-97
Caution
Schema validation errors halt execution immediately by throwing an Error containing formatted validation issues for either invalid frontmatter or invalid JSON data.
Sources: packages/local-md/src/storage.ts:73-78, packages/local-md/src/storage.ts:92-97
Runtime adapters and dev server integration
export function localMd<
FrontmatterSchema extends StandardSchemaV1 = typeof defaultSchemas.pageSchema,
MetaSchema extends StandardSchemaV1 = typeof defaultSchemas.metaSchema,
>(
config: LocalMarkdownConfig<FrontmatterSchema, MetaSchema>,
): LocalMarkdown<FrontmatterSchema, MetaSchema> {
const storage = createStorage(config);
const compiler = createMarkdownCompiler(config);
const compilerCache = new WeakMap<RawPage<unknown>, Promise<CompileResult>>();
const registeredLoaders = new Set<DynamicLoader>();
let cachedStaticSource: Promise<
StaticSource<{
pageData: LocalMarkdownPage<StandardSchemaV1.InferOutput<FrontmatterSchema>, never>;
metaData: StandardSchemaV1.InferOutput<MetaSchema> & MetaData;
}>
> | null = null;
async function createFiles(options?: SourceOptions) {
const { metas, pages } = await storage.getPages();
const baseDir = options?.baseDir;
const files: VirtualFile<{
pageData: LocalMarkdownPage<StandardSchemaV1.InferOutput<FrontmatterSchema>, never>;
metaData: StandardSchemaV1.InferOutput<MetaSchema> & MetaData;
}>[] = [];
for (const page of pages) {
const frontmatter = page.frontmatter as PageData & { _openapi?: unknown };
files.push({
type: 'page',
path: baseDir ? path.join(baseDir, page.path) : page.path,
absolutePath: page.absolutePath,
data: {
title: frontmatter.title ?? path.basename(page.path, path.extname(page.path)),
description: frontmatter.description,
icon: frontmatter.icon,
// for Fumadocs OpenAPI
['_openapi' as never]: frontmatter._openapi,
content: page.content,
frontmatter: page.frontmatter,
async load() {
let promise = compilerCache.get(page);
if (!promise) {
promise = compiler.compile({
path: page.absolutePath,
value: page.content,
data: { frontmatter: page.frontmatter },
});
}
const res = await promise;
return res.type === 'ast'
? fromAst({
tree: res.tree,
filePath: res.file.path,
rehypeToc: res.file.data.rehypeToc,
structuredData: res.file.data.structuredData,
...config.rendererOptions,
})
: fromJS({
code: res.code,
filePath: res.file.path,
baseUrl: pathToFileURL(res.file.path).href,
structuredData: res.file.data.structuredData,
});
},
},
});
}Sources: packages/local-md/src/index.ts:84-153
The localMd function exposes staticSource and dynamicSource runtime adapters that transform raw storage entities into virtual files consumed by core loaders. Each page object provides an asynchronous load method that compiles content via a markdown compiler instance, utilizing compilerCache to memoize compilation promises per page. When connected to a development server via devServer, the runner watches the target directory and dispatches file invalidation events.
Sources: packages/local-md/src/index.ts:84-153, packages/local-md/src/index.ts:174-195
Note
Calling invalidateFile resets cachedStaticSource to null, evicts the path from storage, and triggers invalidation callbacks on all registered dynamic loaders.
Sources: packages/local-md/src/index.ts:168-173
Client deserialization and preview runtime
Client-side page tree consumption
export function deserializePageTree(serialized: SerializedPageTree): PageTree.Root {
const root = serialized.data as PageTree.Root;
visit(root, (item) => {
if ('icon' in item && typeof item.icon === 'string') {
item.icon = (
<span
dangerouslySetInnerHTML={{
__html: item.icon,
}}
/>
);
}
if (typeof item.name === 'string') {
item.name = (
<span
className="fd-page-tree-item-name"
dangerouslySetInnerHTML={{
__html: item.name,
}}
/>
);
}
});
return root;
}Sources: packages/core/src/source/client/index.tsx:14-39
The client package handles serialized navigation trees generated by the server-side loader through deserializePageTree. It inspects incoming objects using isSerializedPageTree, verifying that $fumadocs_loader is set to 'page-tree'. Once validated, visit traverses the tree nodes, parsing raw HTML strings for item icons and names into React span elements configured with dangerouslySetInnerHTML.
Sources: packages/core/src/source/client/index.tsx:5-8, packages/core/src/source/client/index.tsx:14-39, packages/core/src/source/client/index.tsx:62-69
export function useFumadocsLoader<V>(serialized: V): Serialized<V> {
return useMemo(() => {
const out: Record<string, unknown> = {};
for (const k in serialized) {
const v = serialized[k];
if (isSerializedPageTree(v)) {
out[k] = deserializePageTree(v);
} else {
out[k] = v;
}
}
return out as Serialized<V>;
}, [serialized]);
}Sources: packages/core/src/source/client/index.tsx:47-60
The useFumadocsLoader hook memoizes deserialization across re-renders. It iterates over the properties of the provided serialized object, converting matching page tree structures while preserving all unrelated properties in the output.
Sources: packages/core/src/source/client/index.tsx:47-60
Note
useFumadocsLoader preserves any non-loader properties present on the input object alongside the freshly parsed page tree elements.
Sources: packages/core/src/source/client/index.tsx:42-46, packages/core/src/source/client/index.tsx:54-56
Preview revalidation hooks and route rendering
export const getSource = revalidable({
async create(config: ParsedAppConfig) {
return loader({
source: source(await getPages(config.content)),
plugins: [lucideIconsPlugin()],
baseUrl: '/',
});
},
});Sources: packages/preview/src/lib/source/index.ts:7-15
The preview application defines getSource via a revalidable wrapper that instantiates a content loader using source with local page data and the lucideIconsPlugin. Document paths and page image routes are managed by getPageImage, which appends image.webp segments to the URL path array.
Sources: packages/preview/src/lib/source/index.ts:7-27
export default async function DocPage({ slugs }: PageProps<'/docs/[...slugs]'>) {
const config = await getConfigRuntime();
const source = await getSource(config);
const page = source.getPage(slugs);
const mdPreset = config.layout.presets?.md ?? 'docs';
let mdPresetComponents: MdPresetComponents;
if (mdPreset === 'docs') {
mdPresetComponents = {
layout: await import('fumadocs-ui/layouts/docs'),
page: await import('fumadocs-ui/layouts/docs/page'),
};
} else {
mdPresetComponents = {
layout: await import('fumadocs-ui/layouts/flux'),
page: await import('fumadocs-ui/layouts/flux/page'),
};
}
return <MdContent slugs={slugs} page={page} components={mdPresetComponents} />;
}Sources: packages/preview/src/pages/...slugs.tsx:76-96
The dynamic route component DocPage resolves runtime configurations and dynamic layout presets (docs or flux) before rendering document contents. During compilation, remark and rehype plugins process markdown tables, mathematics, mermaid diagrams, code blocks, and table of contents data.
Sources: packages/preview/src/pages/...slugs.tsx:29-46, packages/preview/src/pages/...slugs.tsx:76-96
| Preset Name | Layout Import | Page Import |
|---|---|---|
docs | fumadocs-ui/layouts/docs | fumadocs-ui/layouts/docs/page |
| Other | fumadocs-ui/layouts/flux | fumadocs-ui/layouts/flux/page |
Sources: packages/preview/src/pages/...slugs.tsx:83-93
Warning
If markdown compilation encounters an error, DocPage catches the exception and renders a fallback error view containing the exception message and stack trace inside a code block.
Sources: packages/preview/src/pages/[...slugs].tsx:169-194
Related
On This Page
OverviewUnified source interface and contractsCore data structures and virtual file contractsMetadata schemas and builder utilitiesExternal adapters and runtime transformationIn-memory filesystem and content storageStorage construction and plugin transformationDynamic loader resolution and cachingLoader configuration, plugin pipeline, and execution walkthroughLocal markdown and disk storageFile scanning and storage implementationFrontmatter parsing and schema validationRuntime adapters and dev server integrationClient deserialization and preview runtimeClient-side page tree consumptionPreview revalidation hooks and route renderingRelated