> ## Documentation Index
> Fetch the complete documentation index at: https://docs.chucky.cloud/llms.txt
> Use this file to discover all available pages before exploring further.

# Stream responses

> Real-time streaming responses from Claude

# Stream responses

Streaming lets you receive Claude's response in real-time as it's generated, providing a better user experience for longer responses.

## Basic Streaming

### Prompt Streaming

```typescript theme={null}
for await (const event of client.promptStream({
  message: 'Write a short story about a robot',
  model: 'claude-sonnet-4-5-20250929',
})) {
  if (event.type === 'text') {
    process.stdout.write(event.text);
  }
}
```

### Session Streaming

```typescript theme={null}
const session = await client.createSession({ ... });

for await (const event of session.sendStream('Tell me about space exploration')) {
  if (event.type === 'text') {
    process.stdout.write(event.text);
  }
}
```

## Event Types

```typescript theme={null}
type StreamingEvent =
  | { type: 'text'; text: string }
  | { type: 'tool_use'; id: string; name: string; input: Record<string, unknown> }
  | { type: 'tool_result'; id: string; content: unknown; isError?: boolean }
  | { type: 'thinking'; thinking: string }
  | { type: 'error'; error: Error };
```

### Text Events

Text content as it's generated:

```typescript theme={null}
for await (const event of stream) {
  if (event.type === 'text') {
    // Append to display
    displayElement.textContent += event.text;
  }
}
```

### Tool Use Events

When Claude decides to call a tool:

```typescript theme={null}
for await (const event of stream) {
  if (event.type === 'tool_use') {
    console.log(`Calling tool: ${event.name}`);
    console.log(`Input:`, event.input);
    // Tool execution happens automatically
  }
}
```

### Tool Result Events

After a tool finishes executing:

```typescript theme={null}
for await (const event of stream) {
  if (event.type === 'tool_result') {
    console.log(`Tool ${event.id} returned:`, event.content);
    if (event.isError) {
      console.error('Tool execution failed');
    }
  }
}
```

### Thinking Events

Extended thinking (for supported models):

```typescript theme={null}
for await (const event of stream) {
  if (event.type === 'thinking') {
    console.log('[Claude is thinking...]', event.thinking);
  }
}
```

### Error Events

Errors during streaming:

```typescript theme={null}
for await (const event of stream) {
  if (event.type === 'error') {
    console.error('Stream error:', event.error);
    break;
  }
}
```

For low-level event details, see [WebSocket Events](/api/events).

## Complete Example

```typescript theme={null}
async function streamResponse(message: string) {
  let fullText = '';
  const toolCalls = [];

  try {
    for await (const event of client.promptStream({ message })) {
      switch (event.type) {
        case 'text':
          fullText += event.text;
          updateUI(fullText);
          break;

        case 'tool_use':
          showToolIndicator(event.name);
          toolCalls.push(event);
          break;

        case 'tool_result':
          hideToolIndicator();
          break;

        case 'thinking':
          showThinkingIndicator(event.thinking);
          break;

        case 'error':
          showError(event.error.message);
          return;
      }
    }

    console.log('Stream complete. Final text length:', fullText.length);
    console.log('Tool calls made:', toolCalls.length);
  } catch (err) {
    console.error('Stream failed:', err);
  }
}
```

## Python Streaming

```python theme={null}
async for event in client.stream('Write a poem about coding'):
    if event.type == 'message':
        print(event.data, end='', flush=True)
    elif event.type == 'tool_use':
        print(f'\n[Calling tool: {event.name}]')
    elif event.type == 'error':
        print(f'\nError: {event.error}')
        break
```

## Browser Usage

Display streaming text in a browser:

```typescript theme={null}
async function streamToElement(element: HTMLElement, message: string) {
  element.textContent = '';

  for await (const event of client.promptStream({ message })) {
    if (event.type === 'text') {
      element.textContent += event.text;
      // Auto-scroll to bottom
      element.scrollTop = element.scrollHeight;
    }
  }
}
```

## With React

```tsx theme={null}
function StreamingResponse({ message }: { message: string }) {
  const [text, setText] = useState('');
  const [isStreaming, setIsStreaming] = useState(true);

  useEffect(() => {
    let cancelled = false;

    async function stream() {
      setIsStreaming(true);
      setText('');

      try {
        for await (const event of client.promptStream({ message })) {
          if (cancelled) break;
          if (event.type === 'text') {
            setText(prev => prev + event.text);
          }
        }
      } finally {
        setIsStreaming(false);
      }
    }

    stream();

    return () => { cancelled = true; };
  }, [message]);

  return (
    <div>
      <p>{text}</p>
      {isStreaming && <span>Streaming...</span>}
    </div>
  );
}
```

## Buffering Strategies

### Character-by-character

```typescript theme={null}
for await (const event of stream) {
  if (event.type === 'text') {
    for (const char of event.text) {
      appendChar(char);
      await delay(10); // Typewriter effect
    }
  }
}
```

### Word-by-word

```typescript theme={null}
let buffer = '';
for await (const event of stream) {
  if (event.type === 'text') {
    buffer += event.text;
    const words = buffer.split(/(\s+)/);
    // Keep incomplete word in buffer
    buffer = words.pop() || '';
    for (const word of words) {
      displayWord(word);
    }
  }
}
// Display remaining buffer
if (buffer) displayWord(buffer);
```

### Sentence-by-sentence

```typescript theme={null}
let buffer = '';
for await (const event of stream) {
  if (event.type === 'text') {
    buffer += event.text;
    const sentences = buffer.split(/(?<=[.!?])\s+/);
    // Keep incomplete sentence in buffer
    buffer = sentences.pop() || '';
    for (const sentence of sentences) {
      displaySentence(sentence);
    }
  }
}
if (buffer) displaySentence(buffer);
```

## Performance Tips

<AccordionGroup>
  <Accordion title="Batch DOM updates">
    Instead of updating the DOM on every text event, batch updates:

    ```typescript theme={null}
    let pending = '';
    let rafId: number;

    for await (const event of stream) {
      if (event.type === 'text') {
        pending += event.text;
        if (!rafId) {
          rafId = requestAnimationFrame(() => {
            element.textContent += pending;
            pending = '';
            rafId = 0;
          });
        }
      }
    }
    ```
  </Accordion>

  <Accordion title="Use text fragments">
    For better performance with large amounts of text:

    ```typescript theme={null}
    const fragment = document.createDocumentFragment();

    for await (const event of stream) {
      if (event.type === 'text') {
        const span = document.createElement('span');
        span.textContent = event.text;
        fragment.appendChild(span);
      }
    }

    container.appendChild(fragment);
    ```
  </Accordion>

  <Accordion title="Cancel streams">
    If the user navigates away, cancel the stream:

    ```typescript theme={null}
    const controller = new AbortController();

    try {
      for await (const event of stream) {
        if (controller.signal.aborted) break;
        // Handle event
      }
    } catch (err) {
      if (err.name !== 'AbortError') throw err;
    }

    // Cancel when needed
    controller.abort();
    ```
  </Accordion>
</AccordionGroup>
