---
title: "Streaming and SSE"
description: "Hono provides robust streaming primitives and specialized helpers for real-time data delivery, chunked data pipelines, and server-sent events across modern web runtimes. By leveraging underlying We..."
last_updated: "2026-10-12T08:14:12.75099+00:00"
canonical_url: "https://www.doc0.dev/docs/552ca36e-f67e-41c3-a07a-def9bd9551b0/technical/helpers-and-tooling/streaming-and-sse"
---

<details>
<summary>Relevant source files</summary>

The following files were used as context for generating this wiki page:

- [src/helper/streaming/sse.ts](https://github.com/blade47/hono/blob/e50df01453e71b071c3e6136b161b160b9fdf916/src/helper/streaming/sse.ts)
- [src/helper/streaming/text.ts](https://github.com/blade47/hono/blob/e50df01453e71b071c3e6136b161b160b9fdf916/src/helper/streaming/text.ts)
- [src/jsx/streaming.ts](https://github.com/blade47/hono/blob/e50df01453e71b071c3e6136b161b160b9fdf916/src/jsx/streaming.ts)
- [src/jsx/components.ts](https://github.com/blade47/hono/blob/e50df01453e71b071c3e6136b161b160b9fdf916/src/jsx/components.ts)
- [src/helper/streaming/stream.ts](https://github.com/blade47/hono/blob/e50df01453e71b071c3e6136b161b160b9fdf916/src/helper/streaming/stream.ts)
- [src/adapter/aws-lambda/handler.ts](https://github.com/blade47/hono/blob/e50df01453e71b071c3e6136b161b160b9fdf916/src/adapter/aws-lambda/handler.ts)
- [src/utils/stream.ts](https://github.com/blade47/hono/blob/e50df01453e71b071c3e6136b161b160b9fdf916/src/utils/stream.ts)
- [src/request.ts](https://github.com/blade47/hono/blob/e50df01453e71b071c3e6136b161b160b9fdf916/src/request.ts)
- [src/helper/ssg/ssg.ts](https://github.com/blade47/hono/blob/e50df01453e71b071c3e6136b161b160b9fdf916/src/helper/ssg/ssg.ts)
- [src/helper/proxy/index.ts](https://github.com/blade47/hono/blob/e50df01453e71b071c3e6136b161b160b9fdf916/src/helper/proxy/index.ts)
- [src/helper/streaming/index.ts](https://github.com/blade47/hono/blob/e50df01453e71b071c3e6136b161b160b9fdf916/src/helper/streaming/index.ts)
- [src/client/client.ts](https://github.com/blade47/hono/blob/e50df01453e71b071c3e6136b161b160b9fdf916/src/client/client.ts)
- [src/middleware/etag/digest.ts](https://github.com/blade47/hono/blob/e50df01453e71b071c3e6136b161b160b9fdf916/src/middleware/etag/digest.ts)
- [src/client/fetch-result-please.ts](https://github.com/blade47/hono/blob/e50df01453e71b071c3e6136b161b160b9fdf916/src/client/fetch-result-please.ts)
- [src/context.ts](https://github.com/blade47/hono/blob/e50df01453e71b071c3e6136b161b160b9fdf916/src/context.ts)
- [src/middleware/body-limit/index.ts](https://github.com/blade47/hono/blob/e50df01453e71b071c3e6136b161b160b9fdf916/src/middleware/body-limit/index.ts)
- [runtime-tests/lambda/stream-mock.ts](https://github.com/blade47/hono/blob/e50df01453e71b071c3e6136b161b160b9fdf916/runtime-tests/lambda/stream-mock.ts)
- [src/adapter/deno/websocket.ts](https://github.com/blade47/hono/blob/e50df01453e71b071c3e6136b161b160b9fdf916/src/adapter/deno/websocket.ts)
- [src/adapter/lambda-edge/handler.ts](https://github.com/blade47/hono/blob/e50df01453e71b071c3e6136b161b160b9fdf916/src/adapter/lambda-edge/handler.ts)
- [src/middleware/compress/index.ts](https://github.com/blade47/hono/blob/e50df01453e71b071c3e6136b161b160b9fdf916/src/middleware/compress/index.ts)
- [runtime-tests/lambda/mock.ts](https://github.com/blade47/hono/blob/e50df01453e71b071c3e6136b161b160b9fdf916/runtime-tests/lambda/mock.ts)
- [src/adapter/bun/websocket.ts](https://github.com/blade47/hono/blob/e50df01453e71b071c3e6136b161b160b9fdf916/src/adapter/bun/websocket.ts)
- [src/middleware/jsx-renderer/index.ts](https://github.com/blade47/hono/blob/e50df01453e71b071c3e6136b161b160b9fdf916/src/middleware/jsx-renderer/index.ts)
- [src/helper/streaming/utils.ts](https://github.com/blade47/hono/blob/e50df01453e71b071c3e6136b161b160b9fdf916/src/helper/streaming/utils.ts)
- [src/jsx/dom/server.ts](https://github.com/blade47/hono/blob/e50df01453e71b071c3e6136b161b160b9fdf916/src/jsx/dom/server.ts)
- [src/helper/html/index.ts](https://github.com/blade47/hono/blob/e50df01453e71b071c3e6136b161b160b9fdf916/src/helper/html/index.ts)
- [src/utils/concurrent.ts](https://github.com/blade47/hono/blob/e50df01453e71b071c3e6136b161b160b9fdf916/src/utils/concurrent.ts)
- [src/helper/websocket/index.ts](https://github.com/blade47/hono/blob/e50df01453e71b071c3e6136b161b160b9fdf916/src/helper/websocket/index.ts)
- [src/adapter/cloudflare-workers/websocket.ts](https://github.com/blade47/hono/blob/e50df01453e71b071c3e6136b161b160b9fdf916/src/adapter/cloudflare-workers/websocket.ts)
- [src/client/utils.ts](https://github.com/blade47/hono/blob/e50df01453e71b071c3e6136b161b160b9fdf916/src/client/utils.ts)
</details>

## Overview

Hono provides robust streaming primitives and specialized helpers for real-time data delivery, chunked data pipelines, and server-sent events across modern web runtimes. By leveraging underlying Web Standard `TransformStream` and `ReadableStream` primitives, these APIs manage low-level connection lifecycles, backpressure, and abort signals seamlessly without blocking execution.

Sources: [src/utils/stream.ts:6-46](https://github.com/blade47/hono/blob/e50df01453e71b071c3e6136b161b160b9fdf916/src/utils/stream.ts#L6-L46), [src/helper/streaming/sse.ts:72-99](https://github.com/blade47/hono/blob/e50df01453e71b071c3e6136b161b160b9fdf916/src/helper/streaming/sse.ts#L72-L99), [src/helper/streaming/stream.ts:7-13](https://github.com/blade47/hono/blob/e50df01453e71b071c3e6136b161b160b9fdf916/src/helper/streaming/stream.ts#L7-L13)

## Core Streaming Architecture and Backpressure

### Plumbing and State Management

Hono's streaming infrastructure centers around the `StreamingApi` class, which bridges Web Standard `TransformStream`, `WritableStream`, and `ReadableStream` instances to manage chunk delivery and backpressure.

The `StreamingApi` constructor accepts both a `writable` and an internal `_readable` stream, immediately acquiring a default writer via `writable.getWriter()` and a reader via `_readable.getReader()`. It exposes a public `responseReadable` `ReadableStream` whose `pull()` controller reads chunks from the underlying reader, propagating backpressure signals naturally from the consumer to the producer.

```typescript
export class StreamingApi {
  private writer: WritableStreamDefaultWriter<Uint8Array>
  private encoder: TextEncoder
  private writable: WritableStream
  private abortSubscribers: (() => void | Promise<void>)[] = []
  responseReadable: ReadableStream
  /**
   * Whether the stream has been aborted.
   */
  aborted: boolean = false
  /**
   * Whether the stream has been closed normally.
   */
  closed: boolean = false

  constructor(writable: WritableStream, _readable: ReadableStream) {
    this.writable = writable
    this.writer = writable.getWriter()
    this.encoder = new TextEncoder()

    const reader = _readable.getReader()

    // in case the user disconnects, let the reader know to cancel
    // this in-turn results in responseReadable being closed
    // and writeSSE method no longer blocks indefinitely
    this.abortSubscribers.push(async () => {
      await reader.cancel()
    })

    this.responseReadable = new ReadableStream({
      async pull(controller) {
        const { done, value } = await reader.read()
        done ? controller.close() : controller.enqueue(value)
      },
      cancel: () => {
        if (!this.closed) {
          this.abort()
        }
      },
    })
  }
```
Sources: [src/utils/stream.ts:6-46](https://github.com/blade47/hono/blob/e50df01453e71b071c3e6136b161b160b9fdf916/src/utils/stream.ts#L6-L46)

> [!NOTE]
> When piping an external `ReadableStream` into `StreamingApi` via `pipe(body)`, the internal writer lock is released using `writer.releaseLock()` before calling `body.pipeTo(this.writable, { preventClose: true })`, and a new writer is acquired afterward.

```typescript
async pipe(body: ReadableStream) {
  this.writer.releaseLock()
  await body.pipeTo(this.writable, { preventClose: true })
  this.writer = this.writable.getWriter()
}
```
Sources: [src/utils/stream.ts:78-82](https://github.com/blade47/hono/blob/e50df01453e71b071c3e6136b161b160b9fdf916/src/utils/stream.ts#L78-L82)

### Abort Signals and Runtimes

Connection aborts and cancellation requests trigger subscribers registered in `abortSubscribers`. When `responseReadable` is canceled or an external abort occurs, `StreamingApi.abort()` iterates through all subscribers, invoking functions such as `reader.cancel()` to unblock pending writes and close the pipeline.

```typescript
onAbort(listener: () => void | Promise<void>) {
  this.abortSubscribers.push(listener)
}

/**
 * Abort the stream.
 * You can call this method when stream is aborted by external event.
 */
abort() {
  if (!this.aborted) {
    this.aborted = true
    this.abortSubscribers.forEach((subscriber) => subscriber())
  }
}
```
Sources: [src/utils/stream.ts:84-97](https://github.com/blade47/hono/blob/e50df01453e71b071c3e6136b161b160b9fdf916/src/utils/stream.ts#L84-L97)

> [!WARNING]
> Versions of Bun prior to v1.1.27 do not automatically call `cancel()` on `ReadableStream` objects returned by `Bun.serve()`. Hono works around this by listening to request signal abort events on older runtimes to manually invoke `stream.abort()`.

```typescript
// Until Bun v1.1.27, Bun didn't call cancel() on the ReadableStream for Response objects from Bun.serve()
if (isOldBunVersion()) {
  c.req.raw.signal.addEventListener('abort', () => {
    if (!stream.closed) {
      stream.abort()
    }
  })
}
```
Sources: [src/helper/streaming/stream.ts:15-22](https://github.com/blade47/hono/blob/e50df01453e71b071c3e6136b161b160b9fdf916/src/helper/streaming/stream.ts#L15-L22)

## Binary and Chunked Stream Helper

### Public Stream API and Response Creation

The public `stream` helper function initializes a chunked response workflow by constructing a `TransformStream`, instantiating a `StreamingApi` instance, and returning a response via `c.newResponse(stream.responseReadable)`. It accepts a callback function `cb` receiving the active `StreamingApi` and an optional `onError` handler.

```typescript
export const stream = (
  c: Context,
  cb: (stream: StreamingApi) => Promise<void>,
  onError?: (e: Error, stream: StreamingApi) => Promise<void>
): Response => {
  const { readable, writable } = new TransformStream()
  const stream = new StreamingApi(writable, readable)
```
Sources: [src/helper/streaming/stream.ts:7-13](https://github.com/blade47/hono/blob/e50df01453e71b071c3e6136b161b160b9fdf916/src/helper/streaming/stream.ts:7-13), [src/helper/streaming/stream.ts:44-44](https://github.com/blade47/hono/blob/e50df01453e71b071c3e6136b161b160b9fdf916/src/helper/streaming/stream.ts:44-44)

> [!NOTE]
> In runtimes such as Bun, the context object `c` is destroyed immediately when the initial response is returned. To prevent premature garbage collection, Hono stores the context reference in a `WeakMap` (`contextStash`) keyed by `stream.responseReadable` until streaming completes.

```typescript
// in bun, `c` is destroyed when the request is returned, so hold it until the end of streaming
contextStash.set(stream.responseReadable, c)
;(async () => {
```
Sources: [src/helper/streaming/stream.ts:24-26](https://github.com/blade47/hono/blob/e50df01453e71b071c3e6136b161b160b9fdf916/src/helper/streaming/stream.ts:24-26)

### Raw Chunk Writes and Stream Control

The `StreamingApi` class exposes methods for transmitting raw binary and text chunks, inserting newlines, delaying execution, piping streams, and closing connections.

| Method | Parameters | Return Type | Description |
| :--- | :--- | :--- | :--- |
| `write` | `input: Uint8Array \| string` | `Promise<StreamingApi>` | Encodes string inputs via `TextEncoder` or writes raw `Uint8Array` bytes to the underlying writer. |
| `writeln` | `input: string` | `Promise<StreamingApi>` | Appends a newline character (`\n`) to the input string and writes it. |
| `sleep` | `ms: number` | `Promise<unknown>` | Pauses execution for the specified milliseconds using `setTimeout`. |
| `pipe` | `body: ReadableStream` | `Promise<void>` | Releases the writer lock, pipes an external readable stream into the writable target, and re-acquires the writer. |
| `close` | none | `Promise<void>` | Marks the stream as closed and closes the underlying writer. |
| `onAbort` | `listener: () => void \| Promise<void>` | `void` | Registers a callback invoked when the stream aborts. |
| `abort` | none | `void` | Sets the aborted flag to true and triggers all registered abort subscribers. |

```typescript
async write(input: Uint8Array | string): Promise<StreamingApi> {
  try {
    if (typeof input === 'string') {
      input = this.encoder.encode(input)
    }
    await this.writer.write(input)
  } catch {
    // Do nothing. If you want to handle errors, create a stream by yourself.
  }
  return this
}

async writeln(input: string): Promise<StreamingApi> {
  await this.write(input + '\n')
  return this
}

sleep(ms: number): Promise<unknown> {
  return new Promise((res) => setTimeout(res, ms))
}

async close() {
  this.closed = true
  try {
    await this.writer.close()
  } catch {
    // Do nothing. If you want to handle errors, create a stream by yourself.
  }
}

async pipe(body: ReadableStream) {
  this.writer.releaseLock()
  await body.pipeTo(this.writable, { preventClose: true })
  this.writer = this.writable.getWriter()
}

onAbort(listener: () => void | Promise<void>) {
  this.abortSubscribers.push(listener)
}

/**
 * Abort the stream.
 * You can call this method when stream is aborted by external event.
 */
abort() {
  if (!this.aborted) {
    this.aborted = true
    this.abortSubscribers.forEach((subscriber) => subscriber())
  }
}
```
Sources: [src/utils/stream.ts:48-97](https://github.com/blade47/hono/blob/e50df01453e71b071c3e6136b161b160b9fdf916/src/utils/stream.ts:48-97)

> [!WARNING]
> Both `write` and `close` catch and suppress write errors internally. Developers requiring custom error propagation or handling during raw chunk transmission must construct and manage custom streams directly rather than relying on the helper defaults.

```typescript
try {
  if (typeof input === 'string') {
    input = this.encoder.encode(input)
  }
  await this.writer.write(input)
} catch {
  // Do nothing. If you want to handle errors, create a stream by yourself.
}
```
Sources: [src/utils/stream.ts:49-56](https://github.com/blade47/hono/blob/e50df01453e71b071c3e6136b161b160b9fdf916/src/utils/stream.ts:49-56), [src/utils/stream.ts:71-76](https://github.com/blade47/hono/blob/e50df01453e71b071c3e6136b161b160b9fdf916/src/utils/stream.ts:71-76)

## Plaintext Streaming Helper

### Configuration and Semantics

The `streamText` helper function sets up a response specifically optimized for plain text streaming. It configures the response headers by setting `Content-Type` to `text/plain`, `X-Content-Type-Options` to `nosniff`, and `Transfer-Encoding` to `chunked` before delegating the underlying stream creation to the base `stream` function.

```typescript
export const streamText = (
  c: Context,
  cb: (stream: StreamingApi) => Promise<void>,
  onError?: (e: Error, stream: StreamingApi) => Promise<void>
): Response => {
  c.header('Content-Type', TEXT_PLAIN)
  c.header('X-Content-Type-Options', 'nosniff')
  c.header('Transfer-Encoding', 'chunked')
  return stream(c, cb, onError)
}
```
Sources: [src/helper/streaming/text.ts:6-15](https://github.com/blade47/hono/blob/e50df01453e71b071c3e6136b161b160b9fdf916/src/helper/streaming/text.ts:6-15)

| Header Name | Value | Purpose |
| :--- | :--- | :--- |
| `Content-Type` | `text/plain` | Instructs the client to interpret the incoming payload as plaintext content. |
| `X-Content-Type-Options` | `nosniff` | Prevents MIME-type sniffing by enforcing strict adherence to the declared content type. |
| `Transfer-Encoding` | `chunked` | Enables chunked transfer encoding for streaming responses without a predefined content length. |

```typescript
c.header('Content-Type', TEXT_PLAIN)
c.header('X-Content-Type-Options', 'nosniff')
c.header('Transfer-Encoding', 'chunked')
```
Sources: [src/helper/streaming/text.ts:11-13](https://github.com/blade47/hono/blob/e50df01453e71b071c3e6136b161b160b9fdf916/src/helper/streaming/text.ts:11-13)

> [!NOTE]
> The `streamText` function accepts three parameters: the Hono `Context` object `c`, a mandatory callback function `cb` receiving the active `StreamingApi` instance, and an optional `onError` error-handling callback.

```typescript
export const streamText = (
  c: Context,
  cb: (stream: StreamingApi) => Promise<void>,
  onError?: (e: Error, stream: StreamingApi) => Promise<void>
): Response => {
```
Sources: [src/helper/streaming/text.ts:6-10](https://github.com/blade47/hono/blob/e50df01453e71b071c3e6136b161b160b9fdf916/src/helper/streaming/text.ts:6-10)

## Server-Sent Events API and Lifecycle

### SSEStreamingApi and Connection Setup

The `SSEStreamingApi` class extends `StreamingApi` to provide specialized formatting for Server-Sent Events messages via `writeSSE`. The `streamSSE` helper initializes this architecture by creating a `TransformStream` pair, instantiating `SSEStreamingApi` with the writable and readable ends, and establishing essential response headers for event-stream communication.

| Header Name | Value | Purpose |
| :--- | :--- | :--- |
| `Transfer-Encoding` | `chunked` | Enables chunked transfer encoding for continuous streaming payloads. |
| `Content-Type` | `text/event-stream` | Instructs the client to parse the incoming stream as Server-Sent Events. |
| `Cache-Control` | `no-cache` | Prevents intermediaries and clients from caching the event stream. |
| `Connection` | `keep-alive` | Keeps the underlying TCP connection open for continuous event transmission. |

Sources: [src/helper/streaming/sse.ts:13-16](https://github.com/blade47/hono/blob/e50df01453e71b071c3e6136b161b160b9fdf916/src/helper/streaming/sse.ts:13-16), [src/helper/streaming/sse.ts:72-96](https://github.com/blade47/hono/blob/e50df01453e71b071c3e6136b161b160b9fdf916/src/helper/streaming/sse.ts:72-96)

> [!NOTE]
> In runtime environments such as Bun, context objects can be destroyed immediately after the initial response returns. To prevent premature garbage collection, Hono stores the context reference inside a `contextStash` `WeakMap` keyed by `stream.responseReadable` until the stream lifecycle completes.

```typescript
const contextStash: WeakMap<ReadableStream, Context> = new WeakMap<ReadableStream, Context>()
```
Sources: [src/helper/streaming/sse.ts:70-70](https://github.com/blade47/hono/blob/e50df01453e71b071c3e6136b161b160b9fdf916/src/helper/streaming/sse.ts:70-70), [src/helper/streaming/sse.ts:89-91](https://github.com/blade47/hono/blob/e50df01453e71b071c3e6136b161b160b9fdf916/src/helper/streaming/sse.ts:89-91)

> [!WARNING]
> Prior to Bun v1.1.27, runtime responses from `Bun.serve()` did not automatically invoke `cancel()` on the underlying `ReadableStream`. Hono mitigates this by manually listening to request abort signals on older Bun versions and triggering `stream.abort()` if the stream remains unclosed.

```typescript
// Until Bun v1.1.27, Bun didn't call cancel() on the ReadableStream for Response objects from Bun.serve()
if (isOldBunVersion()) {
  c.req.raw.signal.addEventListener('abort', () => {
    if (!stream.closed) {
      stream.abort()
    }
  })
}
```
Sources: [src/helper/streaming/sse.ts:80-87](https://github.com/blade47/hono/blob/e50df01453e71b071c3e6136b161b160b9fdf916/src/helper/streaming/sse.ts:80-87)

### Execution Walkthrough and Sequence

The execution lifecycle of a Server-Sent Events stream follows a strict invocation path managed by the `run` helper function, executing the user callback, handling errors, and ensuring resource cleanup.

```mermaid
sequenceDiagram
    participant streamSSE as streamSSE (sse.ts)
    participant run as run (sse.ts)
    participant close as close (stream.ts)

    streamSSE->>run: Invokes run(stream, cb, onError)
    run->>cb: Awaits user callback execution
    run-->>close: Enters finally block and calls stream.close()
```

Sources: [src/helper/streaming/sse.ts:47-68](https://github.com/blade47/hono/blob/e50df01453e71b071c3e6136b161b160b9fdf916/src/helper/streaming/sse.ts:47-68), [src/helper/streaming/sse.ts:97-97](https://github.com/blade47/hono/blob/e50df01453e71b071c3e6136b161b160b9fdf916/src/helper/streaming/sse.ts:97-97)

The lifecycle operations proceed through the following steps:
1. `streamSSE` invokes `run` passing the active `SSEStreamingApi` instance, callback, and optional error handler.
Sources: [src/helper/streaming/sse.ts:97-97](https://github.com/blade47/hono/blob/e50df01453e71b071c3e6136b161b160b9fdf916/src/helper/streaming/sse.ts:97-97)
2. `run` awaits the user callback `cb(stream)`. If an error occurs and `onError` is provided, it executes `onError(e, stream)` followed by writing an error event to the stream; otherwise, errors are logged to `console.error`.
Sources: [src/helper/streaming/sse.ts:52-64](https://github.com/blade47/hono/blob/e50df01453e71b071c3e6136b161b160b9fdf916/src/helper/streaming/sse.ts:52-64)
3. `close` is guaranteed to execute in the `finally` block, marking `this.closed = true` and closing the underlying writer to finalize the transmission.
Sources: [src/helper/streaming/sse.ts:65-67](https://github.com/blade47/hono/blob/e50df01453e71b071c3e6136b161b160b9fdf916/src/helper/streaming/sse.ts:65-67), [src/utils/stream.ts:69-76](https://github.com/blade47/hono/blob/e50df01453e71b071c3e6136b161b160b9fdf916/src/utils/stream.ts:69-76)

## Event Framing and Serialization

### Execution Walkthrough

The serialization of Server-Sent Events messages is handled by the `writeSSE` method on the `SSEStreamingApi` class. This method processes incoming message payloads, validates field properties for illegal line breaks, formats them into the standard SSE wire format, and dispatches the raw bytes down the stream via `streamSSE → run → writeSSE → write`.

```mermaid
sequenceDiagram
    participant streamSSE as streamSSE (sse.ts)
    participant run as run (sse.ts)
    participant writeSSE as writeSSE (sse.ts)
    participant write as write (stream.ts)

    streamSSE->>run: Invokes streamSSE -> run
    run->>writeSSE: run -> writeSSE (user callback invokes stream.writeSSE)
    writeSSE->>write: writeSSE -> write (serializes message and invokes this.write)
```

Sources: [src/helper/streaming/sse.ts:18-44](https://github.com/blade47/hono/blob/e50df01453e71b071c3e6136b161b160b9fdf916/src/helper/streaming/sse.ts:18-44), [src/helper/streaming/sse.ts:47-68](https://github.com/blade47/hono/blob/e50df01453e71b071c3e6136b161b160b9fdf916/src/helper/streaming/sse.ts:47-68), [src/helper/streaming/sse.ts:97-97](https://github.com/blade47/hono/blob/e50df01453e71b071c3e6136b161b160b9fdf916/src/helper/streaming/sse.ts:97-97), [src/utils/stream.ts:48-58](https://github.com/blade47/hono/blob/e50df01453e71b071c3e6136b161b160b9fdf916/src/utils/stream.ts:48-58)

1. `streamSSE` initializes the transformation stream and invokes `run`.
Sources: [src/helper/streaming/sse.ts:72-97](https://github.com/blade47/hono/blob/e50df01453e71b071c3e6136b161b160b9fdf916/src/helper/streaming/sse.ts:72-97)
2. `run` executes the user-supplied callback function.
Sources: [src/helper/streaming/sse.ts:47-53](https://github.com/blade47/hono/blob/e50df01453e71b071c3e6136b161b160b9fdf916/src/helper/streaming/sse.ts:47-53)
3. `writeSSE` receives an `SSEMessage` object, resolving any asynchronous data promises and formatting fields into text blocks.
Sources: [src/helper/streaming/sse.ts:18-41](https://github.com/blade47/hono/blob/e50df01453e71b071c3e6136b161b160b9fdf916/src/helper/streaming/sse.ts:18-41)
4. `write` encodes the formatted string into a `Uint8Array` via `TextEncoder` and writes it to the underlying writable stream writer.
Sources: [src/helper/streaming/sse.ts:43-43](https://github.com/blade47/hono/blob/e50df01453e71b071c3e6136b161b160b9fdf916/src/helper/streaming/sse.ts:43-43), [src/utils/stream.ts:7-24](https://github.com/blade47/hono/blob/e50df01453e71b071c3e6136b161b160b9fdf916/src/utils/stream.ts:7-24), [src/utils/stream.ts:48-58](https://github.com/blade47/hono/blob/e50df01453e71b071c3e6136b161b160b9fdf916/src/utils/stream.ts:48-58)

### Message Structure and Validation

The `SSEMessage` interface supports four specific fields used to frame events. Data payloads can be provided as a string or a `Promise<string>`.

| Field | Type | Description |
| :--- | :--- | :--- |
| `data` | `string \| Promise<string>` | The payload content of the message; multi-line strings are automatically prefixed with `data: ` on each line. |
| `event` | `string` (optional) | The event type identifier for the message. |
| `id` | `string` (optional) | The event stream synchronization ID. |
| `retry` | `number` (optional) | The reconnection time interval in milliseconds. |

Sources: [src/helper/streaming/sse.ts:6-11](https://github.com/blade47/hono/blob/e50df01453e71b071c3e6136b161b160b9fdf916/src/helper/streaming/sse.ts:6-11), [src/helper/streaming/sse.ts:18-41](https://github.com/blade47/hono/blob/e50df01453e71b071c3e6136b161b160b9fdf916/src/helper/streaming/sse.ts:18-41)

> [!CAUTION]
> Control character injection is strictly guarded during message serialization. If any of the tracked meta-fields (`event`, `id`, or `retry`) contain carriage return (`\r`) or newline (`\n`) characters, `writeSSE` immediately throws an error to prevent protocol corruption.

```typescript
for (const key of ['event', 'id', 'retry'] as (keyof SSEMessage)[]) {
  if (message[key] && /[\r\n]/.test(message[key] as string)) {
    throw new Error(`${key} must not contain "\\r" or "\\n"`)
  }
}
```
Sources: [src/helper/streaming/sse.ts:27-31](https://github.com/blade47/hono/blob/e50df01453e71b071c3e6136b161b160b9fdf916/src/helper/streaming/sse.ts:27-31)

## Related

- [JSX Streaming and Suspense](https://www.doc0.dev/docs/552ca36e-f67e-41c3-a07a-def9bd9551b0/technical/jsx-and-ui/jsx-streaming-and-suspense)
- [Response Compression](https://www.doc0.dev/docs/552ca36e-f67e-41c3-a07a-def9bd9551b0/technical/traffic-middleware/response-compression)


## Sitemap

See the full [sitemap](https://www.doc0.dev/docs/552ca36e-f67e-41c3-a07a-def9bd9551b0/llms.txt) for all pages in this wiki.
