How Markdown Rendering Works in the Pi Web UI: Architecture, Plugins, and Custom Components
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). 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 rendercwd– The current working directory for resolving relative file pathsonOpenFile– A callback handler that triggers the internal file viewer when users click local file links
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).
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 metadataremarkGfm– Enables GitHub-flavored markdown (tables, strikethrough, task lists)remarkMath– Identifies LaTeX math blocks for processingrehypeRaw– Allows raw HTML within markdownrehypeSanitize– Applies a strict security schema to prevent XSS attacksrehypeKatex– 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) 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.
```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
MarkdownBodyincomponents/MarkdownBody.tsxserves as the central rendering component for all markdown content- Content preprocessing occurs via
normalizeDisplayMathinlib/markdown.tsto prepare LaTeX expressions - Plugin pipeline includes
remarkGfm,remarkMath,rehypeSanitize, andrehypeKatexfor extended functionality - Custom renderers handle code blocks, lazy-loaded Mermaid diagrams, local file navigation, and responsive tables
resolveLocalFileHrefinlib/file-links.tsenables 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 and 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. 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 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.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →