From 610fa922605e0a65ff05f5d0eac11733c923bd0d Mon Sep 17 00:00:00 2001 From: Dinh Le Date: Sat, 12 Sep 2026 16:18:08 +0700 Subject: [PATCH 1/3] docs(expo): use emoji in feature support tables MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Replace the Yes/No and Works/Fails cells in the Expo adapter's feature support tables with ✅ and ❌ and align the table columns, matching the style used by the rate limit adapters table. --- apps/content/docs/adapters/expo.mdx | 28 ++++++++++++++-------------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/apps/content/docs/adapters/expo.mdx b/apps/content/docs/adapters/expo.mdx index 32421c21c..3555f66a5 100644 --- a/apps/content/docs/adapters/expo.mdx +++ b/apps/content/docs/adapters/expo.mdx @@ -84,16 +84,16 @@ oRPC treats binary data two different ways, and the difference decides what work Expo never parses multipart, and the WebSocket link cannot even produce it, which is why every nested row below fails somewhere. Everything else depends on whether you install the [Blob polyfill](#blob-polyfill). -| Feature | Fetch | Fetch + polyfill | WebSocket | WebSocket + polyfill | -| --- | --- | --- | --- | --- | -| [AsyncIteratorObject](/docs/async-iterator-object) download | Yes | Yes | Yes | Yes | -| `AsyncIteratorObject` upload | Buffered | Buffered | Yes | Yes | -| [`ReadableStream`](/docs/binary-data#readablestreamuint8array) download | Yes | Yes | Yes | Yes | -| `ReadableStream` upload | Buffered | Buffered | Yes | Yes | -| Root-level [`File` or `Blob`](/docs/binary-data#file-and-blob) download | No | Yes | No | Yes | -| Root-level `File` or `Blob` upload | Yes | Yes | No | Yes | -| Nested `File` or `Blob` download | No | No | No | No | -| Nested `File` or `Blob` upload | No | Yes | No | No | +| Feature | Fetch | Fetch + polyfill | WebSocket | WebSocket + polyfill | +| ----------------------------------------------------------------------------------- | -------- | ---------------- | --------- | -------------------- | +| [AsyncIteratorObject](/docs/async-iterator-object) download | ✅ | ✅ | ✅ | ✅ | +| `AsyncIteratorObject` upload | Buffered | Buffered | ✅ | ✅ | +| [`ReadableStream`](/docs/binary-data#readablestreamuint8array) download | ✅ | ✅ | ✅ | ✅ | +| `ReadableStream` upload | Buffered | Buffered | ✅ | ✅ | +| Root-level [`File` or `Blob`](/docs/binary-data#file-and-blob) download | ❌ | ✅ | ❌ | ✅ | +| Root-level `File` or `Blob` upload | ✅ | ✅ | ❌ | ✅ | +| Nested `File` or `Blob` download | ❌ | ❌ | ❌ | ❌ | +| Nested `File` or `Blob` upload | ❌ | ✅ | ❌ | ❌ | Two things are worth knowing beyond the table: @@ -145,10 +145,10 @@ A nested `File` or `Blob` is packed into a `FormData` and sent as `multipart/for Writing multipart works, but only on the fetch link, where `expo/fetch` does the encoding. The WebSocket link uses `Response` to write it too, so that link fails in both directions. -| Link | Nested upload | Nested download | -| --- | --- | --- | -| Fetch | Works with the [Blob polyfill](#blob-polyfill) | Fails | -| WebSocket | Fails | Fails | +| Link | Nested upload | Nested download | +| --------- | ------------------------------------------ | --------------- | +| Fetch | ✅ with the [Blob polyfill](#blob-polyfill) | ❌ | +| WebSocket | ❌ | ❌ | **The easy fix is to avoid multipart.** Keep files at the root of a procedure's input or output instead of inside an object. Or extend the [RPC JSON Serializer](/docs/rpc/serializer) to carry binary as `base64`. From da6f60fda04ba3d4d91cb81c457217636ff09e51 Mon Sep 17 00:00:00 2001 From: Dinh Le Date: Sat, 12 Sep 2026 16:35:46 +0700 Subject: [PATCH 2/3] =?UTF-8?q?docs(expo):=20merge=20polyfill=20columns=20?= =?UTF-8?q?and=20mark=20conditional=20support=20with=20=F0=9F=9F=A1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Collapse the feature support table to Fetch and WebSocket columns and mark cells that only work when buffered or with the Blob polyfill as 🟡 with the condition inline. --- apps/content/docs/adapters/expo.mdx | 29 +++++++++++++---------------- 1 file changed, 13 insertions(+), 16 deletions(-) diff --git a/apps/content/docs/adapters/expo.mdx b/apps/content/docs/adapters/expo.mdx index 3555f66a5..1ffec83e6 100644 --- a/apps/content/docs/adapters/expo.mdx +++ b/apps/content/docs/adapters/expo.mdx @@ -82,23 +82,20 @@ oRPC treats binary data two different ways, and the difference decides what work - **Root level**, where a `File` or `Blob` is the entire input or output. oRPC sends it as the raw body, with no multipart involved. - **Nested**, where a `File` or `Blob` sits inside an object or array. oRPC packs it into a `FormData`, which travels as `multipart/form-data`. -Expo never parses multipart, and the WebSocket link cannot even produce it, which is why every nested row below fails somewhere. Everything else depends on whether you install the [Blob polyfill](#blob-polyfill). +Expo never parses multipart, and the WebSocket link cannot even produce it, which is why every nested row below fails somewhere. -| Feature | Fetch | Fetch + polyfill | WebSocket | WebSocket + polyfill | -| ----------------------------------------------------------------------------------- | -------- | ---------------- | --------- | -------------------- | -| [AsyncIteratorObject](/docs/async-iterator-object) download | ✅ | ✅ | ✅ | ✅ | -| `AsyncIteratorObject` upload | Buffered | Buffered | ✅ | ✅ | -| [`ReadableStream`](/docs/binary-data#readablestreamuint8array) download | ✅ | ✅ | ✅ | ✅ | -| `ReadableStream` upload | Buffered | Buffered | ✅ | ✅ | -| Root-level [`File` or `Blob`](/docs/binary-data#file-and-blob) download | ❌ | ✅ | ❌ | ✅ | -| Root-level `File` or `Blob` upload | ✅ | ✅ | ❌ | ✅ | -| Nested `File` or `Blob` download | ❌ | ❌ | ❌ | ❌ | -| Nested `File` or `Blob` upload | ❌ | ✅ | ❌ | ❌ | +| Feature | Fetch | WebSocket | +| ----------------------------------------------------------------------------------- | ------------------------------------------ | ------------------------------------------ | +| [AsyncIteratorObject](/docs/async-iterator-object) download | ✅ | ✅ | +| `AsyncIteratorObject` upload | 🟡 buffered | ✅ | +| [`ReadableStream`](/docs/binary-data#readablestreamuint8array) download | ✅ | ✅ | +| `ReadableStream` upload | 🟡 buffered | ✅ | +| Root-level [`File` or `Blob`](/docs/binary-data#file-and-blob) download | 🟡 with the [Blob polyfill](#blob-polyfill) | 🟡 with the [Blob polyfill](#blob-polyfill) | +| Root-level `File` or `Blob` upload | ✅ | 🟡 with the [Blob polyfill](#blob-polyfill) | +| Nested `File` or `Blob` download | ❌ | ❌ | +| Nested `File` or `Blob` upload | 🟡 with the [Blob polyfill](#blob-polyfill) | ❌ | -Two things are worth knowing beyond the table: - -- **`expo/fetch` buffers every upload.** It accepts a streamed request body, whether that is an `AsyncIteratorObject` or a `ReadableStream`, but drains it into one buffer before the request starts. Nothing reaches the server until the stream finishes, and the whole payload sits in memory meanwhile. Downloads stream properly. The WebSocket link has no such limit: each chunk is its own frame, sent immediately, so uploads genuinely stream. It is the one place the WebSocket link beats fetch. -- **The WebSocket link handles nested binary worse.** It runs `FormData` through the global `Response` in both directions, and Expo leaves that as React Native's implementation, which can neither read a `FormData` body as a blob nor parse a multipart one. The fetch link only has the decode half of that problem. +The WebSocket link handles nested binary worse than fetch. It runs `FormData` through the global `Response` in both directions, and Expo leaves that as React Native's implementation, which can neither read a `FormData` body as a blob nor parse a multipart one. The fetch link only has the decode half of that problem. ### Blob Polyfill @@ -147,7 +144,7 @@ Writing multipart works, but only on the fetch link, where `expo/fetch` does the | Link | Nested upload | Nested download | | --------- | ------------------------------------------ | --------------- | -| Fetch | ✅ with the [Blob polyfill](#blob-polyfill) | ❌ | +| Fetch | 🟡 with the [Blob polyfill](#blob-polyfill) | ❌ | | WebSocket | ❌ | ❌ | **The easy fix is to avoid multipart.** Keep files at the root of a procedure's input or output instead of inside an object. Or extend the [RPC JSON Serializer](/docs/rpc/serializer) to carry binary as `base64`. From bf7ffe737056d73de6248f206e74e2e973d002d8 Mon Sep 17 00:00:00 2001 From: Dinh Le Date: Sat, 12 Sep 2026 16:39:11 +0700 Subject: [PATCH 3/3] docs(expo): keep the notes below the feature support table MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Restore the two notes on buffered fetch uploads and nested binary over WebSocket, and tie them to the 🟡 cells in the table. --- apps/content/docs/adapters/expo.mdx | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/apps/content/docs/adapters/expo.mdx b/apps/content/docs/adapters/expo.mdx index 1ffec83e6..9543c6810 100644 --- a/apps/content/docs/adapters/expo.mdx +++ b/apps/content/docs/adapters/expo.mdx @@ -95,7 +95,10 @@ Expo never parses multipart, and the WebSocket link cannot even produce it, whic | Nested `File` or `Blob` download | ❌ | ❌ | | Nested `File` or `Blob` upload | 🟡 with the [Blob polyfill](#blob-polyfill) | ❌ | -The WebSocket link handles nested binary worse than fetch. It runs `FormData` through the global `Response` in both directions, and Expo leaves that as React Native's implementation, which can neither read a `FormData` body as a blob nor parse a multipart one. The fetch link only has the decode half of that problem. +Two things are worth knowing beyond the table: + +- **`expo/fetch` buffers streamed uploads.** It accepts a streamed request body, whether that is an `AsyncIteratorObject` or a `ReadableStream`, but drains it into one buffer before the request starts. Nothing reaches the server until the stream finishes, and the whole payload sits in memory meanwhile. Downloads stream properly. The WebSocket link has no such limit: each chunk is its own frame, sent immediately, so uploads genuinely stream. It is the one place the WebSocket link beats fetch. +- **The WebSocket link handles nested binary worse.** It runs `FormData` through the global `Response` in both directions, and Expo leaves that as React Native's implementation, which can neither read a `FormData` body as a blob nor parse a multipart one. The fetch link only has the decode half of that problem, which is why nested uploads still work there. ### Blob Polyfill