> ## Documentation Index
> Fetch the complete documentation index at: https://mintlify.com/honojs/hono/llms.txt
> Use this file to discover all available pages before exploring further.

# Streaming

> Stream responses with regular streams, SSE, and text streaming

The Streaming helper provides utilities for sending streaming responses, including regular data streams, Server-Sent Events (SSE), and text streams.

## Import

```typescript theme={null}
import { stream, streamSSE, streamText } from 'hono/streaming'
```

## Basic Streaming

The `stream` helper creates a streaming response for sending data progressively.

### Simple Stream

```typescript theme={null}
import { stream } from 'hono/streaming'

app.get('/stream', (c) => {
  return stream(c, async (stream) => {
    // Write data chunks
    for (let i = 0; i < 5; i++) {
      await stream.write(new Uint8Array([i]))
      await stream.sleep(100) // Wait 100ms between chunks
    }
  })
})
```

### Writing Different Data Types

```typescript theme={null}
import { stream } from 'hono/streaming'

app.get('/data', (c) => {
  return stream(c, async (stream) => {
    // Write string
    await stream.write('Hello ')
    
    // Write Uint8Array
    await stream.write(new Uint8Array([72, 105])) // "Hi"
    
    // Write with new line
    await stream.writeln('World')
  })
})
```

### Error Handling

Handle errors during streaming:

```typescript theme={null}
import { stream } from 'hono/streaming'

app.get('/stream-with-error', (c) => {
  return stream(
    c,
    async (stream) => {
      await stream.write('Starting...')
      throw new Error('Something went wrong!')
    },
    async (err, stream) => {
      // Error handler
      console.error('Stream error:', err)
      await stream.write('Error occurred')
    }
  )
})
```

### Abort Handling

Detect when the client disconnects:

```typescript theme={null}
import { stream } from 'hono/streaming'

app.get('/long-stream', (c) => {
  return stream(c, async (stream) => {
    let stopped = false
    
    stream.onAbort(() => {
      console.log('Client disconnected')
      stopped = true
    })
    
    while (!stopped) {
      await stream.write('data...')
      await stream.sleep(1000)
    }
  })
})
```

### Pipe from ReadableStream

Pipe data from another stream:

```typescript theme={null}
import { stream } from 'hono/streaming'

app.get('/pipe', (c) => {
  return stream(c, async (stream) => {
    const response = await fetch('https://example.com/data')
    
    if (response.body) {
      await stream.pipe(response.body)
    }
  })
})
```

## Server-Sent Events (SSE)

The `streamSSE` helper provides real-time server-to-client communication using Server-Sent Events.

### Basic SSE

```typescript theme={null}
import { streamSSE } from 'hono/streaming'

app.get('/sse', (c) => {
  return streamSSE(c, async (stream) => {
    let id = 0
    
    while (true) {
      await stream.writeSSE({
        data: `Message ${id}`,
        event: 'update',
        id: String(id++),
      })
      
      await stream.sleep(1000)
    }
  })
})
```

### SSE Message Format

```typescript theme={null}
import { streamSSE, SSEMessage } from 'hono/streaming'

app.get('/sse-advanced', (c) => {
  return streamSSE(c, async (stream) => {
    // Send message with all fields
    await stream.writeSSE({
      data: 'Message content',
      event: 'custom-event',
      id: '123',
      retry: 5000, // Reconnection time in ms
    })
  })
})
```

<ParamField path="data" type="string | Promise<string>" required>
  The message data. Can contain newlines.
</ParamField>

<ParamField path="event" type="string">
  Custom event type (default is "message")
</ParamField>

<ParamField path="id" type="string">
  Event ID for the Last-Event-ID header
</ParamField>

<ParamField path="retry" type="number">
  Reconnection time in milliseconds
</ParamField>

### Client-Side SSE

Connect to SSE endpoint from the browser:

