# Progressive JSON: stop waiting for the last byte

> Why JSON's all-or-nothing wire format wastes time, and how sending the tree breadth-first with placeholders unlocks progressive loading.
- Title: Progressive JSON: stop waiting for the last byte · Overreacted
- Summary: Why JSON's all-or-nothing wire format wastes time, and how sending the tree breadth-first with placeholders unlocks progressive loading. Send your data tree…
- Keywords: react, rsc, streaming, json, web performance, technology, design, Progressive, stop, waiting, last, byte
- Source: Overreacted — https://overreacted.io/progressive-json
- Read time: 3 min
- Topics: react, rsc, streaming, json, web performance, technology, design
## Borrow the progressive JPEG trick
A progressive JPEG shows a fuzzy version of the whole image first, then sharpens it. Dan Abramov asks what happens if you apply that same idea to sending JSON over the wire.
## A simple blog payload
Picture a tree with a header, a post containing comments, and a footer. It looks like nothing to transfer, and that is exactly where the problem hides.
## JSON is all or nothing
You do not have a valid object tree until the final byte lands. Wait for the whole response, call JSON.parse, then finally start doing work.

> The client can't do anything with JSON until the server sends the last byte.
## One slow query blocks everything
If comments need a slow database trip, the ready header and footer sit in a buffer doing nothing. The client stays idle until the server finishes every last piece.

> Would you call that good engineering? And yet it's the status quo
## Try a streaming parser
A streaming parser can build an object tree from incomplete input. Ask it mid-transfer and you get the header, the post content, and whichever comments have arrived so far.
## The objects come out malformed
The top-level object was supposed to have three properties, but footer is simply absent. Your types no longer describe what is actually sitting in memory.
## You cannot tell what finished
Two comments showed up. Is that the whole list, or the first two of fifty? Nothing in the stream separates pending from done.
## Why nobody streams JSON
Application code assumes types hold and that ready means complete. Naive streaming breaks both, so it survives only in niche corners.

> It's just too hard to actually take advantage of it in the application logic
## Crisp but only the top
This matches top-down image loading. Every pixel you have is perfect, you just have the top ten percent, so you still cannot see what the picture is.
## HTML streams the same way
On a slow connection the browser receives the document in source order. The cutoff point is arbitrary, layout can lurch, and the footer waits even though the server had it ready early.
## In order means in line
Order-preserving streaming turns your response into a queue. The slowest item decides when everything behind it gets to move.

> when we stream things in order they appear, a single slow part delays everything that comes after it
## Send breadth-first instead
Depth-first walks into post, then into comments, and stalls there. Breadth-first ships the outer shell immediately and fills the interior in later.
## Placeholders hold the shape
The shell goes out as header: "$1", post: "$2", footer: "$3". Each marker is a labeled slot that a later row in the stream resolves.
## Rows can arrive out of order
The server can emit $1 and $3 while $2 is still waiting on a database call. The client reassembles by id, not by arrival position.
## Pending becomes a promise
Unresolved slots show up on the client as Promises. Header and footer are real strings right away, and post resolves the moment its row lands.
## Do not over-stream
A separate row only pays off where the work is genuinely slow. With one slow post load and one slow comments load, three chunks is the right split.
## Outline repeats to dedupe
A user object shared by header, sidebar, and footer can be emitted once as $1 and referenced three times. The same trick makes cyclic objects serializable, which plain JSON cannot do.
## This is how RSC works
React Server Components serialize your component tree into exactly this kind of stream, sending props outside-in as each async component resolves on the server.
## Suspense decides the reveal
React refuses to render a hole for a pending Promise. It shows the nearest Suspense fallback instead, so the loading sequence is designed rather than dictated by network timing.

> The data arrives as fast as it can in whatever order the server is ready to send it
## Streaming needs a programming model
Streaming alone does not help if your code cannot handle missing pieces. Abramov's challenge to other tools: if the client waits on the server finishing, or one slow step stalls the rest, you have found the bug.
## Key takeaway

Send your data tree outside-in with placeholders for the slow parts, so one slow query stops holding up everything behind it.