---
title: "Preview Environment"
description: "The Preview Environment subsystem provides a robust, real-time development and documentation rendering experience. By integrating dynamic compilation, filesystem watching, and reactive UI component..."
last_updated: "2026-07-02T09:46:39.499576+00:00"
canonical_url: "https://www.doc0.dev/docs/e8872a72-1909-479c-b585-dc74c6e26726/technical/developer-tools/preview-environment"
---

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

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

- [packages/preview/src/components/ai/search.tsx](https://github.com/blade47/fumadocs/blob/main/packages/preview/src/components/ai/search.tsx)
- [packages/preview/src/pages/[...slugs].tsx](https://github.com/blade47/fumadocs/blob/main/packages/preview/src/pages/%5B...slugs%5D.tsx)
- [packages/preview/src/pages/_api/api/chat.ts](https://github.com/blade47/fumadocs/blob/main/packages/preview/src/pages/_api/api/chat.ts)
- [packages/asyncapi/src/ui/contexts/api.tsx](https://github.com/blade47/fumadocs/blob/main/packages/asyncapi/src/ui/contexts/api.tsx)
- [packages/openapi/src/ui/operation/index.tsx](https://github.com/blade47/fumadocs/blob/main/packages/openapi/src/ui/operation/index.tsx)
- [packages/mdx/src/runtime/dynamic.ts](https://github.com/blade47/fumadocs/blob/main/packages/mdx/src/runtime/dynamic.ts)
- [packages/openapi/src/ui/contexts/api.tsx](https://github.com/blade47/fumadocs/blob/main/packages/openapi/src/ui/contexts/api.tsx)
- [packages/create-app/src/plugins/ai.ts](https://github.com/blade47/fumadocs/blob/main/packages/create-app/src/plugins/ai.ts)
- [packages/preview/src/lib/source/index.ts](https://github.com/blade47/fumadocs/blob/main/packages/preview/src/lib/source/index.ts)
- [packages/preview/src/pages/_api/img.ts](https://github.com/blade47/fumadocs/blob/main/packages/preview/src/pages/_api/img.ts)
- [packages/api-docs/src/components/schema/client.tsx](https://github.com/blade47/fumadocs/blob/main/packages/api-docs/src/components/schema/client.tsx)
- [packages/preview/src/config/load-runtime.ts](https://github.com/blade47/fumadocs/blob/main/packages/preview/src/config/load-runtime.ts)
- [packages/preview/src/cli/commands.ts](https://github.com/blade47/fumadocs/blob/main/packages/preview/src/cli/commands.ts)
- [packages/preview/src/components/hot-reload.tsx](https://github.com/blade47/fumadocs/blob/main/packages/preview/src/components/hot-reload.tsx)
- [packages/mdx/src/runtime/browser.tsx](https://github.com/blade47/fumadocs/blob/main/packages/mdx/src/runtime/browser.tsx)
- [packages/preview/src/layouts/config.tsx](https://github.com/blade47/fumadocs/blob/main/packages/preview/src/layouts/config.tsx)
- [packages/asyncapi/src/ui/api-page.tsx](https://github.com/blade47/fumadocs/blob/main/packages/asyncapi/src/ui/api-page.tsx)
- [packages/preview/src/lib/ai.ts](https://github.com/blade47/fumadocs/blob/main/packages/preview/src/lib/ai.ts)
- [packages/preview/src/pages/_api/api/search.ts](https://github.com/blade47/fumadocs/blob/main/packages/preview/src/pages/_api/api/search.ts)
- [packages/core/src/framework/index.tsx](https://github.com/blade47/fumadocs/blob/main/packages/core/src/framework/index.tsx)
- [packages/preview/src/pages/_root.tsx](https://github.com/blade47/fumadocs/blob/main/packages/preview/src/pages/_root.tsx)
- [packages/preview/src/lib/env.d.ts](https://github.com/blade47/fumadocs/blob/main/packages/preview/src/lib/env.d.ts)
- [packages/core/src/mdx-plugins/remark-llms.runtime.ts](https://github.com/blade47/fumadocs/blob/main/packages/core/src/mdx-plugins/remark-llms.runtime.ts)
- [packages/preview/src/components/markdown.tsx](https://github.com/blade47/fumadocs/blob/main/packages/preview/src/components/markdown.tsx)
- [packages/core/src/framework/waku.tsx](https://github.com/blade47/fumadocs/blob/main/packages/core/src/framework/waku.tsx)
- [packages/preview/src/waku.server.tsx](https://github.com/blade47/fumadocs/blob/main/packages/preview/src/waku.server.tsx)
- [packages/openapi/src/ui/operation/context.tsx](https://github.com/blade47/fumadocs/blob/main/packages/openapi/src/ui/operation/context.tsx)
- [packages/core/src/framework/react-router.tsx](https://github.com/blade47/fumadocs/blob/main/packages/core/src/framework/react-router.tsx)
- [packages/preview/src/pages/_api/og/[...slugs]/image.webp.tsx](https://github.com/blade47/fumadocs/blob/main/packages/preview/src/pages/_api/og/%5B...slugs%5D/image.webp.tsx)
- [packages/preview/src/components/provider.tsx](https://github.com/blade47/fumadocs/blob/main/packages/preview/src/components/provider.tsx)
</details>

The Preview Environment subsystem provides a robust, real-time development and documentation rendering experience. By integrating dynamic compilation, filesystem watching, and reactive UI components, it enables developers to view documentation content as it is authored while supporting advanced features like AI-powered search, API playground interactions, and hot reloading.

The environment acts as a bridge between static content and a live, interactive UI. It orchestrates the loading of documentation sources from the filesystem, processes MDX content through a unified compiler, and renders the result within a framework-agnostic layout. Central to this architecture is the decoupling of the content engine from the rendering framework, allowing for consistent behavior whether running in development or a production-preview context.

By centralizing configuration (via `load-runtime.ts`) and leveraging hot-reloading mechanisms (via `waku.server.tsx`), the Preview Environment ensures that changes to source files are immediately reflected in the user's view. This system eliminates the latency typical of static site regeneration during development, creating a fluid, high-fidelity experience for authors and technical documentation consumers alike.

## Dynamic Content Compilation
The system employs a dynamic compilation pipeline to transform Markdown and MDX content on the fly. This avoids pre-build overhead and ensures the latest document content is always available. When a request is made, the `compiler` (a `createMarkdownCompiler` instance) processes the source using remark and rehype plugins, including support for GFM, math, code blocks, and Mermaids diagrams.

The pipeline specifically handles file resolution through `getPage` and compiles the document content within the `MdContent` component. If compilation fails, the system catches the error and renders an error page displaying the stack trace, preventing the entire application from crashing.

```mermaid
flowchart TD
    A["Request Source Page"] --> B{"Page Exists?"}
    B -- Yes --> C["Compile<br>Markdown"]
    C -- Success --> D["Render Page"]
    C -- Error --> E["Render Error Page"]
    B -- No --> F["Render 404"]
```
Sources: [packages/preview/src/pages/[...slugs].tsx:29-46,140-194](https://github.com/blade47/fumadocs/blob/main/packages/preview/src/pages/[...slugs].tsx#L29-L46,L140-L194)

## AI Search Integration
The Preview Environment includes an integrated AI chat interface, `AISearch`, which allows users to query documentation interactively. This component uses the `@ai-sdk/react` library to stream responses from an AI model.

The chat mechanism relies on a tool-calling pattern where the AI can invoke a `search` tool. This tool queries a local Flexsearch index built at runtime from the documentation source files. The system guards against excessive usage via a simple IP-based rate limiter located in the API endpoint.

> [!IMPORTANT]
> The rate limiter identifies clients by their IP address derived from headers (`x-forwarded-for` or `cf-connecting-ip`). If a bucket reaches `rateLimitMaxRequests` (20), the system returns a `429` status code and a `Retry-After` header.

```mermaid
sequenceDiagram
    participant User
    participant Frontend
    participant ChatAPI
    participant SearchTool

    User->>Frontend: Send Query
    Frontend->>ChatAPI: POST /api/chat
    ChatAPI->>SearchTool: Invoke search tool
    SearchTool->>ChatAPI: Returns search results
    ChatAPI-->>Frontend: Stream AI response
```
Sources: [packages/preview/src/components/ai/search.tsx:292-304,331-388](https://github.com/blade47/fumadocs/blob/main/packages/preview/src/components/ai/search.tsx#L292-L304,L331-L388), [packages/preview/src/pages/_api/api/chat.ts:55-156](https://github.com/blade47/fumadocs/blob/main/packages/preview/src/pages/_api/api/chat.ts#L55-L156)

## Development Hot Reloading
To facilitate immediate feedback, the system monitors the filesystem for changes. The `initHotReload` function in the server entry point establishes a WebSocket connection with the client-side `HotReload` component.

When the file watcher detects a change, it performs two critical steps:
1. It deletes the specific file from the `filesCache`.
2. It calls `getSource.revalidate(false)` to force a refresh of the content source, ensuring that subsequent requests pull the modified data.

The client-side component then receives a `revalidate` event and triggers a `router.reload()`. This creates a seamless development loop for content authors.

Sources: [packages/preview/src/waku.server.tsx:56-92](https://github.com/blade47/fumadocs/blob/main/packages/preview/src/waku.server.tsx#L56-L92), [packages/preview/src/components/hot-reload.tsx:6-26](https://github.com/blade47/fumadocs/blob/main/packages/preview/src/components/hot-reload.tsx#L6-L26)

## API Documentation and Schemas
For API-driven documentation, the system leverages `ServerProvider` and `Operation` contexts. These components maintain the state of API servers and request schemas. The schema UI uses a path-tracking system (`PathItemType[]`) to allow users to navigate nested objects within OpenAPI definitions via a popover.

| Component | Responsibility |
| :--- | :--- |
| `ServerProvider` | Manages active API server URL and variables. |
| `OperationProvider` | Handles state for example requests and update listeners. |
| `SchemaUI` | Manages nested object navigation and search. |

Sources: [packages/asyncapi/src/ui/contexts/api.tsx:47-124](https://github.com/blade47/fumadocs/blob/main/packages/asyncapi/src/ui/contexts/api.tsx#L47-L124), [packages/openapi/src/ui/operation/context.tsx:20-82](https://github.com/blade47/fumadocs/blob/main/packages/openapi/src/ui/operation/context.tsx#L20-L82), [packages/api-docs/src/components/schema/client.tsx:83-185](https://github.com/blade47/fumadocs/blob/main/packages/api-docs/src/components/schema/client.tsx#L83-L185)

## Configuration and Runtime Initialization
The application configuration is parsed dynamically. The system provides a centralized `getConfigRuntime` function which resolves the config path, loads it, and returns a validated `ParsedAppConfig`. This is then injected throughout the application via the source loader, ensuring that project-specific directories (where Markdown files are found) are correctly identified during startup.

```typescript
// Initializing the configuration for the preview app
const configPath = await findConfigPath();
const config = await loadConfig(configPath);
```
Sources: [packages/preview/src/config/load-runtime.ts:26-31](https://github.com/blade47/fumadocs/blob/main/packages/preview/src/config/load-runtime.ts#L26-L31)

## Asset Handling
The system exposes a custom image proxy API (`/api/img`) to resolve image assets from projects that may exist outside the public directory. It checks for assets in the page's relative directory, configured asset directories, or the project root. This ensures documentation images remain linked correctly even in complex file structures.

> [!NOTE]
> The file lookup logic follows a strict order: relative page directory → project-configured assets directories → root public directory. If a file is not found, the proxy falls back to a `404`.

Sources: [packages/preview/src/pages/_api/img.ts:19-66](https://github.com/blade47/fumadocs/blob/main/packages/preview/src/pages/_api/img.ts#L19-L66)

## Related

- [Local Markdown Dev](https://www.doc0.dev/docs/e8872a72-1909-479c-b585-dc74c6e26726/technical/developer-tools/local-markdown-dev)


## Sitemap

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