Hacker News new | past | comments | ask | show | jobs | submit login
React for Two Computers (overreacted.io)
67 points by abraham 28 days ago | hide | past | favorite | 54 comments



> The calls are not yet made—we’re only building a blueprint of those calls:

    <alert>
      <concat>
        Hello,
        <prompt>Who are you?</prompt>
      </concat>
    </alert>
> This blueprint of “potential calls” looks code, but it acts like data. It’s structurally similar to function calls but it is more passive, inert, open to interpretation. We’re yet to send this blueprint to the other computer which will actually interpret it.

You've reinvented Free Monads: https://www.haskellforall.com/2012/06/you-could-have-invente...

That article is a bit dense if you're less familiar with Haskell, but the basic idea is that you can write code that looks like:

    do
      name <- prompt "Who are you?"
      message <- concat "Hello, " name
      alert message
And what actually gets assembled is something resembling a syntax tree of operations, waiting to be interpreted.

You could interpret it locally, or have the interpreter send requests to another computer. And (if such are your desired semantics) the interpreter can short-circuit the whole thing and return an error message if any step fails - similar to how you describe these as being "potential function calls", i.e. calls which as of yet may or may not successfully execute.

Analogously, JavaScript already has a monad-like construct which can achieve something similar:

    async () => {
      const name = await prompt()
      const message = await concat("Hello, ", World)
      const result = await alert(message)
      return result
    }
  
But this is pure code, rather than data that can be interpreted in a custom way - it will always be "interpreted" by the runtime itself. The Haskell example is somewhat of a blend between data and code (it desugars to a tree of closures calling each other in sequence, which you have a lot of control over the execution of).

Your React-style example attempts to make this concept into pure data. Though in some ways, a DSL becoming pure data wraps around and becomes pure code again. You need to parse it, semantically analyze it, and then execute it - in other words, you've invented a compiler.


Thanks for the context! I give Haskell a one-word shoutout in the article as a hint but I really wanted to take the reader on the journey of inventing some of these things themselves.


Interestingly Wat (https://github.com/manuel/wat-js) handles Dynamic Variables, which fit well with serialized code, in that the serialization boundary can be better partitioned into variables you need to serialize (locals etc.), and environmental (dynamic) variables which are subject to their runtime env.

Wat also happens to have a good concurrency model, and a portable bytecode. Worth looking at in this space.


It seems like that the author by using JSX to express code directly as an AST re-invented LISP? See Greenspun's tenth rule[1].

[1]: https://wiki.c2.com/?GreenspunsTenthRuleOfProgramming


Sort of! My impression is that LISP is a little more powerful than what we're building up in the article. I wanted to focus on a few specific aspects (like first-class cross-environment imports and serializing continuations) but I'd ofc expect all of that to be expressible in LISP.


It's fun to theorize about an alternate universe where JavaScript has a LISP-like syntax (Brendan Eich originally wanted "Scheme in the browser"[1], so this isn't so far-fetched!). Indeed, `interpret` sounds like a LISP macro that can "partially evaluate" code (so someone else, ie. the browser can continue to evaluate it).

[1]: https://brendaneich.com/2008/04/popularity/


This is very long, I’ve skimmed over the article. In the end, what I’m trying to understand, is what server components solve? Faster performance? Serving compiled app html on refresh? Why is it so much better than the old school hydration?


Value proposition is “components for server”. But it’s worth to take a step back first.

What is value proposition of react component in comparison to older “mvc-inspired” frameworks? That you can drop a component in your code and call it a day. Don’t need to wire up separate controllers, models and views. Just one thing.

Now, if you want to add “auth” to your app (in react meta framework like next) you need to add component and probably add some routes and inject some middleware. With server components, you just add one component somewhere in the root of your app. Just one thing.

All that over the wire format, suspended data fetching etc, are a result of solving that problem and keeping in mind that those components are slightly different. It takes more work, but in return we can interleave both types of components nearly freely.


Sorry, having trouble understanding the nuance of the auth example (FYI in react I have experience only in small scale projects). What’s the difference in classic client components having a root component validation auth?


