Skip to content(if available)orjump to list(if available)

JSX over the Wire

JSX over the Wire

92 comments

·April 15, 2025

esprehn

The big challenge with the approach not touched on in the post is version skew. During a deploy you'll have some new clients talk to old servers and some old clients talk to new servers. The ViewModel is a minimal representation of the data and you can constrain it with backwards compatibility guarantees (ex. Protos or Thrift), while the UI component JSON and their associated JS must be compatible with the running client.

Vercel fixes this for a fee: https://vercel.com/docs/skew-protection

I do wonder how many people will use the new React features and then have short outages during deploys like the FOUC of the past. Even their Pro plan has only 12 hours of protection so if you leave a tab open for 24 hours and then click a button it might hit a server where the server components and functions are incompatible.

yawaramin

Wouldn't this be easy to fix by injecting a a version number field in every JSON payload and if the expected version doesn't match the received one, just force a redirect/reload?

tantalor

Thrashing is why

mattbessey

This was a really compelling article Dan, and I say that as a long time l advocate of "traditional" server side rendering like Rails of old.

I think your checklist of characteristics frames things well. it reminds me of Remix's introduction to the library

https://remix.run/docs/en/main/discussion/introduction > Building a plain HTML form and server-side handler in a back-end heavy web framework is just as easy to do as it is in Remix. But as soon as you want to cross over into an experience with animated validation messages, focus management, and pending UI, it requires a fundamental change in the code. Typically, people build an API route and then bring in a splash of client-side JavaScript to connect the two. With Remix, you simply add some code around the existing "server side view" without changing how it works fundamentally

it was this argument (and a lot of playing around with challengers like htmx and JSX like syntax for Python / Go) that has brought me round to the idea that RSCs or something similar might well be the way to go.

Bit of a shame seeing how poor some of the engagement has been on here and Reddit though. I thought the structure and length of the article was justified and helpful. Concerning how many peoples' responses are quite clearly covered in TFA they didn't read...

hcarvalhoalves

> REST (or, rather, how REST is broadly used) encourages you to think in terms of Resources rather than Models or ViewModels. At first, your Resources start out as mirroring Models. But a single Model rarely has enough data for a screen, so you develop ad-hoc conventions for nesting Models in a Resource. However, including all the relevant Models (e.g. all Likes of a Post) is often impossible or impractical, so you start adding ViewModel-ish fields like friendLikes to your Resources.

So, let's assume the alternative universe, where we did not mess up and got REST wrong.

There's no constraint saying a resource (in the hypermedia sense) has to have the same shape as your business data, or anything else really. A resource should have whatever representation is most useful to the client. If your language is "components" because you're making an interactive app – sure, go ahead and represent this as a resource. And we did that for a while, with xmlhttprequest + HTML fragments, and PHP includes on the server side.

What we were missing all along was a way to decouple the browser from a single resource (the whole document), so we could have nested resources, and keep client state intact on refresh?

yawaramin

And this is exactly what we get with htmx.

modal-soul

I like this article a lot more than the previous one; not because of length.

In the previous article, I was annoyed a bit by some of the fluffiness and redefinition of concepts that I was already familiar with. This one, however, felt much more concrete, and grounded in the history of the space, showing the tradeoffs and improvements in certain areas between them.

The section that amounted to "I'm doing all of this other stuff just to turn it into HTML. With nice, functional, reusable JSX components, but still." really hit close to how I've felt.

My question is: When did you first realize the usefulness of something like RSC? If React had cooked a little longer before gaining traction as the client-side thing, would it have been for "two computers"?

I'm imagining a past where there was some "fuller stack" version that came out first, then there would've been something that could've been run on its own. "Here's our page-stitcher made to run client-side-only".

acemarke

Sounds like another one of Dan's talks, "React from Another Dimension", where he imagines a world in which server-side React came first and then extracted client functionality:

- https://www.youtube.com/watch?v=zMf_xeGPn6s

cadamsdotcom

Really like this pattern, it’s a new location of the curve of “how much rendering do you give the client”. In the described architecture, JSX-as-JSON provides versatility once you’ve already shipped all the behavior to the client (a bunch of React components in a static JS that can be cached, the React Native example really demonstrated this well)

One way to decide if this architecture is for you, is to consider where your app lands on the curve of “how much rendering code should you ship to client vs. how much unhydrated data should you ship”. On that curve you can find everything from fully server-rendered HTML to REST APIs and everything in between, plus some less common examples too.

Fully server-rendered HTML is among the fastest to usefulness - only relying on the browser to render HTML. By contrast in traditional React server rendering is only half of the story. Since after the layout is sent a great many API calls have to happen to provide a fully hydrated page.

Your sweet spot on that curve is different for every app and depends on a few factors - chiefly, your app’s blend of rate-of-change (maintenance burden over time) and its interactivity.

If the app will not be interactive, take advantage of fully-backend rendering of HTML since the browser’s rendering code is already installed and wicked fast.

