---
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
---

# Code Blocks



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:

<CodeBlockTabs defaultValue="npm">
  <CodeBlockTabsList>
    <CodeBlockTabsTrigger value="npm">
      npm
    </CodeBlockTabsTrigger>

    <CodeBlockTabsTrigger value="pnpm">
      pnpm
    </CodeBlockTabsTrigger>

    <CodeBlockTabsTrigger value="yarn">
      yarn
    </CodeBlockTabsTrigger>

    <CodeBlockTabsTrigger value="bun">
      bun
    </CodeBlockTabsTrigger>
  </CodeBlockTabsList>

  <CodeBlockTab value="npm">
    ```bash
    npm install @streamdown/code
    ```
  </CodeBlockTab>

  <CodeBlockTab value="pnpm">
    ```bash
    pnpm add @streamdown/code
    ```
  </CodeBlockTab>

  <CodeBlockTab value="yarn">
    ```bash
    yarn add @streamdown/code
    ```
  </CodeBlockTab>

  <CodeBlockTab value="bun">
    ```bash
    bun add @streamdown/code
    ```
  </CodeBlockTab>
</CodeBlockTabs>

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
* **Responsive Design** - Adapts to container width

## 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);
    }
  );
}
```


---

For a semantic overview of all documentation, see [/sitemap.md](/sitemap.md)

For an index of all available documentation, see [/llms.txt](/llms.txt)

For agent-facing discovery, including API and MCP surfaces, see [/agents.md](/agents.md)