Sure! In some methods of authentication, like OpenID, not only your client need to present some UI, but also you need to prepare some endpoints on the backend to handle full flow of authentication. You probably also need to provide some parameters to those endpoints and methods inside.

So again, you want to add one thing “auth”, but need to add code in multiple places in your app. Server components promise to encapsulate that. Idea is that you can grab a component from npm, and it will handle all of that orchestration for you, and component will be your only one interface, so all configuration can be passed as props (of course it will be taken from your env/secret).

The promise is that you can encapsulate inside a components both client and server code.


Ok I probably need to read deeper into server components and look at specific implementation. Your saying that the same component, in the client makes sure that you have a live session (a la JWT in local storage), but in the backend checks that the client requests arrived with the correct credentials?


Kinda? I’m starting to regret using auth as an example! Dan recommended Sam’s talk to you. He explains that with better examples and more clarity than me.


lol gotcha. Thanks for the time!


It might be a contrived example, but I found the example of syntax highlighting a blog post to be a good example of the benefits of RSC.

If you hydrate on the client, you need to run the same exact code on the server and again on the client - however, that means you now might need to bring in a big syntax highlighting library to the client.

With RSC all of that is done server side, so you don't need to ship the syntax highlighting library.


That may be fine for static rendering libraries, but chart libraries for example would have to be pushed to the client as well. So it seems to me that the benefits are so small and network speeds today are so fast, that switching your entire backend stack for that is over complicated to the point of absurdity…


If it's a highly interactive chart that's displaying data small enough to easily be transmitted to and manipulated by the client, I agree that server components might not provide much benefit.

The main use cases where server components provide a large benefit are:

1) Web sites with a very large number of components, but where any one page or session will use a very small (and unpredictable) subset of those components. These are things like social media feeds, where there could be many thousands of types of feed items, but most people will see just a handful of them. This is a tricky problem for code-splitting.

2) Components which require a lot of code to perform a render relative to the size of the rendered output. This is stuff like Markdown renderers and syntax highlighting. This benefit becomes even more obvious if the rendered output is mostly static but does contain some islands of interactivity.


Oh okay, example 1 makes it much more clear. Angular solves it very differently, fetching the data for the component, and the code to render it in parallel using @defer syntax. The client still renders it, but only on demand


@danabramov, would love to hear your stance on that


I didn't really attempt to motivate it from the practical standpoint in the article. The article aims to be more of "reinvent some old ideas from first principles, while explaining some novel parts" kind of thing.

In particular, the callback to Miguel's article about async/await (https://tirania.org/blog/archive/2013/Aug-15.html) is intentional. I'd like to see some parts of my article as making a similar argument for 'use client' / 'use server' and the concept of first-class cross-environment references module system.

And as for practical benefits, I think the main benefit is composition--ability to compose and weave Server/Client building blocks together. Of course there's been myriads of approaches to server/client apps but I've never seen anything with the same compositional properties as RSC. I think Sam's talk (https://www.youtube.com/watch?app=desktop&v=9CN9RCzznZc) makes an amazing argument in favor of that viewpoint so I'll defer to him.


Thanks for the explanation, I understand that wasn’t the point of the article, I’m one step before trying to understand the motivation behind server components :)

Obviously the technology is impressive, basically serializing code over the network.

I’ll look into Sam’s talk.


Author is explaining react server components in a colorful style.

They aren't reinventing or proposing anything new.

Most of the complaints in the comments here are probably due to author not saying "hey we're going to pretend to invent RSC so you get how it works, hop in"


The need to wax philosophical really shows how complex and overengineered RSC is compared to solutions from other ecosystems that are just as effective but far simpler to work with and understand.


And yet the final code is ~150 lines.

https://codesandbox.io/p/sandbox/8dgdz8

What's overengineered here?


I'll add I think you're mistaking the cause and the effect. I'm not trying to sell you on something; I don't give a damn what you use. Like literally, I don't. I'm not personally invested in that. I'm not being paid to work on this or promote it or whatever. But I've been thinking about this stuff for a while and I wanted to write it down on the page. And my thoughts tend to be longwinded because I prefer that style. I think someone will salvage something valuable from my writing if they find it there.


Who said you were selling something?