```javascript theme={null}
const eventSource = new EventSource('/sse')

// Listen to default message event
eventSource.onmessage = (event) => {
  console.log('Message:', event.data)
}

// Listen to custom event
eventSource.addEventListener('update', (event) => {
  console.log('Update:', event.data)
})

// Handle connection open
eventSource.onopen = () => {
  console.log('Connection opened')
}

// Handle errors
eventSource.onerror = (error) => {
  console.error('SSE error:', error)
}

// Close connection
eventSource.close()
```

### SSE with Error Handling

```typescript theme={null}
import { streamSSE } from 'hono/streaming'

app.get('/sse-safe', (c) => {
  return streamSSE(
    c,
    async (stream) => {
      await stream.writeSSE({
        data: 'Starting...',
      })
      
      throw new Error('Processing failed')
    },
    async (err, stream) => {
      // Error handler sends error event
      await stream.writeSSE({
        event: 'error',
        data: err.message,
      })
    }
  )
})
```

### SSE Headers

SSE responses automatically set appropriate headers:

```typescript theme={null}
Transfer-Encoding: chunked
Content-Type: text/event-stream
Cache-Control: no-cache
Connection: keep-alive
```

## Text Streaming

The `streamText` helper is optimized for streaming text responses.

### Basic Text Stream

```typescript theme={null}
import { streamText } from 'hono/streaming'

app.get('/text-stream', (c) => {
  return streamText(c, async (stream) => {
    const words = ['Hello', 'World', 'from', 'Hono']
    
    for (const word of words) {
      await stream.write(word + ' ')
      await stream.sleep(200)
    }
  })
})
```

### Streaming LLM Responses

```typescript theme={null}
import { streamText } from 'hono/streaming'

app.get('/ai-stream', (c) => {
  return streamText(c, async (stream) => {
    // Simulate streaming from an LLM
    const response = 'This is a simulated AI response'
    
    for (const char of response) {
      await stream.write(char)
      await stream.sleep(50) // Simulate typing effect
    }
  })
})
```

### Text Stream Headers

Text streams automatically set:

```typescript theme={null}
Content-Type: text/plain
X-Content-Type-Options: nosniff
Transfer-Encoding: chunked
```

## StreamingAPI Methods

All streaming helpers provide a `StreamingApi` instance with these methods:

### write()

Write data to the stream:

```typescript theme={null}
await stream.write('text')
await stream.write(new Uint8Array([1, 2, 3]))
```

### writeln()

Write data with a newline:

```typescript theme={null}
await stream.writeln('Line 1')
await stream.writeln('Line 2')
```

### sleep()

Pause for a specified duration:

```typescript theme={null}
await stream.sleep(1000) // Wait 1 second
```

### pipe()

Pipe from a ReadableStream:

```typescript theme={null}
await stream.pipe(readableStream)
```

### onAbort()

Register an abort handler:

```typescript theme={null}
stream.onAbort(() => {
  console.log('Stream aborted')
})
```

### close()

Manually close the stream:

```typescript theme={null}
await stream.write('Final message')
stream.close()
```

## Use Cases

<CardGroup cols={2}>
  <Card title="Real-time Updates" icon="bolt">
    Use SSE for live notifications, dashboards, and real-time data feeds
  </Card>

  <Card title="AI Streaming" icon="brain">
    Stream LLM responses for better user experience with text streaming
  </Card>

  <Card title="File Downloads" icon="download">
    Stream large files without loading them entirely into memory
  </Card>

  <Card title="Log Streaming" icon="terminal">
    Stream server logs or process output in real-time
  </Card>
</CardGroup>

## Best Practices

<Note>
  Always handle client disconnections with `onAbort()` to clean up resources and stop processing.
</Note>

<Warning>
  SSE is one-way communication (server to client). For bidirectional communication, use WebSockets instead.
</Warning>

<Info>
  Add appropriate error handlers to prevent unhandled promise rejections and ensure graceful error recovery.
</Info>

## Browser Compatibility

SSE is supported in all modern browsers:

* Chrome/Edge: Full support
* Firefox: Full support
* Safari: Full support
* Opera: Full support

<Note>
  SSE does not work in Internet Explorer. Consider using WebSockets for broader compatibility.
</Note>
