Convert a ReadableStream to a String in JavaScript

21st May 2023

I have recently come across the need to convert a ReadableStream to a String. In my case this was because I was using React's react-dom/server to render a component in an edge worker. More specifically, I needed to build an email body from ReMirror JSON content using their static renderer.

Using ReactDOMServer.renderToReadableStream.

The renderToReadableStream method is the only option for rendering React DOM in a modern edge environment. That's ok, it's not hard to use a reader to build a string, chunk by chunk.

First, you need the stream and reader:

const stream = await ReactDOMServer.renderToReadableStream(
createElement(RichTextRenderer, { node: doc })
)
const reader = stream.getReader()

A reader has a read() method which returns a Promise that resolves to the next value received from the stream. There is one problem, the value is received as Uint8Array and so we will need to decode it with a TextDecoder.

const { value, done } = await reader.read()
if (value) {
html += new TextDecoder().decode(value)
}

We can use this within a loop to read until the stream is finished. Here is the final result.

import ReactDOMServer from 'react-dom/server'
import { createElement } from 'react'
import type { RemirrorJSON } from 'remirror'
import { RichTextRenderer } from './src/RichTextRenderer'
export { isRemirrorJSON } from 'remirror'
export async function generateHTMLFromDoc(doc: RemirrorJSON) {
const stream = await ReactDOMServer.renderToReadableStream(
createElement(RichTextRenderer, { node: doc })
)
const reader = stream.getReader()
let html = ''
while (true) {
const { value, done } = await reader.read()
if (value) {
html += new TextDecoder().decode(value)
}
if (done) {
return html
}
}
}

Further Reading