If it’ll be highly interactive with changes that ripple across the app, you could go all the way past plain React to a Redux/Flux-like central client-side data store.

And if it’ll be extremely interactive client-side (eg. Google Docs), you may wish to ship all the code to the client and have it update its local store then sync to the server in the background.

But this React Server Components paradigm is surprisingly suited to a great many CRUD apps. Definitely will consider it for future projects - thanks for such a great writeup!

null

[deleted]

h14h

Excellent read! This is the first time I feel like I finally have a good handle on the "what" & "why" of RSCs.

It has also sparked a strong desire to see RSCs compared and contrasted with Phoenix LiveView.

The distinction between RSCs sending "JSX" over the Wire, and LiveViews sending "minimal HTML diffs"[0] over the wire is fascinating to me, and I'm really curious how the two methodologies compare/contrast in practice.

It'd be especially interesting to see how client-driven mutations are handled under each paradigm. For example, let's say an "onClick" is added to the `<button>` element in the `LikeButton` client component -- it immediately brings up a laundry list of questions for me:

1. Do you update the client state optimistically? 2. If you do, what do you do if the server request fails? 3. If you don't, what do you do instead? Intermediate loading state? 4. What happens if some of your friends submit likes the same time you do? 5. What if a user accidentally "liked", and tries to immediately "unlike" by double-clicking? 6. What if a friend submitted a like right after you did, but theirs was persisted before yours?

