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

Import

import { stream, streamSSE, streamText } from 'hono/streaming'

Basic Streaming

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

Simple Stream

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

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

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

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
    })
  })
})
data
string | Promise<string>
required
The message data. Can contain newlines.
event
string
Custom event type (default is “message”)
id
string
Event ID for the Last-Event-ID header
retry
number
Reconnection time in milliseconds

Client-Side SSE

Connect to SSE endpoint from the browser:
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

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

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

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:
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:
await stream.write('text')
await stream.write(new Uint8Array([1, 2, 3]))

writeln()

Write data with a newline:
await stream.writeln('Line 1')
await stream.writeln('Line 2')

sleep()

Pause for a specified duration:
await stream.sleep(1000) // Wait 1 second

pipe()

Pipe from a ReadableStream:
await stream.pipe(readableStream)

onAbort()

Register an abort handler:
stream.onAbort(() => {
  console.log('Stream aborted')
})

close()

Manually close the stream:
await stream.write('Final message')
stream.close()

Use Cases

Real-time Updates

Use SSE for live notifications, dashboards, and real-time data feeds

AI Streaming

Stream LLM responses for better user experience with text streaming

File Downloads

Stream large files without loading them entirely into memory

Log Streaming

Stream server logs or process output in real-time

Best Practices

Always handle client disconnections with onAbort() to clean up resources and stop processing.
SSE is one-way communication (server to client). For bidirectional communication, use WebSockets instead.
Add appropriate error handlers to prevent unhandled promise rejections and ensure graceful error recovery.

Browser Compatibility

SSE is supported in all modern browsers:
  • Chrome/Edge: Full support
  • Firefox: Full support
  • Safari: Full support
  • Opera: Full support
SSE does not work in Internet Explorer. Consider using WebSockets for broader compatibility.

Build docs developers (and LLMs) love