My point is that the entire approach is architecturally far more complex than is suitable for the vast majority of projects. You have influence and people will make real world decisions to choose unnecessary complexity because of this article.

Why do 150 lines when 10 lines of PHP is functionally equivalent to the user? The user doesn't care about your execution model. They just want some information or to submit a form. Adopting an RSC-like technology makes development more complicated and take longer.

If you're building Figma or something then sure, go nuts with the complicated things. The tradeoff probably makes sense.

For the other 99% of us, this model just gets in the way.


The execution model enables more powerful composition (reusable components whose functionality spans both client and server) which hopefully helps people make things people want faster (the story of our industry). Sam gave this argument a wonderful treatment in https://www.youtube.com/watch?v=9CN9RCzznZc, I don’t think I can make as compelling a case in a comment. But he fully represents my viewpoint on why. If you’re genuinely interested then do check it out.


One of the biggest problems with mixing client and server code like this together is coupling which is one of the reasons that client-server architecture was created to avoid.


I watched the video, and it didn't feel compelling honestly, backend frameworks like symfony have ways to compose client and server side code for years.

There are many ways to mix and match client(js) and server (php) in a composable way without react. Components can be created on the server side as well (mixing html+js+php). Only thing lacking is DX and type checking.


Most other solutions are not composable to the same extent. It’s not enough to be able to mix client/server (although that’s a start). It’s more about it going all the way — for example, the state of the client parts is preserved on refreshes of the server parts around them, the same components can be reused on either side depending on whether you want them to be state-driven or data-driven, state changes are predictably instant, you can stay within the same paradigm while moving code between highly interactive and fully data-driven (no awkward moving stuff “between PHP and JS” when something needs to be “more dynamic” and back when it needs some server-only feature), ability to send closures over the server to the client and back (lets you essentially .bind server actions over the network), all of this is integrated end-to-end (a client button can know precisely how long to display an indicator while the server is data fetching next page and the next client code and CSS are loading), and of course all of this being fully composable (you can nest server stuff into client stuff into server stuff into client stuff and they all retain state and refresh as you would expect).

If you don’t see what’s new about this I encourage you to look deeper because this definitely isn’t what backend frameworks have been doing for years so you are missing something.


I feel like it's a great bellwether that react has jump shipped as a viable way to build applications if the mindshare of maintainers/core devs/influencers are pushing in this direction.


This reads like trying to find meaning where there is none.


sorry :’(


I really want to try and read this thoroughly, can you perhaps add a clickable table of contents? It's really long and easy to get lost in.


If you read the introduction, it points out that this is not really a substitute for Dan's underlying talk (which is linked/embedded), but merely an addendum of notes for it. The talk is quite digestible imho.

> I’ve given up on the idea of converting this talk into a post form, nor do I think it’s possible. But I wanted to jot down a few notes that are complementary to the talk. I’m going to assume that you have watched the talk itself. This is just the stuff that wasn’t coherent enough to make the cut—the loose threads I couldn’t tie together.


> If you read the introduction, it points out that this is not really a substitute for Dan's underlying talk (which is linked/embedded), but merely an addendum of notes for it. The talk is quite digestible imho.

Thanks, I must have skipped that part. It's hard for me to sit down watch 30 minutes of a video, so I like reading text more, but I will try to watch it tomorrow.


I've added clickable anchors for the headings so that it's at least possible to click on the last one you've read and thus stay in the same place. I think making an actual TOC is a bit much for my taste. I do hear it's not ideal for all use cases.


What I've gathered from this: It's an explantion of why serializable closures are a way to solve/tackle the problem of hydration, etc. Hence the compiler technology in newer react.


The selling point looks to be this: you get all the advantages of server side rendering but with the flexibility of react client side components as well. Being able to just use react components for composability on the server side is pretty nice.


this is the most brilliant piece of writing i've ever seen -- reminds me of Godel, Escher, Bach.

Would love recommendations for other things like this where the style is one of discovery!! (like math textbooks that make you feel like you invented something?)


Haha thanks, I know it's not everyone's cup of tea but I appreciate kind words, and GEB is too obvious of an inspiration. I would highly recommend Terence Tao's Analysis textbook. It's maybe a bit too rigorous/formal (and maybe doesn't quite describe discovery because it develops very standard theory) but it builds in a very pleasant way. If you're interested in Lean, some of Kevin Buzzard's writing at https://xenaproject.wordpress.com/ is similarly pleasant (e.g. https://xenaproject.wordpress.com/2021/04/18/induction-on-eq...).


I seem to recall what the parent describes when taking your course on redux @ Egghead long, long ago, where I think you went through creating a simplified version of redux? Such a great way to gain a deeper understanding of something that, on the surface, seems very complex. Thank you for making that course free :)


Why briliant ??


Author invents a programming language where the code is server-side JSX tags ("Early World") and the runtime evaluation is divided into as multiple lazily-evaluated stages ("Late World") in the browser.

Author unfortunately fails to justify or provide a demonstration that justifies the increased complexity over current methodology.

Interesting exploration of an unexplored space, but should be more concise (and use either better or no attempts at humour).

> In the Early world, you dissolve all the Early Components with interpret. This gives you a string that represents how to finish the computation in the Late world: [code]

> In the Late world, you parse that string, load the references, and then dissolve the Late Components with interpret. That leaves you with a tree of Primitives: [code]

> Finally, those Primitives are ready to be turned into DOM or some other format: [code]


Author isn't inventing a new language. They are "inventing" react server components. It's not unexplored, they are teaching the reader how server side rendering in react works.


Even though already implemented, still valid points.


Really suggested to watch the talk / video that this is for: https://www.youtube.com/watch?v=ozI4V_29fj4


This article is amazing. I think any engineer outside of programming language builders would get something incredibly useful for it.

Not enough engineers engage with the concept of code and programming and as an industry we suffer for it. The beginning of this post does a phenomenal job of simplifying very basic but high level concepts as “what is a function” “what is a tag”.

99% or programmers don’t think about these things like this, and so get confused when these building blocks are manipulated and presented in seemingly strange ways like React components or server components or whatever. By breaking down these concepts (functions, blueprints) and having rebuilding them with simple definitions it allows the reader to start their mental model fresh and go from there.

This is a masterclass in technical communication.

Who gives a shit if it’s long. In fact I’m glad it’s long because every sentence is gold. These are deep subjects and foundational to programming and so ya, talking about them like this can take a few words.

99% of people who take the time to really read and process this post will come away as noticeably improved developers. That kind of bang for the buck is rare!


This is unusual for Dan he usually writes very well. This time not.


What do you dislike? I wrote it a bit more like how I like to think about it, so maybe it's a bit more "for myself" than my past writing.


(not OP)

After reading the post, and re-reading the opening, it almost seems they are about two different posts. e.g. "Jot down a few notes" vs actually going meta with yourself in the middle about how the post isn't even halfway. That doesn't really make a lot of sense.

That you then expect the reader to want to read all this _after_ already having watched the video seems a bit gratuitous? Perhaps it would've been better built as something interactive or graphical so you wouldn't need to explain as much of the mechanics.

I did find it interesting, but had to lol at `{fn, args}`: I've spent the last few years working on Live, a not-React run-time, which has exactly that type for its deferred calls. It was inspired by the same idea of treating JSX tags as just `fn(...args)` in drag.

I've always been curious to hear your opinion on Use.GPU / Live. There is a keynote video linked here https://usegpu.live/ if you're interested.


Oh! I didn’t expect the reader to want anything. :) When I started writing the post, I genuinely thought it would be “a few notes”, especially since I had a writer’s block for this topic for almost two years. Then it just kept on going. I know the beginning understates what it ended up being but I thought that mismatch was funny so I kept it. Maybe our sense of humor is slightly different.

Re Live, I actually don’t know anything about it! Unfortunately I also don’t know anything about graphics programming so it might be tricky for me to evaluate. I’ll check it out though.


Haven’t got through the whole article yet. The beginning was slow for me, but it did start to get interesting quickly.


Tiktok has rotted my brain for this length of article


If it's this length but about the history of S3, I can easily stay engaged for each new story. When it's a bunch of hypothetical syntaxes that will get thrown away, I'm only staying if I entered the article with the purpose of "I want to understand React Server Components".




Consider applying for YC's Summer 2025 batch! Applications are open till May 13

Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: