Files
Hendrik LiebauandGitHub e194a06552 Fix local fonts in statically prerendered ImageResponse metadata route (#95121)
Follow-up to #94957, which made cached `ImageResponse` routes statically
prerenderable by serializing the constructor arguments through React
Flight (to build a cache key and to run the element's async Server
Components once). A custom font passed as a `Buffer` in the options was
corrupted by that round-trip: Flight applies the `toJSON` method that
Node's `Buffer` carries, so the font reached satori as a `{ type:
'Buffer', data: [...] }` plain object rather than binary, and satori's
font parser threw `TypeError: First argument to DataView constructor
must be an ArrayBuffer`. The crash happened even without a user `'use
cache'` directive, because the serialization runs unconditionally during
the prerender.

We now serialize only the element through Flight, which is the only part
that needs async resolution and dynamic-input detection, and pair the
resolved element with the original in-memory options when handing the
tree to satori. The font `Buffer` is therefore never serialized and
reaches satori intact. The cache key no longer comes from the serialized
arguments either; it is derived from a SHA-256 hash of the serialized
element combined with a content hash of the options, with binary font
data hashed by its bytes and everything else by a sorted, type-tagged
walk. Besides fixing the crash, this keeps font bytes out of the cache
key and removes the "Uint8Array objects are not supported" warning that
the dev React runtime emitted during `next build --debug-prerender`.

The custom-fonts documentation example now loads the font once at module
scope. This keeps the route statically generated, since reading inside
the component would be uncached I/O that Cache Components treats as
dynamic, and it steers users away from wrapping the read in `'use
cache'`, which would route the font `Buffer` through use-cache's own
Flight serialization and reintroduce the same corruption upstream of
`ImageResponse`, where this fix cannot reach it. A regression test loads
a local font via a module-scope `readFile` and asserts the route
prerenders statically.
2026-06-24 20:48:02 +00:00
..