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:
transform(string). Anything that does not need the event loop qualifies,
including process.nextTick and already-resolved Promises. 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 you can chain calls: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, HTMLRewriter:- Preserves the status code, headers, and other response properties
- Transforms the body while maintaining streaming capabilities
- Handles content-encoding (like gzip) automatically
- Marks the original response body as used after transformation
- Clones headers to the new response
Error Handling
The overload you called decides which channel an error takes. Timing never does.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. The same goes for 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.