mirror of
https://github.com/vercel/streamdown.git
synced 2026-09-19 02:18:37 +08:00
e30219d2db
Cover defaults, disable values, and streaming auto-scroll pin behavior in configuration, code blocks, and GFM docs.
383 lines
9.6 KiB
Plaintext
383 lines
9.6 KiB
Plaintext
---
|
|
title: Code Blocks
|
|
description: Beautiful syntax highlighting and interactive code blocks powered by Shiki.
|
|
type: reference
|
|
summary: Shiki-powered syntax highlighting with line numbers, copy buttons, and language detection.
|
|
prerequisites:
|
|
- /docs/getting-started
|
|
related:
|
|
- /docs/interactivity
|
|
- /docs/plugins
|
|
---
|
|
|
|
Streamdown provides beautiful, interactive code blocks with syntax highlighting powered by [Shiki](https://shiki.style/). Every code block includes a copy button and supports a wide range of programming languages.
|
|
|
|
## Basic Usage
|
|
|
|
Create code blocks using triple backticks with an optional language identifier:
|
|
|
|
````markdown
|
|
```javascript
|
|
function greet(name) {
|
|
return `Hello, ${name}!`;
|
|
}
|
|
```
|
|
````
|
|
|
|
Streamdown will automatically apply syntax highlighting based on the specified language.
|
|
|
|
## Enabling Syntax Highlighting
|
|
|
|
Syntax highlighting requires the code plugin. Install it:
|
|
|
|
```package-install
|
|
npm install @streamdown/code
|
|
```
|
|
|
|
Then import and pass the plugin to Streamdown:
|
|
|
|
```tsx title="app/page.tsx"
|
|
import { Streamdown } from "streamdown";
|
|
import { code } from "@streamdown/code";
|
|
|
|
export default function Page() {
|
|
return (
|
|
<Streamdown plugins={{ code: code }}>
|
|
{markdown}
|
|
</Streamdown>
|
|
);
|
|
}
|
|
```
|
|
|
|
Without the code plugin, code blocks render as plain text with no highlighting.
|
|
|
|
## Supported Languages
|
|
|
|
Streamdown supports 200+ programming languages through Shiki. All languages are lazy-loaded on demand, so only the grammars you use are downloaded.
|
|
|
|
### Common Languages
|
|
|
|
- **Web**: JavaScript, TypeScript, JSX, TSX, HTML, CSS
|
|
- **Data**: JSON, YAML, TOML
|
|
- **Shell**: Bash, Shell Script, PowerShell
|
|
- **Backend**: Python, Go, Java, Rust, C, C++, C#, PHP, Ruby
|
|
- **Functional**: Haskell, Elixir, Clojure, F#, OCaml
|
|
- **Markup**: Markdown, LaTeX, MDX, XML
|
|
- **And 180+ more languages**
|
|
|
|
### Language Examples
|
|
|
|
#### TypeScript
|
|
|
|
````markdown
|
|
```typescript
|
|
interface User {
|
|
id: number;
|
|
name: string;
|
|
email: string;
|
|
}
|
|
|
|
async function fetchUser(id: number): Promise<User> {
|
|
const response = await fetch(`/api/users/${id}`);
|
|
return response.json();
|
|
}
|
|
```
|
|
````
|
|
|
|
#### Python
|
|
|
|
````markdown
|
|
```python
|
|
def fibonacci(n: int) -> list[int]:
|
|
"""Generate Fibonacci sequence up to n terms."""
|
|
fib = [0, 1]
|
|
for i in range(2, n):
|
|
fib.append(fib[i-1] + fib[i-2])
|
|
return fib
|
|
|
|
print(fibonacci(10))
|
|
```
|
|
````
|
|
|
|
#### Rust
|
|
|
|
````markdown
|
|
```rust
|
|
fn main() {
|
|
let numbers = vec![1, 2, 3, 4, 5];
|
|
let sum: i32 = numbers.iter().sum();
|
|
println!("Sum: {}", sum);
|
|
}
|
|
```
|
|
````
|
|
|
|
## Theme Configuration
|
|
|
|
Streamdown uses dual themes for light and dark modes. You can customize the themes using the `shikiTheme` prop:
|
|
|
|
```tsx title="app/page.tsx"
|
|
import { Streamdown } from "streamdown";
|
|
import { code } from "@streamdown/code";
|
|
|
|
export default function Page() {
|
|
return (
|
|
<Streamdown
|
|
plugins={{ code: code }}
|
|
shikiTheme={["dracula", "dracula"]}
|
|
>
|
|
{markdown}
|
|
</Streamdown>
|
|
);
|
|
}
|
|
```
|
|
|
|
### Available Themes
|
|
|
|
Streamdown supports all Shiki themes including:
|
|
|
|
- `github-light` (default light theme)
|
|
- `github-dark` (default dark theme)
|
|
- `dracula`, `nord`, `one-dark-pro`, `monokai`
|
|
- `catppuccin-latte`, `catppuccin-mocha`
|
|
- `vitesse-light`, `vitesse-dark`
|
|
- `tokyo-night`, `slack-dark`, `slack-ochin`
|
|
- And [many more](https://shiki.style/themes)
|
|
|
|
### Custom theme objects
|
|
|
|
The `shikiTheme` prop accepts `[ThemeInput, ThemeInput]` where `ThemeInput` is either a bundled theme name (`BundledTheme`) or a custom theme object (`ThemeRegistrationAny`). You can mix and match:
|
|
|
|
```tsx title="app/page.tsx"
|
|
import { Streamdown } from "streamdown";
|
|
import { code } from "@streamdown/code";
|
|
import myCustomDarkTheme from "./my-dark-theme.json";
|
|
|
|
export default function Page() {
|
|
return (
|
|
<Streamdown
|
|
plugins={{ code: code }}
|
|
shikiTheme={["github-light", myCustomDarkTheme]}
|
|
>
|
|
{markdown}
|
|
</Streamdown>
|
|
);
|
|
}
|
|
```
|
|
|
|
<Callout type="info">
|
|
Bundled theme names (strings) load from Shiki's built-in registry. Custom theme objects follow the `ThemeRegistrationAny` format from Shiki — any VS Code `.tmTheme` or JSON theme file works.
|
|
</Callout>
|
|
|
|
## Line Numbers
|
|
|
|
Line numbers are shown by default on all code blocks.
|
|
|
|
### Disable globally
|
|
|
|
Turn off line numbers for every code block with the `lineNumbers` prop:
|
|
|
|
```tsx title="app/page.tsx"
|
|
<Streamdown lineNumbers={false}>{markdown}</Streamdown>
|
|
```
|
|
|
|
### Disable per block
|
|
|
|
Add `noLineNumbers` to the code fence meta to hide line numbers on a single block:
|
|
|
|
````markdown
|
|
```typescript noLineNumbers
|
|
const user = await getUser(id);
|
|
const profile = await getProfile(user);
|
|
```
|
|
````
|
|
|
|
When `lineNumbers` is set to `false` globally, all blocks hide line numbers regardless of the meta string.
|
|
|
|
### Custom start line
|
|
|
|
Set the starting line number for a code block using `startLine=N` in the code fence meta:
|
|
|
|
````markdown
|
|
```typescript startLine=10
|
|
const user = await getUser(id);
|
|
const profile = await getProfile(user);
|
|
```
|
|
````
|
|
|
|
Line numbers begin at the value you specify instead of 1. The value must be a positive integer (>= 1).
|
|
|
|
## Interactive Features
|
|
|
|
### Copy Button
|
|
|
|
Every code block includes a copy button that appears on hover. Users can click to copy the entire code block content to their clipboard.
|
|
|
|
The copy button:
|
|
|
|
- Appears on hover (desktop) or is always visible (mobile)
|
|
- Provides visual feedback on successful copy
|
|
- Is automatically disabled during streaming (when `isAnimating={true}`)
|
|
|
|
### Disable Controls
|
|
|
|
Disable individual code block buttons using the `controls` prop:
|
|
|
|
```tsx title="app/page.tsx"
|
|
// Hide the download button, keep copy
|
|
<Streamdown controls={{ code: { download: false } }}>{markdown}</Streamdown>
|
|
|
|
// Hide the copy button, keep download
|
|
<Streamdown controls={{ code: { copy: false } }}>{markdown}</Streamdown>
|
|
|
|
// Hide all code block controls
|
|
<Streamdown controls={{ code: false }}>{markdown}</Streamdown>
|
|
|
|
// Hide all controls across all block types
|
|
<Streamdown controls={false}>{markdown}</Streamdown>
|
|
```
|
|
|
|
## Inline Code
|
|
|
|
Inline code uses backticks and receives subtle styling:
|
|
|
|
```markdown
|
|
Use the `useState` hook to manage state in React.
|
|
```
|
|
|
|
Inline code is styled with:
|
|
|
|
- Monospace font family
|
|
- Subtle background color
|
|
- Rounded corners
|
|
- Appropriate padding
|
|
|
|
## Code Block Styling
|
|
|
|
Code blocks include:
|
|
|
|
- **Line Numbers** - Optional line numbers for reference
|
|
- **Rounded Corners** - Modern, polished appearance
|
|
- **Proper Padding** - Comfortable spacing
|
|
- **Scrolling** - Horizontal scroll for long lines, vertical scroll when max height is set
|
|
- **Responsive Design** - Adapts to container width
|
|
|
|
## Max Height
|
|
|
|
Long code blocks are capped at `400px` by default and scroll vertically. Customize (or disable) the limit with `codeBlockMaxHeight`:
|
|
|
|
```tsx title="app/page.tsx"
|
|
// Custom pixel height
|
|
<Streamdown codeBlockMaxHeight={240}>{markdown}</Streamdown>
|
|
|
|
// CSS length value
|
|
<Streamdown codeBlockMaxHeight="50vh">{markdown}</Streamdown>
|
|
|
|
// Disable the height constraint
|
|
<Streamdown codeBlockMaxHeight={0}>{markdown}</Streamdown>
|
|
// or
|
|
<Streamdown codeBlockMaxHeight={Infinity}>{markdown}</Streamdown>
|
|
```
|
|
|
|
| Value | Behavior |
|
|
| --- | --- |
|
|
| `number` | Treated as pixels (`400` → `400px`) |
|
|
| `string` | Passed through as a CSS value (`"50vh"`, `"20rem"`) |
|
|
| `0` / `Infinity` / `"none"` | No max height |
|
|
|
|
### Auto-scroll while streaming
|
|
|
|
When `isAnimating` is true, code blocks stay pinned to the bottom as new lines arrive. If the user scrolls up, auto-scroll unpins until they return to the bottom (or a new streaming session starts).
|
|
|
|
```tsx title="app/page.tsx"
|
|
<Streamdown codeBlockMaxHeight={320} isAnimating={isStreaming}>
|
|
{markdown}
|
|
</Streamdown>
|
|
```
|
|
|
|
## Streaming Considerations
|
|
|
|
Code blocks work seamlessly with streaming content:
|
|
|
|
### Incomplete Code Blocks
|
|
|
|
When a code block is streaming in, Streamdown handles the incomplete state gracefully:
|
|
|
|
````markdown
|
|
```javascript
|
|
function example() {
|
|
// Streaming in progress...
|
|
```
|
|
````
|
|
|
|
The unterminated block parser ensures the code block renders properly even without the closing backticks.
|
|
|
|
### Loading Behavior
|
|
|
|
Code block shells render immediately with plain text content, then syntax colors are applied when highlighting resolves.
|
|
|
|
This keeps code readable on first paint and improves visual stability during lazy highlight loading.
|
|
|
|
### Disabling Interactions During Streaming
|
|
|
|
Use the `isAnimating` prop to disable copy buttons while streaming:
|
|
|
|
```tsx title="app/page.tsx"
|
|
<Streamdown isAnimating={isStreaming}>{markdown}</Streamdown>
|
|
```
|
|
|
|
This prevents users from copying incomplete code.
|
|
|
|
## Plugin Interface
|
|
|
|
The Code plugin implements the `CodeHighlighterPlugin` interface:
|
|
|
|
```tsx
|
|
interface CodeHighlighterPlugin {
|
|
name: "shiki";
|
|
type: "code-highlighter";
|
|
highlight: (options: HighlightOptions, callback?: (result: HighlightResult) => void) => HighlightResult | null;
|
|
supportsLanguage: (language: BundledLanguage) => boolean;
|
|
getSupportedLanguages: () => BundledLanguage[];
|
|
getThemes: () => [BundledTheme, BundledTheme];
|
|
}
|
|
```
|
|
|
|
### Exported Types
|
|
|
|
```tsx
|
|
import type {
|
|
CodeHighlighterPlugin,
|
|
HighlightOptions,
|
|
HighlightResult,
|
|
} from '@streamdown/code';
|
|
|
|
// HighlightOptions - parameters for highlighting
|
|
interface HighlightOptions {
|
|
code: string;
|
|
language: BundledLanguage;
|
|
themes: [string, string];
|
|
}
|
|
|
|
// HighlightResult - Shiki's TokensResult type
|
|
type HighlightResult = TokensResult;
|
|
```
|
|
|
|
### Programmatic Highlighting
|
|
|
|
Use the plugin directly for custom highlighting:
|
|
|
|
```tsx
|
|
import { code } from '@streamdown/code';
|
|
|
|
// Check language support
|
|
if (code.supportsLanguage('typescript')) {
|
|
code.highlight(
|
|
{ code: 'const x = 1;', language: 'typescript', themes: ['github-light', 'github-dark'] },
|
|
(result) => {
|
|
// Handle highlighted tokens
|
|
console.log(result.tokens);
|
|
}
|
|
);
|
|
}
|
|
```
|