Skip to main content
HTMLRewriter transforms HTML documents with CSS selectors. It works with 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:
The rewriter replaces every image with a thumbnail of Rick Astley and wraps each <img> in a link, producing a diff like this:
Clicking any image now leads to a very famous video.

Input types

HTMLRewriter can transform HTML from several input types:
The Cloudflare Workers implementation of HTMLRewriter only supports Response objects.

Element Handlers

The on(selector, handlers) method registers handlers for HTML elements that match a CSS selector. The handlers run for each matching element during parsing:
Handlers can be asynchronous and return a Promise. The transformation pauses on that element until the Promise settles, so handlers still run one at a time, in document order:
transform(response) returns immediately; the rewrite continues in the background and you read the result off the returned Response. 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:
A handler whose Promise settles within a microtask checkpoint (anything that does not need the event loop, including process.nextTick and already-resolved Promises) still works with transform(string). Pass a Response whenever a handler might await real work.

CSS Selector Support

The on() 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

The onDocument(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 / ArrayBuffer input, since those have to produce their result before transform() returns — including a handler that needs the event loop (see Element Handlers)
For a 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
A rejection from a Promise a handler creates but neither returns nor awaits reaches neither channel: like any detached rejection, it goes to the process-global unhandledRejection path. Earlier versions of Bun could surface it from transform() itself.

See also

You can also read the Cloudflare documentation, which this API is intended to be compatible with.