Response, string, and ArrayBuffer inputs. Bun’s implementation is based on Cloudflare’s lol-html.
Usage
A common use case is rewriting URLs in HTML content:<img> in a link, producing a diff like this:
Input types
HTMLRewriter can transform HTML from several input types:Response objects.
Element Handlers
Theon(selector, handlers) method registers handlers for HTML elements that match a CSS selector. The handlers run for each matching element during parsing:
transform(response) returns immediately; the rewrite continues in the
background and you read the result off the returned Response. Reading it
paces the rewrite: a streamed input (a file, a fetch() response, a
ReadableStream) is pulled through only as fast as the returned body is
consumed, so a slow reader does not accumulate the whole document in memory. If
nothing reads the body, the rewrite still runs every handler to the end of the
document and buffers the output until it is read. Because the rewrite outlives
transform(), an error thrown by an async handler (or a Promise it returns that
rejects) rejects the response body instead of throwing from transform():
transform() on a string or ArrayBuffer has to return its result
synchronously, so it cannot wait for a handler that needs the event loop to turn
(a timer, I/O, a fetch). Such a handler makes transform() throw a
TypeError, and the rewrite fails without running any further handlers:
process.nextTick and already-resolved
Promises) still works with transform(string). Pass a Response whenever a
handler might await real work.
CSS Selector Support
Theon() method supports a wide range of CSS selectors:
Element Operations
All element modification methods return the element instance, so calls can be chained:Text Operations
Text chunks represent portions of text content and report their position in the text node:Comment Operations
Comments support similar methods to text nodes:Document Handlers
TheonDocument(handlers) method registers handlers for events at the document level rather than within specific elements:
Response Handling
When transforming a Response:- The status code, headers, and other response properties are preserved
- The body is transformed while maintaining streaming capabilities
- Content-encoding (like gzip) is handled automatically
- The original response body is marked as used after transformation
- Headers are cloned to the new response
Error Handling
Which channel an error takes is decided by the overload you called, never by timing.transform() itself throws for:
- Invalid selector syntax in the
on()method - Invalid input types (for example, passing a Symbol)
- Body already used errors, and input bodies that have already failed or aborted
- Anything a content handler raises on a
string/ArrayBufferinput, since those have to produce their result beforetransform()returns — including a handler that needs the event loop (see Element Handlers)
Response input, transform() returns before the rewrite finishes, so
everything the rewrite discovers surfaces on the output body instead:
- An error thrown by a content handler, or a rejected Promise one returned
- Malformed or truncated input
- Stream errors reading the input body
- Memory allocation failures
unhandledRejection path. Earlier versions of Bun could surface
it from transform() itself.