https://overreacted.io/progressive-json/ overreactedby Dan Abramov Progressive JSON May 31, 2025 Pay what you like Do you know about Progressive JPEGs? Here's a nice explanation of what a Progressive JPEG is. The idea is that instead of loading the image top to bottom, the image instead is fuzzy at first and then progressively becomes more crisp. What if we apply the same idea to transferring JSON? Suppose you have a JSON tree with some data: { header: 'Welcome to my blog', post: { content: 'This is my article', comments: [ 'First comment', 'Second comment', // ... ] }, footer: 'Hope you like it' } Now imagine you want to transfer it over the wire. Because the format is JSON, you're not going to have a valid object tree until the last byte loads. You have to wait for the entire thing to load, then call JSON.parse, and then process it. The client can't do anything with JSON until the server sends the last byte. If a part of the JSON was slow to generate on the server (e.g. loading comments took a slow database trip), the client can't start any work until the server finishes all the work. Would you call that good engineering? And yet it's the status quo--that's how 99.9999%^* of apps send and process JSON. Do we dare to improve on that? * I made it up --------------------------------------------------------------------- Streaming JSON We can try to improve this by implementing a streaming JSON parser. A streaming JSON parser would be able to produce an object tree from an incomplete input: { header: 'Welcome to my blog', post: { content: 'This is my article', comments: [ 'First comment', 'Second comment' If you ask for the result at this point, a streaming parser would hand you this: { header: 'Welcome to my blog', post: { content: 'This is my article', comments: [ 'First comment', 'Second comment' // (The rest of the comments are missing) ] } // (The footer property is missing) } However, this isn't too great either. One downside of this approach is that the objects are kind of malformed. For example, the top-level object was supposed to have three properties (header, post, and footer), but the footer is missing because it hasn't appeared in the stream yet. The post was supposed to have three comments, but you can't actually tell whether more comments are coming or if this was the last one. In a way, this is inherent to streaming--didn't we want to get incomplete data?--but this makes it very difficult to actually use this data on the client. None of the types "match up" due to missing fields. We don't know what's complete and what's not. That's why streaming JSON isn't popular aside from niche use cases. It's just too hard to actually take advantage of it in the application logic which generally assumes the types are correct, "ready" means "complete", and so on. In the analogy with JPEG, this naive approach to streaming matches the default "top-down" loading mechanism. The picture you see is crisp but you only see the top 10%. So despite the high fidelity, you don't actually see what's on the picture. Curiously, this is also how streaming HTML itself works by default. If you load an HTML page on a slow connection, it will be streamed in the document order:
Welcome to my blog

This is my article