# How Markdown Rendering Works in the Pi Web UI: Architecture, Plugins, and Custom Components

> Discover how the Pi Web UI renders markdown using react-markdown plugins for math, GitHub flavor, code highlighting, Mermaid diagrams, and local links. Understand its architecture.

- Repository: [Alex Yang/pi-web](https://github.com/agegr/pi-web)
- Tags: architecture
- Published: 2026-08-17

---

**The Pi Web UI handles markdown rendering through a central `MarkdownBody` React component that processes content with `react-markdown`, applies a pipeline of remark/rehype plugins for math and GitHub-flavored markdown, and overrides default renderers to support syntax-highlighted code, interactive Mermaid diagrams, and local file links.**

The `agegr/pi-web` repository implements a sophisticated markdown rendering pipeline designed specifically for interactive coding environments. This article examines how the UI transforms raw markdown strings into rich, secure, and interactive content using a plugin-based architecture with custom React components.

## The MarkdownBody Component Entry Point

At the heart of the system lies **`MarkdownBody`**, located in [[`components/MarkdownBody.tsx`](https://github.com/agegr/pi-web/blob/main/components/MarkdownBody.tsx)](https://github.com/agegr/pi-web/blob/main/components/MarkdownBody.tsx). This component serves as the primary interface for rendering markdown content throughout the application interface.

The component accepts several key props:
- `children` – The raw markdown string to render
- `cwd` – The current working directory for resolving relative file paths
- `onOpenFile` – A callback handler that triggers the internal file viewer when users click local file links

```tsx
import { MarkdownBody } from "@/components/MarkdownBody";

function ChatMessage({ text }: { text: string }) {
  return (
    <div className="chat-message">
      <MarkdownBody
        children={text}
        cwd={"/home/user/project"}
        onOpenFile={(path) => console.log(path)}
      />
    </div>
  );
}

```

## Preprocessing and Plugin Pipeline

Before rendering occurs, the component prepares content through a series of transformations defined in [[`lib/markdown.ts`](https://github.com/agegr/pi-web/blob/main/lib/markdown.ts)](https://github.com/agegr/pi-web/blob/main/lib/markdown.ts).

### Normalizing Mathematical Expressions

The pipeline begins with **`normalizeDisplayMath`**, a utility function that fixes LaTeX delimiters and prepares inline math syntax for subsequent processing. This normalization ensures compatibility with the KaTeX rendering engine used later in the transformation chain.

### Remark and Rehype Plugin Architecture

The component configures `react-markdown` with a comprehensive plugin suite that extends standard markdown capabilities:

- **`remarkFrontmatter`** – Parses YAML frontmatter metadata
- **`remarkGfm`** – Enables GitHub-flavored markdown (tables, strikethrough, task lists)
- **`remarkMath`** – Identifies LaTeX math blocks for processing
- **`rehypeRaw`** – Allows raw HTML within markdown
- **`rehypeSanitize`** – Applies a strict security schema to prevent XSS attacks
- **`rehypeKatex`** – Renders mathematical expressions using KaTeX

## Custom Component Renderers

The `components` prop of `react-markdown` is overridden with a **memoized object** mapping element types to custom React components. This mapping ensures stateful blocks like Mermaid diagrams remain mounted across streaming updates, preserving interactive state during real-time content generation.

### Syntax-Highlighted Code Blocks

Standard code fences trigger a custom **`CodeBlock`** component, which utilizes `react-syntax-highlighter` for language-specific coloring. This implementation provides consistent syntax highlighting across all supported programming languages.

### Interactive Mermaid Diagrams

When the language identifier equals `"mermaid"`, the system renders **`MermaidBlock`** from [[`components/MermaidBlock.tsx`](https://github.com/agegr/pi-web/blob/main/components/MermaidBlock.tsx)](https://github.com/agegr/pi-web/blob/main/components/MermaidBlock.tsx) instead of the standard code block. This component lazy-loads the `mermaid` library only when a user clicks the **"Preview"** button. After rendering the SVG diagram, it provides zoom functionality for detailed inspection.

```markdown

```mermaid
graph LR
  A --> B
  B --> C

```

```

### Local File Links and Image Handling

Links and images undergo special handling for local filesystem references. The **`resolveLocalFileHref`** utility in [[`lib/file-links.ts`](https://github.com/agegr/pi-web/blob/main/lib/file-links.ts)](https://github.com/agegr/pi-web/blob/main/lib/file-links.ts) converts relative paths to absolute locations using the `cwd` prop, while [[`lib/file-paths.ts`](https://github.com/agegr/pi-web/blob/main/lib/file-paths.ts)](https://github.com/agegr/pi-web/blob/main/lib/file-paths.ts) handles path encoding for API calls.

Local links trigger the `onOpenFile` callback to open files within the UI rather than navigating externally:

```tsx
const filePath = resolveLocalFileHref(href, cwd);
if (filePath && onOpenFile) {
  openFile(filePath);
}

```

Images map to the **`/api/files`** endpoint for secure serving, ensuring local file access remains controlled through the application's permission system.

### Responsive Table Containers

Markdown tables automatically wrap in a `<div className="markdown-table-wrap">` container, enabling horizontal scrolling and responsive styling across device sizes. The final markup resides within a `<div className="markdown-body …">` element, ensuring global CSS for markdown styling applies consistently.

## Security and Performance Optimizations

The implementation prioritizes both safety and rendering efficiency. The **`rehypeSanitize`** plugin enforces a strict HTML schema to neutralize malicious content before it reaches the DOM.

Meanwhile, the **memoization** of the custom `components` object prevents unnecessary remounts of interactive elements during real-time streaming updates. This architectural choice preserves state in complex diagrams and code blocks even as new content streams into the view.

## Summary

- **`MarkdownBody`** in [`components/MarkdownBody.tsx`](https://github.com/agegr/pi-web/blob/main/components/MarkdownBody.tsx) serves as the central rendering component for all markdown content
- **Content preprocessing** occurs via `normalizeDisplayMath` in [`lib/markdown.ts`](https://github.com/agegr/pi-web/blob/main/lib/markdown.ts) to prepare LaTeX expressions
- **Plugin pipeline** includes `remarkGfm`, `remarkMath`, `rehypeSanitize`, and `rehypeKatex` for extended functionality
- **Custom renderers** handle code blocks, lazy-loaded Mermaid diagrams, local file navigation, and responsive tables
- **`resolveLocalFileHref`** in [`lib/file-links.ts`](https://github.com/agegr/pi-web/blob/main/lib/file-links.ts) enables internal file resolution against the current working directory
- **Component memoization** maintains interactive state across streaming markdown updates

## Frequently Asked Questions

### What library does Pi Web use for markdown rendering?

Pi Web uses the `react-markdown` library as its core rendering engine, extended with custom plugins and component overrides defined in [`components/MarkdownBody.tsx`](https://github.com/agegr/pi-web/blob/main/components/MarkdownBody.tsx) and [`lib/markdown.ts`](https://github.com/agegr/pi-web/blob/main/lib/markdown.ts). This combination provides a React-native solution that supports server-side rendering while allowing deep customization of HTML output.

### How does Pi Web handle LaTeX math in markdown content?

The system processes mathematical expressions through the `normalizeDisplayMath` function for delimiter normalization, then applies the `remarkMath` plugin to identify math blocks and `rehypeKatex` to render equations using the KaTeX library. This pipeline supports both inline and display math modes with proper escaping and security sanitization.

### Can Pi Web render Mermaid diagrams from markdown code blocks?

Yes. When the renderer encounters a code block with the language identifier `mermaid`, it substitutes the default code component with `MermaidBlock` from [`components/MermaidBlock.tsx`](https://github.com/agegr/pi-web/blob/main/components/MermaidBlock.tsx). This component implements lazy-loading to avoid bundling the Mermaid library unnecessarily, and provides interactive features including a preview button and zoom controls for generated diagrams.

### How are local file links resolved in the markdown renderer?

The `resolveLocalFileHref` utility in [`lib/file-links.ts`](https://github.com/agegr/pi-web/blob/main/lib/file-links.ts) resolves relative paths against the provided `cwd` (current working directory) prop. Valid local links trigger the `onOpenFile` callback to open files within the application's file viewer rather than navigating to external URLs, while images map to the `/api/files` endpoint for secure serving through the file API.