e0a5f57793
Vendor the rebranded BoosAPI Desktop client (Tauri + Vue) into metapi as a monorepo subdirectory under desktop/. Source only — node_modules, dist, Cargo target and the upstream .git history are excluded. desktop/ keeps its own independent Cargo workspace (desktop/src-tauri with crates/*); it is not part of the backend boosapi crate, so a `cargo check` at the metapi root still compiles only the backend. Build the desktop app from desktop/src-tauri per desktop/README.md. Note: app icons are still placeholder assets pending the final BoosAPI logo. The updater signing keys live outside the repo and are not included.
453 lines
9.1 KiB
Markdown
453 lines
9.1 KiB
Markdown
# React Quick Start
|
|
|
|
Get started with markstream-react in your React project.
|
|
|
|
::: tip Migrating from react-markdown?
|
|
If your app already uses `react-markdown`, start with the [migration guide](/guide/react-markdown-migration) and the [migration cookbook](/guide/react-markdown-migration-cookbook) before wiring new code by hand.
|
|
:::
|
|
|
|
## Basic Setup
|
|
|
|
### 1. Installation
|
|
|
|
First, install the package:
|
|
|
|
```bash
|
|
pnpm add markstream-react
|
|
```
|
|
|
|
### 2. Import Styles
|
|
|
|
In your main entry file (e.g., `main.tsx`, `index.tsx`, or `App.tsx`):
|
|
|
|
```tsx
|
|
import React from 'react'
|
|
import ReactDOM from 'react-dom/client'
|
|
import App from './App'
|
|
import 'markstream-react/index.css'
|
|
|
|
ReactDOM.createRoot(document.getElementById('root')!).render(
|
|
<React.StrictMode>
|
|
<App />
|
|
</React.StrictMode>
|
|
)
|
|
```
|
|
|
|
### 3. Use the Component
|
|
|
|
```tsx
|
|
import MarkdownRender from 'markstream-react'
|
|
|
|
function App() {
|
|
const markdown = `# Hello React!
|
|
|
|
This is **markstream-react** - a streaming-friendly Markdown renderer for React.
|
|
|
|
## Features
|
|
|
|
- Code syntax highlighting
|
|
- Mermaid diagrams
|
|
- Math formulas
|
|
- And much more!
|
|
|
|
\`\`\`javascript
|
|
console.log('Hello from React!')
|
|
\`\`\`
|
|
`
|
|
|
|
return (
|
|
<div>
|
|
<MarkdownRender content={markdown} />
|
|
</div>
|
|
)
|
|
}
|
|
|
|
export default App
|
|
```
|
|
|
|
## Using with TypeScript
|
|
|
|
markstream-react is built with TypeScript and includes full type definitions:
|
|
|
|
```tsx
|
|
import type { NodeRendererProps } from 'markstream-react'
|
|
import MarkdownRender from 'markstream-react'
|
|
|
|
const props: NodeRendererProps = {
|
|
content: '# Hello TypeScript!',
|
|
}
|
|
|
|
function App() {
|
|
return <MarkdownRender {...props} />
|
|
}
|
|
```
|
|
|
|
## Using with Next.js
|
|
|
|
Use the dedicated SSR entrypoints instead of `mounted` guards or `ssr: false`.
|
|
|
|
See [React Next SSR](/guide/react-next-ssr) for the full contract and examples.
|
|
|
|
### App Router (Next.js 14 / 15)
|
|
|
|
```tsx
|
|
import MarkdownRender from 'markstream-react/next'
|
|
|
|
export default function MarkdownPage() {
|
|
const markdown = `# Hello Next.js!
|
|
|
|
This route ships real SSR HTML first.
|
|
`
|
|
|
|
return <MarkdownRender content={markdown} final />
|
|
}
|
|
```
|
|
|
|
### Pages Router
|
|
|
|
```tsx
|
|
import MarkdownRender from 'markstream-react/next'
|
|
import 'markstream-react/index.css'
|
|
|
|
export default function MarkdownPage() {
|
|
const markdown = `# Hello Next.js Pages Router!`
|
|
|
|
return <MarkdownRender content={markdown} final />
|
|
}
|
|
```
|
|
|
|
### Pure Server Rendering
|
|
|
|
```tsx
|
|
import MarkdownRender from 'markstream-react/server'
|
|
|
|
export default function MarkdownPage() {
|
|
const markdown = `# Pure server entry`
|
|
|
|
return <MarkdownRender content={markdown} final />
|
|
}
|
|
```
|
|
|
|
## Enabling Optional Features
|
|
|
|
### Code Syntax Highlighting
|
|
|
|
Install dependencies:
|
|
|
|
```bash
|
|
pnpm add stream-markdown
|
|
```
|
|
|
|
```tsx
|
|
import MarkdownRender, { MarkdownCodeBlockNode, setCustomComponents } from 'markstream-react'
|
|
|
|
// Use Shiki-based code blocks inside MarkdownRender
|
|
setCustomComponents({
|
|
code_block: ({ node, isDark, ctx }: any) => (
|
|
<MarkdownCodeBlockNode
|
|
node={node}
|
|
isDark={isDark}
|
|
stream={ctx?.codeBlockStream}
|
|
{...(ctx?.codeBlockProps || {})}
|
|
/>
|
|
),
|
|
})
|
|
|
|
function App() {
|
|
const markdown = `\`\`\`javascript
|
|
const hello = 'world'
|
|
console.log(hello)
|
|
\`\`\``
|
|
|
|
return <MarkdownRender content={markdown} />
|
|
}
|
|
```
|
|
|
|
### Mermaid Diagrams
|
|
|
|
Install mermaid:
|
|
|
|
```bash
|
|
pnpm add mermaid
|
|
```
|
|
|
|
Import styles (Mermaid auto-loads when installed):
|
|
|
|
```tsx
|
|
import 'markstream-react/index.css'
|
|
|
|
function App() {
|
|
const markdown = `#### Mermaid Diagram
|
|
|
|
\`\`\`mermaid
|
|
graph TD
|
|
A[Start] --> B{Is it working?}
|
|
B -->|Yes| C[Great!]
|
|
B -->|No| D[Keep trying]
|
|
\`\`\``
|
|
|
|
return <MarkdownRender content={markdown} />
|
|
}
|
|
```
|
|
|
|
### D2 Diagrams
|
|
|
|
Install D2:
|
|
|
|
```bash
|
|
pnpm add @terrastruct/d2
|
|
```
|
|
|
|
```tsx
|
|
import 'markstream-react/index.css'
|
|
|
|
function App() {
|
|
const markdown = `#### D2 Diagram
|
|
|
|
\`\`\`d2
|
|
direction: right
|
|
Client -> API: request
|
|
API -> DB: query
|
|
DB -> API: rows
|
|
API -> Client: response
|
|
\`\`\``
|
|
|
|
return <MarkdownRender content={markdown} />
|
|
}
|
|
```
|
|
|
|
### Math Formulas (KaTeX)
|
|
|
|
Install katex:
|
|
|
|
```bash
|
|
pnpm add katex
|
|
```
|
|
|
|
Import styles (KaTeX auto-loads when installed):
|
|
|
|
```tsx
|
|
import 'markstream-react/index.css'
|
|
import 'katex/dist/katex.min.css'
|
|
|
|
function App() {
|
|
const markdown = `#### Math Example
|
|
|
|
Inline math: $E = mc^2$
|
|
|
|
Block math:
|
|
|
|
$$
|
|
\\int_0^\\infty e^{-x^2} dx = \\frac{\\sqrt{\\pi}}{2}
|
|
$$`
|
|
|
|
return <MarkdownRender content={markdown} />
|
|
}
|
|
```
|
|
|
|
## Custom Components
|
|
|
|
You can customize how specific nodes are rendered by passing custom component mappings:
|
|
|
|
```tsx
|
|
import MarkdownRender, { setCustomComponents } from 'markstream-react'
|
|
|
|
// Custom heading component
|
|
function CustomHeading({ node, customId }: any) {
|
|
const level = node.level || 1
|
|
const Tag = `h${level}` as keyof JSX.IntrinsicElements
|
|
|
|
return (
|
|
<Tag className="custom-heading" data-custom-id={customId}>
|
|
{node.children?.map((child: any, i: number) => (
|
|
<span key={i}>{child.content || ''}</span>
|
|
))}
|
|
</Tag>
|
|
)
|
|
}
|
|
|
|
// Register for a scoped customId
|
|
setCustomComponents('docs', { heading: CustomHeading })
|
|
|
|
function App() {
|
|
const markdown = `# Custom Heading
|
|
|
|
This heading is rendered with a custom component.
|
|
`
|
|
|
|
return <MarkdownRender customId="docs" content={markdown} />
|
|
}
|
|
```
|
|
|
|
## Streaming Content
|
|
|
|
markstream-react supports streaming markdown content, which is useful for AI-generated content:
|
|
|
|
```tsx
|
|
import MarkdownRender from 'markstream-react'
|
|
import { useState } from 'react'
|
|
|
|
function StreamingDemo() {
|
|
const [markdown, setMarkdown] = useState('')
|
|
const [isStreaming, setIsStreaming] = useState(false)
|
|
|
|
const fullText = `# Streaming Demo
|
|
|
|
This content is being streamed **in small chunks**.
|
|
|
|
## Features
|
|
|
|
1. Progressive rendering
|
|
2. No layout shift
|
|
3. Smooth animations
|
|
|
|
\`\`\`javascript
|
|
const streaming = true
|
|
console.log('Streaming enabled:', streaming)
|
|
\`\`\`
|
|
`
|
|
|
|
const startStreaming = () => {
|
|
setIsStreaming(true)
|
|
setMarkdown('')
|
|
let i = 0
|
|
|
|
const interval = setInterval(() => {
|
|
if (i < fullText.length) {
|
|
setMarkdown(prev => prev + fullText[i])
|
|
i++
|
|
}
|
|
else {
|
|
clearInterval(interval)
|
|
setIsStreaming(false)
|
|
}
|
|
}, 20)
|
|
|
|
return () => clearInterval(interval)
|
|
}
|
|
|
|
return (
|
|
<div>
|
|
<button onClick={startStreaming} disabled={isStreaming}>
|
|
{isStreaming ? 'Streaming...' : 'Start Streaming'}
|
|
</button>
|
|
<MarkdownRender content={markdown} />
|
|
</div>
|
|
)
|
|
}
|
|
```
|
|
|
|
## Using with React Hooks
|
|
|
|
```tsx
|
|
import type { ChangeEvent } from 'react'
|
|
import MarkdownRender from 'markstream-react'
|
|
import { useCallback, useEffect, useState } from 'react'
|
|
|
|
function MarkdownEditor() {
|
|
const [content, setContent] = useState('# Edit me!')
|
|
const [html, setHtml] = useState('')
|
|
|
|
const handleChange = useCallback((e: ChangeEvent<HTMLTextAreaElement>) => {
|
|
setContent(e.target.value)
|
|
}, [])
|
|
|
|
return (
|
|
<div className="markdown-editor">
|
|
<textarea
|
|
value={content}
|
|
onChange={handleChange}
|
|
placeholder="Write your markdown here..."
|
|
className="editor-input"
|
|
/>
|
|
<div className="editor-preview">
|
|
<MarkdownRender content={content} />
|
|
</div>
|
|
</div>
|
|
)
|
|
}
|
|
```
|
|
|
|
## Virtualized Lists
|
|
|
|
For large markdown documents, you can use virtualization:
|
|
|
|
```tsx
|
|
import MarkdownRender from 'markstream-react'
|
|
|
|
function LongDocument() {
|
|
// Your very long markdown content
|
|
const markdown = `# Long Document`
|
|
|
|
return (
|
|
<MarkdownRender
|
|
content={markdown}
|
|
maxLiveNodes={200}
|
|
liveNodeBuffer={10}
|
|
/>
|
|
)
|
|
}
|
|
```
|
|
|
|
## Common Props
|
|
|
|
Below are the most commonly used props. For the full list, see [React Components](/guide/react-components).
|
|
|
|
| Prop | Type | Default | Description |
|
|
|------|------|---------|-------------|
|
|
| `content` | `string` | - | Markdown content to render |
|
|
| `nodes` | `BaseNode[]` | - | Pre-parsed AST nodes (typically `ParsedNode[]`) |
|
|
| `customId` | `string` | - | Identifier for scoping |
|
|
| `maxLiveNodes` | `number` | `320` | Max nodes for virtualization |
|
|
| `liveNodeBuffer` | `number` | `60` | Buffer for overscan |
|
|
| `batchRendering` | `boolean` | `true` | Enable batched rendering |
|
|
| `deferNodesUntilVisible` | `boolean` | `true` | Defer heavy nodes |
|
|
| `renderCodeBlocksAsPre` | `boolean` | `false` | Use `<pre><code>` fallback (Mermaid blocks will also fall back) |
|
|
|
|
## Styling
|
|
|
|
The default styles are scoped under `.markstream-react` class. You can override styles:
|
|
|
|
```css
|
|
/* Your global styles */
|
|
.markstream-react {
|
|
font-family: system-ui, -apple-system, sans-serif;
|
|
line-height: 1.6;
|
|
}
|
|
|
|
.markstream-react h1 {
|
|
font-size: 2.5rem;
|
|
font-weight: 700;
|
|
border-bottom: 2px solid #e5e7eb;
|
|
padding-bottom: 0.5rem;
|
|
}
|
|
|
|
.markstream-react code {
|
|
background: #f3f4f6;
|
|
padding: 0.2rem 0.4rem;
|
|
border-radius: 0.25rem;
|
|
}
|
|
```
|
|
|
|
## Using with Tailwind CSS
|
|
|
|
```tsx
|
|
import MarkdownRender from 'markstream-react'
|
|
import 'markstream-react/index.css'
|
|
import './output.css' // Your Tailwind output
|
|
|
|
function App() {
|
|
return (
|
|
<div className="max-w-4xl mx-auto py-8">
|
|
<MarkdownRender content="# Hello Tailwind!" />
|
|
</div>
|
|
)
|
|
}
|
|
```
|
|
|
|
## Next Steps
|
|
|
|
- Explore [React Components documentation](/guide/react-components) for all available components
|
|
- Check out [Examples](/guide/examples) for more usage examples
|
|
- See [API Reference](/guide/components) for detailed API documentation
|