(I'll refrain from adding questions about how all this would work in a globally distributed system (like BlueSky) with multiple servers and DB replicas ;))

Essentially, I'm curious whether RSCs offer potential solutions to the same sorts of problems Jose Valim identified here[1] when looking at Remix Submission & Revalidation.

Overall, LiveView & RSCs are easily my top two most exciting "full stack" application frameworks, and I love seeing how radically different their approaches are to solving the same set of problems.

[0]: <https://www.phoenixframework.org/blog/phoenix-liveview-1.0-r...> [1]: <https://dashbit.co/blog/remix-concurrent-submissions-flawed>

skydhash

Everything old is new again, and I'm not even that old to know that you can return HTML fragments from AJAX call. But this is worse from any architectural point view. Why?

The old way was to return HTML fragments and add them to the DOM. There was still a separation of concern as the presentation layer on the server didn't care about the interface presented on the client. It was just data generally composed by a template library. The advent of SPA makes it so that we can reunite the presentation layer (with the template library) on the frontend and just send the data to be composed down with the request's response.

The issue with this approach is to again split the frontend, but now you have two template libraries to take care of (in this case one, but on the two sides). The main advantages of having a boundary is that you can have the best representation of data for each side's logic, converting only when needs. And the conversion layer needs to be simple enough to not introduce complexity of its own. JSON is fine as it's easy to audit a parser and HTML is fine, because it's mostly used as is on the other layer. We also have binary representation, but they also have strong arguments for their use.

With JSX on the server side, it's abstraction when there's no need to be. And in the wrong place to boot.

danabramov

It feels like you haven't read the article and commented on the title.

>The old way was to return HTML fragments and add them to the DOM.

Yes, and the problem with that is described at the end of this part: https://overreacted.io/jsx-over-the-wire/#async-xhp

>JSON is fine [..] With JSX on the server side, it's abstraction when there's no need to be. And in the wrong place to boot.

I really don't know what you mean; the transport literally is JSON. We're not literally sending JSX anywhere. That's also in the article. The JSON output is shown about a dozen times throughout, especially in the third part. You can search for "JSON" on the page. It appears 97 times.

skydhash

From the article:

  Replacing innerHTML wasn’t working out particularly well—especially for the highly interative Ads product—which made an engineer (who was not me, by the way) wonder whether it’s possible to run an XHP-style “tags render other tags” paradigm directly on the client computer without losing state between the re-renders.
HTML is still a document format, and while there's a lot of features added to browsers over the year, we still have this as the core of any web page. It's always a given that state don't survive renders. In desktop software, the process is alive while the UI is shown, so that's great for having state, but web pages started as documents, and the API reflects that. So saying that it's an issue, it's the same as saying a fork is not great for cutting.

React is an abstraction over the DOM for having a better API when you're trying not to re-render. And you can then simplify the format for transferring data between server and client. Net win on both side.

But the technique described in the article is like having an hammer and seeing nails everywhere. I don't see the advantages of having JSX representation of JSON objects on the server side.

danabramov

>I don't see the advantages of having JSX representation of JSON objects on the server side.

That's not what we're building towards. I'm just using "breaking JSON apart" as a narrative device to show that Server Components componentize the UI-specific parts of the API logic (which previously lived in ad-hoc ViewModel-like parts of REST responses, or in the client codebase where REST responses get massaged).

The change-up happens at this point in the article: https://overreacted.io/jsx-over-the-wire/#viewmodels-revisit...

If you're interested in the "final" code, it's here: https://overreacted.io/jsx-over-the-wire/#final-code-slightl....

It blends the previous "JSON-building" into components.

whalesalad

to be fair this post is enormous. if i were to try and print it on 8.5x11 it comes out to 71 pages

danabramov

I mean sure but not commenting is always an option. I don't really understand the impulse to argue with a position not expressed in the text.

aylmao

> The main advantages of having a boundary is that you can have the best representation of data for each side's logic, converting only when needs.

RSC doesn't impede this. In fact it improves it. Instead of having your ORM's objects, to be converted to JSON, sent, parsed, and finally manipulated to your UIs needs, you skip the whole "convert to JSON" part. You can go straight from your ORM objects (best for data operations) to UI (best for rendering) and skip having to think about how the heck you'll serialize this to be serialized over the wire.

> With JSX on the server side, it's abstraction when there's no need to be. And in the wrong place to boot.

JSX is syntactic sugar for a specific format of JavaScript object. It's a pretty simple format really. From ReactJSXElement.js, L242 [1]:

  element = {
    // This tag allows us to uniquely identify this as a React Element
    $$typeof: REACT_ELEMENT_TYPE,

    // Built-in properties that belong on the element
    type,
    key,
    ref,

    props,
  };
As far as I'm aware, TC39 hasn't yet specified which shape of literal is "ok" and which one is "wrong" to run on a computer, depending on wether that computer has a screen or not. I imagine this is why V8, JSC and SpiderMonkey, etc let you create objects of any shape you want on any environment. I don't understand what's wrong about using this shape on the server.

[1] https://github.com/facebook/react/blob/e71d4205aed6c41b88e36...

tshaddox

> The old way was to return HTML fragments and add them to the DOM. There was still a separation of concern as the presentation layer on the server didn't care about the interface presented on the client.

I doubt there were many systems where the server-generated HTML fragments were generic enough that the server and client HTML documents didn't need to know anything about each other's HTML. It's conceivable to build such a system, particularly if it's intended for a screen-reader or an extremely thinly-styled web page, but in either of those cases HTML injection over AJAX would have been an unlikely architectural choice.

In practice, all these systems that did HTML injection over AJAX were tightly coupled. The server made strong assumptions about the HTML documents that would be requesting HTML fragments, and the HTML documents made strong assumptions about the shape of the HTML fragments the server would give it.

skydhash

> where the server-generated HTML fragments were generic enough that the server and client HTML documents didn't need to know anything about each other's HTML.

> all these systems that did HTML injection over AJAX were tightly coupled

That's because the presentation layer originated on the server. What the server didn't care about was the transformation that alters the display of the HTML on the client. So you can add add an extension to your browser that translate the text to another language and it wouldn't matter to the server. Or inject your own styles. Even when you do an AJAX request, you can add JS code that discards the response.

rapnie

> Everything old is new again

An age ago I took interest in KnockoutJS based on Model-View-ViewModel and found it pragmatic and easy to use. It was however at the beginning of the mad javascript framework-hopping marathon, so it was considered 'obsolete' after a few months. I just peeked, Knockout still exists.

https://knockoutjs.com/

Btw, I wouldn't hop back, but better hop forward, like with Datastar that was on HN the other day: https://news.ycombinator.com/item?id=43655914

tshaddox

Knockout was a huge leap in developer experience at the time. It's worth noting that Ryan Carniato, the creator of SolidJS, was a huge fan of Knockout. It's a major influence of SolidJS.

kilroy123

I was a big fan of knockoutjs back in the day! An app I built with it is still in use today.

jauntywundrkind

Dan Abramov (author) also recently wrote a related post, React for Two Computers.

https://overreacted.io/react-for-two-computers/ https://news.ycombinator.com/item?id=43631004 (66 points, 6 days ago, 54 comments)

jonathanhefner

RSC is indeed very cool. It also serves as a superior serialization format compared to JSON. For example, it can roundtrip basic types such as `Date` and `Map` with no extra effort.

One thing I would like to see more focus on in React is returning components from server functions. Right now, using server functions for data fetching is discouraged, but I think it has some compelling use cases. It is especially useful when you have components that need to fetch data dynamically, but you don't want the fetch / data tied to the URL, as it would be with a typical server component. For example, when fetching suggestions for a typeahead text input.

(Self-promotion) I prototyped an API for consuming such components in an idiomatic way: https://github.com/jonathanhefner/next-remote-components. You can see a demo: https://next-remote-components.vercel.app/.

To prove the idea is viable beyond Next.js, I also ported it to the Waku framework (https://github.com/jonathanhefner/twofold-remote-components) and the Twofold framework (https://github.com/jonathanhefner/twofold-remote-components).

I would love to see something like it integrated into React proper.

wallrat

Very well written (as expected) argument for RSC. It's interesting to see the parallels with Inertia.js.

(a bit sad to see all the commenters that clearly haven't read the article though)

android521

Very well written. It is rare to see these kinds of high quality articles these days.

danabramov

Thanks!