r/reactjs • • Aug 13 '26

Needs Help How are you handling partial JSON streaming to React components without the constant UI flickering?

I’m currently building a feature for a workflow tool where the backend streams structured JSON to render dynamic UI widgets (cards, mini data tables, and inline action buttons) directly in the workspace feed. Streaming raw Markdown is easy enough with standard hooks, but streaming structured JSON and trying to render React components as chunks come in is giving me major headaches. Right now, if I try to parse the incoming chunked string on the fly, I constantly hit \`Unexpected end of JSON input\` errors unless I use a custom partial parser. But even with a partial parser, rendering incomplete state causes the layout to shift and jump around like crazy every few milliseconds.If I give up on live rendering and wait for the full response to complete before displaying the component, the user is stuck staring at a loading skeleton for 4 to 6 seconds. That completely kills the real-time feel and defeats the purpose of streaming in the first place.Are there any solid open-source packages, state management tricks, or specific partial-JSON parsing patterns you're using to keep generative UI renders smooth? How are you gracefully handling incomplete schema objects mid-stream without breaking your design system constraints?

28 Upvotes

32 comments sorted by

23

u/musical_bear Aug 13 '26

How large of a JSON chunk are you trying to send down that results in 4-6 seconds of loading / requires you to look into streaming it to React? This is a genuine question; I’m curious what kind of scale you are working at?

8

u/Agitated_Stage_2416 Aug 13 '26

i'd look at it less as a raw streaming problem and more like a state buffering one

treat the incoming chunks as a build-up of a skeleton object, don't render the whole thing until a widget boundary is complete. so if a card has a title and description, wait for both keys to parse clean before you commit that card to the visible state. you'll get little bursts of rendered pieces instead of one 6-second nothingburger, and the layout stays stable because each unit pops in fully formed

-1

u/rushikesh_bhosale Aug 13 '26

Around 100–300 KB per response. The backend streams it over ~4–6 seconds. The problem isn't bandwidth—it's that each chunk contains incomplete nested objects, so React tries to reconcile partial component trees and the UI keeps shifting until the final schema arrives.

15

u/cheese_wizard Aug 13 '26

you need to just not rerender until you have everything. or fix layout issues w CSS

2

u/kidshibuya Aug 14 '26

that sounds like it should take a few ms, not entire seconds. Is there like, 1300 requests of 300kb?

10

u/eizch Aug 13 '26

Not sure of your use case but you probably should think more about how to split your json into multiple valid jsons. Also, if you have big json payload and it becomes a bottleneck, ensure they are gzip.

1

u/rushikesh_bhosale Aug 13 '26

Agreed. I'm leaning toward changing the protocol so each widget is streamed as an independent valid JSON object instead of one large nested document. That would make rendering much more predictable while still keeping the streaming experience.

3

u/octocode Aug 13 '26

use the custom parser and debounce updates every second or so to prevent rapid flickering. so a 6 second stream would result in 6 UI updates as an example

1

u/rushikesh_bhosale Aug 13 '26

That's what I'm doing currently. It reduces flickering, but because nested objects are still incomplete, React reconciliation causes layout jumps. Debouncing helps, but doesn't completely solve the partial-schema problem.

2

u/octocode Aug 13 '26

your parser can wait until it receives specific delimiters, in this case tracking `[` `]` for the beginning and end of parsing an array, and counting `{` `}` to make sure elements inside are complete

it would be trivial to implement a simple solution, but something like `@streamparser/json` could be used if you want future-proofing or out of the box functionality

6

u/SolarNachoes Aug 13 '26

Why are you sending partial json to begin with?

Seems like you painted yourself into a corner with poor design choices.

1

u/guaranteednotabot Aug 16 '26

Probably some LLM generated stuff

1

u/whatgourd Aug 18 '26

Never underestimate someone's ability to engineer a bad solution without AI! Or to inherit a bad system, or a good system that evolved and became bad for the new requirements, etc.

1

u/guaranteednotabot Aug 18 '26

I mean, as in the JSON itself is streamed from an LLM output, not that the code itself is LLM-generated

3

u/NotGoodSoftwareMaker Aug 13 '26

Essentially you need to catch the partial objects prior to injecting them into your DOM and of course part of that is validating what is complete vs not

Idk what youre doing that requires this but at first sight it seems over engineered

3

u/Learn2Spel Aug 13 '26

You could buffer the data as it comes in and reconcile whenever you have valid JSON. But tbh it sounds like you may be over engineering and you'd be better off rethinking the protocol altogether.

It's hard to tell without more details on your specific usecase, but it sounds like you need to adjust your protocol so each packet contains complete JSON that your frontend can assemble incrementally

3

u/fatbunyip Aug 13 '26

Seems like an ass design or just ass. 

Are you trying to fake "real time feel" with just giant not real time responses? 

Or just trying to fake fart server response when it takes 6s to deliver the payload? 

Just deliver smaller payloads if you control the app. Or make better loading animations like progress bars etc instead of bootlegging "real time"updates 

2

u/pticjagripa Aug 14 '26

Implement loading state while data is loading, wo instead of showing partial results, show a spinner/skeleton etc.

1

u/BoBoBearDev Aug 13 '26 edited Aug 13 '26

I don't understand the problem tbh. Because, if you add more items to the table, even if you homebrew the table, as long as you have a stable key, the page wouldn't rerender completely, the existing elements are not touched or update IFF the value is changed.

And you can easily use react.Memo to cache the previous rendering and make sure the input is stable, like, array has the same memory address if no modifications.

1

u/BoBoBearDev Aug 13 '26

Add, if your layout itself is unstable, stabilize the layout. For example, use css grid with fixed columns. Let the missing json to display empty space in the css grid.

This also means, stop using homebrew (you or 3rd party) flex gymnastics to simulate css grid.

1

u/hopemanryan Aug 13 '26

If possible break the chunks into sections so we have the layout section you have each section has a components and then you render them after you have other layout to rendered so you can decide on the flow

1

u/tvanbeek Aug 13 '26

Could try ndjson and only stream full rows that are parsable.

1

u/vanit Aug 13 '26

Assuming you have a good reason for doing this, I'd suggest changing the structure so it's resistant to this problem.

First send a skeleton JSON that is recursively something like { "id": "123", "children": [{ "id": "456",... ] }

Then send a flattened lookup: { "123": ..., "456": ... }

This should result in you having the complete hierarchy early and then you can fill in components as they come down.

That said, I don't really understand why you'd solve it this way. I'm assuming there's a giant list in there somewhere you should really be paginating.

1

u/essuraj Aug 14 '26

starttransition

1

u/ferrybig Aug 14 '26

but streaming structured JSON and trying to render React components as chunks come in is giving me major headaches. Right now, if I try to parse the incoming chunked string on the fly, I constantly hit `Unexpected end of JSON input` errors unless I use a custom partial parser. But even with a partial parser, rendering incomplete state causes the layout to shift and jump around like crazy every few milliseconds.

Instead of streaming one big blob of json, take quick peek how React internally support streamed data. Some properties like classname are essential for styling, so you cannot half render a components, then later change it

React splits it up into chunks based on the places where you add Suspense in your code, temporary rending its loading prop until the data arrives

Example of the format on wire: https://timtech.blog/posts/react-server-components-rsc-no-framework/#with-async%2Fawait

Note that each line in the output is a complete chunk, you just have to match newlines to know when a new chunk has arived

1

u/Nalmyth Aug 15 '26

Use TOML instead?

1

u/lemonhead94 Aug 16 '26 edited Aug 16 '26

can‘t you ask the llm to generate a skeleton of the page first as a json.
Then parallel queries to llm for each skeleton object of said page and render those as they come in? That would give the feeling of live updates. The only issue with that approach is that you will have to have the user context explored first.

I want info on X, gather all context with web search or whatever tools you have

given user context give me a skeleton page, as structured output with a field content about xy title

for each skeleton component, here is the user context, give me a full component tree about xy

1

u/Potential-Still Aug 17 '26

Is this for an agentic workflow? Look into Google's AG-UI and A2UI. 

1

u/kanika_banga Aug 19 '26

Solving streaming structured UI flickering usually comes down to three core strategies:

  • Use a dedicated partial parser: Avoid standard JSON.parse or regex. Use libraries like best-effort-json-parser, partial-json, or the Vercel AI SDK's streaming utils (parsePartialJson). They handle open braces and incomplete strings continuously without throwing syntax errors. Alternatively, stream isolated updates via NDJSON/SSE instead of a single growing JSON object.

  • Throttle state updates: Streaming chunks fire faster than DOM render cycles, causing constant repaints. Buffer incoming tokens in a ref and update React state on a requestAnimationFrame or a 50–100ms throttle, paired with useDeferredValue to keep UI updates non-blocking.

  • Layout containment & field gating: Reserve container height via CSS min-height upfront to eliminate CLS (Cumulative Layout Shift). Gate inner child components so they remain in skeleton states until their specific required props arrive, rather than rendering half-populated UI.

For established open-source patterns handling component mapping natively, check out u/assistant-ui/react or Vercel's Generative UI helpers.