Hacker News new | past | comments | ask | show | jobs | submit login
Making sense of React Server Components (joshwcomeau.com)
118 points by joshwcomeau on Sept 6, 2023 | hide | past | favorite | 164 comments



The last decade of web dev can be summed up as:

– A bunch of smart Facebook engineers with unlimited funding decided to mess around and see just how much they could upend all the existing web paradigms, before finally concluding that conventional wisdom and the traditional server-side rendering model exist for a reason and make sense.

– The entire software industry decided that every new experiment by this above group was worth ripping out and rewriting their entire codebase over, whether it made sense for their needs or not and whether it actually made their product better or not.

I have not been too deep into the weeds of it myself but have been interacting with FE teams at my company enough to see what a charade it all is.

"This React thing is cool, and everyone is talking about it. Let's move some of our existing JS components over to it and see how things turns out."

"We need to do a top-down rewrite of the entire app in React."

"We're going to need the infra team to set up a GraphQL API layer for us. It can't be that hard, right?"

"Hmm, initial page loads are too slow. Hey infra team, can you set up a render farm of Node.js servers so we can do SSR?"

"Whoa, functional components are cool! We need to do another top-down rewrite and get rid of all the silly classes."

"We have a small team dedicated to investigating server components, and if the prototype looks good then we'll switch over to Next.js and rewrite our FE + API stack."

I'm sure my company wasn't the only one that caught this virus. I shudder to think just how much money and productivity has been wasted industry-wide over the obsession with this framework.


I don’t think you could be more off, honestly. React revolutionized frontend development and made it dramatically better. Server side rendering for React has existed since very early in its lifetime.

The rest of the snark looks silly in context of the above two points.


But haruff harruf everything is stupid and I don't see any difference in what I can do with the web now vs 15 years ago haruff haruff


Doing Web related stuff since 1998, the only revolution is cargo cult from FB stuff, while refusing to support Web components like all other frameworks.


I do agree with most of this. I work with React on a daily basis and, while it has made things much easier than using something like jQuery or writing vanilla Javascript, I do think they're adding features that is making it more complicated to use. I still can't wrap my head around NextJS and some of the things it is doing. I prefer not to use the new features at this point.


I still think one way data flow is profound for complex frontend UIs. The idea of making something as complex as Facebook ad manager without it sounds true nightmare level

I personally love vue and single page templates (I hate jsx)

SSR is stil a good option i think for complex apps


People were doing SPAs way before react came around (and it took a few years for it become popular), I myself had a jQuery-based SPA a few years before React was released and when I saw React I was like: "this solves all of the problems my codebase has"

React was more of a replacement for jQuery/BackboneJS/MooTools/Angular/etc than a replacement for PHP/JSP/ASP/etc


Don't forget "functions that do totally normal things now need to be rewritten as a hook".


If you are writing functions that do totally normally things as hooks, you are indeed doing it wrong.

Hooks are for stateful code.


Yeah I think that’s their point. People rewriting stateless functions as hooks because “hooks are cool” before they actually understand what they do.

Easiest way I put it to my team was that in order to qualify as a hook, it must consume a hook.


this is the single biggest problem with React hooks, people overusing useEffect:

  const [sortedValues, setSortedValues] = useState([])
  useEffect(() => {
    setSortedValues(values.sort())
  }, [values])


It is unfortunate that useEffect is too powerful of a escape hatch that people forget it IS a escape hatch and should be avoided

To be honest I much prefer hooks than old class-based state manipulation, but it does require deeper understanding of the React, JS and state to get right.


What's the actual solution for that? I've only used Vue and Solid where "computeds" functions fall out of how signals or reactivity work, not sure how it works in React-land. Do you just leave it as a plain function and rely on React calling on your component?


Ignoring that sort mutates the initial array which is a bug in that example, you would just sort it in the function body. If the sort takes a lot of time or the array is big and you don't want to create garbage, you memoize it with useMemo.


See Sitecore fully embracing NextJS, moving away from .NET, with Vercel sponsored keynotes.


The more features React adds the less I end up using React. The new additions are just so inelegant, and don't fit my brain. The function useEffect has so many caveats and such a terrible API. A magic "use client" string at the top of a file is just ridiculous. React code is nearly impossible to understand and write without extensive external documentation now. I feel sad for the future developer that has to modify this type of code ten years from now; I hope Google is good at searching decade old documentation to figure out what the hell is going on in old React code.


This is how it always goes. JQuery was a response to browsers having poor standardization. Then it became a larger and larger ecosystem, and the code owners started getting megalomaniacal (I'm sorry, "live" is deprecated, please us the function "on" which does the exact same thing, we just liked the name better, thank you for complying). It became an ecosystem. Every 3rd party library was designed to integrate with JQuery. Soon there was JQuery UI, a UI library for adding modal dialogs and tooltips.

And then someone realized that browsers had long since reached standards parity, and javascript engines were getting really fast, and re-rendering the DOM was GPU-accelerated, and we just needed a simple little library. And Angular and React were born (and Ember but we don't speak of satan in this house).

React feels like it's following the exact same trajectory. It was created out of necessity, it was the best framework for a given point in time, but it's being stretched to handle the changing landscape, and it's feeling very kludgey. I expect that soon, very soon, someone will release the new paradigm. Probably just as soon as WASM is able to manipulate the DOM without JS hacks, which has been on the roadmap since before there was a road.


Agreed. IMO, it all started with Hooks. Things really went downhill from there. useEffect is terrible! Example, how easy it to forget the dependency array?! Because of that, you have to setup linting rules to fix the poor API provided by that hook...


HN is a funny place.

A useful feature that an insane majority of React devs have come to prefer is "downhill."

Kids these days.


I think this is an artifact of HN's voting system. Most readers probably don't have enough karma to even downvote. For the ones that do, guideline discourage downvoting for comments that you disagree with.

And I can see why the majority of HN readers would upvote the top level comment. Particularly since it's a popular re-hashed opinion.

Top comments aren't a good survey of the opinions of a specific community for that reason.


Many folks were criticizing hooks when they were introduced.

Of course React devs will "prefer" it; they are the idiomatic way to adding state/triggering effects in React.


As someone who used to use React and now has to grapple with an in-house web framework: hooks are right on the money and offer tools backing some of the best devex in web right now.


I've been using React and writing it professionally since early days before they even supported using ES6 classes. I have also answered questions for years on one of Reacts largest discord communities.

Hooks were nice when they came out, but woefully under-documented. The fact that decent docs for hooks first development came out literally just this year when hooks have been out for years is kind of a shame.

As opposed to useEffect, most other hooks are fairly straightforward, but the fact it years of articles upon articles of mistakes and documentation to explain useEffect to both experienced and non-experienced devs alike shows that it really wasn't that great of an API for end users.

In fact the most common topic nowadays is how much you need to avoid useEffect, especially if you're not a library developer. None of this was documented/mentioned when it first came out.

Yes it does enable powerful workflows, but plenty of other frameworks/libraries handle the same thing in a nice way with much less drawbacks than react. (ex: automatic dependency tracking, etc)

The number of times I have to tell people to stop putting things in effects at all, or that the reason something isn't working is a stale callback due to missing dependencies in my opinion shows that its not really a good API for the average dev to be using.


Your last paragraph rang so true. Webpack will tell you that you are missing a dependency and ask you to include it or remove it. I feel useEffect is a fine api. It’s not the apis fault if I shot myself in the foot with it.


There is zero functionality that has been removed from class-based components.

Devs use hooks primarily because hooks are better.


> There is zero functionality that has been removed from class-based components.

No, but the React ecosystem has almost collectively migrated away from class-based components or deprecated support for them.

> Devs use hooks primarily because hooks are better.

You avoid much of the dependency array issues by using class components. But you have to deal with `this` and it's significantly more keystrokes. It's more of a tradeoff in my opinion.


Well yes that's exactly what I said: it had been very very widely adopted


I don't see where you said that. It sounded to me like you were insinuating that hooks are inherently better than class components, and that zero functionality has been removed from class components, they are still viable (but they have largely been left in the dust by most popular libraries).

Also, devs don't use hooks primarily because they are better. They use them because they are idiomatic React in 2023.


> zero functionality has been removed from class components

yes, exactly!

> They use them because they are idiomatic React

right, and do you think it'd be easy for a feature to become idiomatic if it wasn't an improvement over the previous patterns?


That's akin to saying people don't like Taylor Swift's music because it is good, they like it because it is popular.

:hmm:


It is not akin to saying that. Please argue in good faith [1]. Writing idiomatic React code makes a lot of practical sense (easier-to-find examples e.g. in the docs, wider library support, onboarding newbies). You can choose to use hooks while acknowledging the tradeoffs from class components.

[1] https://news.ycombinator.com/newsguidelines.html


I find it's mostly the kids who like hooks, and the older devs who've been around since jQuery or further back, who dislike them.


I'm 49 and started with the Commodore 64, and function based components with hooks are so much cleaner than the old class based components, it's unbelievable.

The classes had closely related functionality spread out over several different methods making them hard to understand at a glance, function based components are very concise.

I think people who dislike hooks tend to overuse useEffect (it's rarely needed) and to do too much inside components rather than inside custom hooks.


For what it's worth, that's not at all my experience on the teams I've worked with. I was around when hooks were released and it was an immediate and significant improvement to our team at the time.


My experiences with jQuery are literally the exact reason why I like React.

I strongly suspect most HNers who are overly critical of React (and/or general web framework complexity) have never had to write and then maintain a large web application in jQuery.


JQuery is just a hammer. Its not a framework. It does provide utilities like selectors, events, triggers, dom manipulation etc. React is a component based framework. It contains pages, routes, state management, caching etc. You cannot compare those two. In a way jQuery is low level just above vanilla. Wheras react has a minimum of 500 execution layers. Thats why its slow and painful in many cases.


To be pedantic, React is a library, and doesn't provide page abstractions, routes, or state management outside useState and useReducer. It also doesn't have very strong opinions about how you structure your application (what a framework does). That's what Next.js is for.

I agree with your overall sentiment though.


> It contains pages, routes, state management, caching etc.

React does not have routes or caching. It has about as much management as jQuery.


Not talking about React, I’m talking about effects. Two very different things. I love React, I don’t love hooks or effects.

I don’t think application state belongs inside the rendering machine.


Wait until they find componentDidUpdate and the cascade of if statements to handle what useEffect does very elegantly.


> HN is a funny place.

Full of (ex?) React devs. There are way better things out there now if you are starting a project from zero.


In my experience, a lot of people don’t actually know how to use hooks correctly and find them frustrating as a result. It depends on what you’re used to, but I find that class based components leave too much room for scattered logic. Let’s not also forget those who abused `shouldComponentUpdate`.


Hooks felt like a big make-work project to let folks claim high-impact commits on a project that didn’t really need any of those, anymore.


What does this expression mean, make-work project?


Digging a hole and filling it back up again. Work for the sake of having something to do.


Ah I understand it now thanks. Not an uncommon thing in many places!


Every other framework not only has its own implementation of hooks, but notably: every one of them is faster, smarter, easier to write, or a combination of all three.

I agree that useEffect is bad.

But there are other SPA frameworks that do things better. In fact, just about any other framework does things better.


In your estimation, what framework has best in class hooks?


Some alternatives are:

Vue3/composition API -- probably the simplest if you're coming from React. Definitely feels like it was inspired by hooks.

Svelte/store -- svelte has a term called 'hook' but it's unrelated. The equivalent would be Svelte's store.

The other frameworks use something called 'Signals' (Preact, Qwik, Solid, Elm and even Angular/backbone though I don't personally have in depth knowledge of all of these).

IMO, I would prefer Svelte if you are adventurous, or Vue if you like React but just want something like 'better React'.


I mean, Svelte/store is something completely different though, isn't it? it's more akin to Jotai/Zustand/Redux and friends than it is to hooks. If you had to compare Svelte with React, the thing "closest" (while stretching that word very much) to hooks would be the `$` reactivity things Svelte does? Because if you want a state management solution, react has that as well (many, in fact). Just not built in, and a lot of them work really well


You dont have to specify the dependency array, it’s an optional optimisation.


Effects are not required to be idempotent, so it's not just an optimisation. React documentation has an example where the effect establishes a network connection. Even if there's time to connect between renders, the remote system doesn't necessarily handle such short-lived connections.


This isn’t quite true. If your effect depends on state, that state must be included as a dependency.

This is easy to miss, so you install an eslint plugin to warn you about missing dependencies.

This causes the opposite problem - adding dependencies unnecessarily to silence the warning. Then you need to ensure those dependencies are stable to avoid running the hook on every render. Or ignore the warning with a comment, and you’re back to square one.

It’s all too easy to write an effect that either doesn’t run when it should, or runs when it shouldn’t. This is especially true for inexperienced developers.


I switched to React when jQuery (and particularly storing state in the DOM and lots of custom events called) became a PITA.

I don't feel that React has reached that tipped point yet, but when it does I'm hoping we'll all move to Sveltekit or something like it.


I despise almost every single web framework written in the last decade. They always feel like byzantine monoliths of complexity built from tons of layers over existing libraries. They invent tons of concepts, always require patching, and have an army of sycophants who believe building a web framework is basically rocket science and only a select few are ever capable of such a feat.

SvelteKit is the first piece of web tech that I actually enjoy using. It’s the rare piece of tech that knows its lane, stays in it, and excels at it. It is marked by fewer features that work well together vs lots of features that sort of work well together.

You can read the docs in an hour or two and then start to get an understanding of what it’s doing.

Basically, it feels like it was made by someone who really understands the medium, thought deeply about how to distill that, and iterated to improve on it. (I get same vibes from Vue, but don’t have much experience there)


I remember when HN said that about React. Lololol


Totally fair comment. Time will tell.

Edit: I'm just happy to find something that I like to make web stuff in. Now I can make some apps and actually enjoy the process.


How do devs generally feel about Vue 3?

So far it seems really amazing even for the most complex apps. I’ve built 5 nontrivial apps with 2 being 2+ year projects

I haven’t found a “ceiling” yet


> The function useEffect has so many caveats and such a terrible API.

Really? What caveats?

The only maybe oddity that I can think of is that if the deps array is omitted it always runs.

What API would you prefer? Something more like signals?


This may be a personal thing, but I really like reading and writing code that feels like it's telling a story. That is, operating sequentially (this happens, then this happens, then this other thing, etc).

useEffect breaks the sequential nature of your code and makes it feel like you're reading a script from a Quentin Tarantino movie.


Agreed, but non-sequential execution is the nature of event-driven, async non-blocking code. Event-driven as Web Applications (not Websites!) mostly deal with events (user input or network events). As long as the JavaScript VM and the DOM uses non-blocking async models no framework - being it Vue, Angular, React or whatnot - will ever change that.

Sequential, blocking modells like i.e. batch ETL jobs are just not the domain of the browser.


> As long as the JavaScript VM and the DOM uses non-blocking async models

The DOM is almost entirely sync, all the operations on it are blocking operations (including things like replacing an entire document with innerHTML).


While not strictly on "the DOM", a lot of modern APIs are within the browser's JS VM (Notification, Camera, Audio, QR scanning, etc.) and all of them are async to the core. My argument therefore stands.


It does not, since most of those APIs are not directly called from the framework. What react and most other JS frameworks do is DOM modification (which is clearly sync) and delegate events (which is usually done via callbacks or binding).


THAT'S THE BEAUTY OF IT!

Thing about async/await.

  const a = await doThing();
  const b = await doOtherThing();
The order of the written code and the order of the executed codes are two different things. Being able to write code in proximate/logical order is a enormous relief, even if things don't actually happen like that.

Same with hooks.


To be fair I wasn't very clear, but what I meant was like this:

  function SomeComponent({ someProp }) {
    function doStuff() {
      // do stuff
    }

    useEffect(doStuff, [someProp])
  }
This trivial example is easy to read, but as more state and effects get added to a component, it quickly becomes fairly cumbersome to figure out the exact order in which your code runs.


They're side effects, by the very nature of them, if you're putting them in your code you shouldn't care which order they run.


Not useEffect specifically, but wiring something very stateful like a map library into the React rendering paradigm while keeping the state management in React via useState etc. can be very difficult to do so in a performant way.

I recalling using a lot of useRef to manually tune when to re-render, and in an ideal world I shouldn't be this granular about it.

You can certainly use one of the many React map wrapper libraries to lessen the pain, the pain is still very real, just dealt with by someone else.


But what’s better? In the end you need control over stuff, it’s almost like people want frontend to be magically easy but unfortunately there’s just the reality that either you have high abstraction but less control, or low abstraction and more control (more code, more gotchas, etc) until you get a smart optimizing compiler ofc.

Hooks imo are great, as they are pretty explicit and low level.

Nowadays signals are the new kid on the block, though mobx and similar have existed since forever in react land, but they have their own crazy edge cases like reactive loops and implicit behavior.


I like Svelte. It embraces the fact that UI is stateful and doesn't try to pretend it isn't.


I do too, I made this [0] 3 years before Svelte existed. But I don't think the "pretend it isn't" makes any sense, I actually like React more than Svelte for many reasons, but magic compilers and templates are two big ones.

[0] https://www.youtube.com/watch?v=HHTYHm6qLFY&t=11s


Thats a cool project.

>But I don't think the "pretend it isn't" makes any sense

Well, let me expand a bit more. React's pitch is that view is a function of state, and essentially designed around the idea of an immutable state which re-renders everything when changed. It then offered some APIs to allow fine-tuning of rendering via hooks which either ties with data changes or some rendering life-cycle. To me the API screams of stubborn refusal to let go of the "view as a function of state" mantra: When you encounter a scenario where it breaks the mantra, lets add another lever to handle it. This lever also has to be pulled by you the developer, and it is up to you to know when to do it.

>I actually like React more than Svelte for many reasons, but magic compilers and templates are two big ones.

I don't really have an issue with compilers. They are the accepted magic that bridges between language for people and language for machines. The ideal language might be something that is functional and immutable language that it is easy for us to read, but compiles to the optimized, imperative updates that machine can run well.

I'd probably like React better if it had a compile layer on top of its more functional parts?

As for templates, JSX is probably the one great thing that came out of React. It is no wonder that many other frameworks are embracing JSX, but none of them are really forking the idea of hooks.


I don't think React ever pitched view as a function of state actually. Many in the community latched onto that, and some React-inspired projects tried to make that a thing, but I'm positive that was never the pitch from the core team, in fact very certain of this.

Lifecycles existed since the first version, and they were always absolutely critical. Hooks just fixed many of their issues and made them more elegant, composable, and simple to write.


>I don't think React ever pitched view as a function of state actually. Many in the community latched onto that, and some React-inspired projects tried to make that a thing, but I'm positive that was never the pitch from the core team, in fact very certain of this.

If it quacks like a duck..


> and in an ideal world I shouldn't be this granular about it

Hm, could you give an example of this ideal performant state management world? Angular? Vue? Svelte? MobX?


Svelte is a good example. But you don't have to go that far; Solid and Preact have signals with auto-tracked dependencies.

As another commenter mentioned, yes it's less low level than managing your own dependencies, but then again, most React apps are already wrapped in layers and layers of abstractions/context providers/selectors and use Jest as their test runner (which injects the global namespace with functions) and does other black magic and weird metaprogramming, so I think most React devs are already OK with less lower level stuff.


I'm a bit surprised the poster was asking for something more magic not less.


I mean, I am OK with magic, but it has to remain magical to me. The moment I have to deal with the magic, I don't want it anymore.


The dependency array is a huge footgun. Engineers, experienced and newbie alike, get it wrong all the time (either causing infinite render loops, or skipping necessary re-renders). This affects useCallback and useMemo as well. After a while, I stopped thinking "oh the developers just aren't doing it right" and more "the React team can do better than this."

> What API would you prefer? Something more like signals?

Yes. Or full tree redraws like Mithril.js did 8 years ago.


Or you installed the lint rule once 4 years ago and never had a problem again.


Agreed, everyone who complains about dependency arrays almost certainly hasn't used the linting rule. That being said, I'm not sure that an API is well-designed if the only way to use it effectively is by forcing you to even know there is an eslint plugin to begin with, and then once you do know that, that means now you're being forced to add eslint to your project whether you like it or not. And sometimes eslint just stops working for who knows what reason, I have a developer on my team who's constantly dealing with issues where his eslint/prettier setup isn't running correctly.


> That being said, I'm not sure that an API is well-designed if the only way to use it effectively is by forcing you to even know there is an eslint plugin to begin with

I don't really agree with this except to the extent that, sure, it would be ideal for all APIs and indeed the entire syntax of your programming language to be so well-designed that you don't need any lint rules to catch common mistakes. I'd love it if my static type checker could catch most mistakes like this (although the boundary between linting and static type checking is fuzzy).

But in practice, you're almost certainly going to either 1) have a linter or 2) have strong resolve that you will not make any easy mistakes that a linter could catch. And if you're in the second boat, it doesn't make sense to single out this particular easy mistake, given that omitting arguments is always an easy mistake to make in JavaScript.


> Agreed, everyone who complains about dependency arrays almost certainly hasn't used the linting rule.

Which linting rule?

> That being said, I'm not sure that an API is well-designed if the only way to use it effectively is by forcing you to even know there is an eslint plugin to begin with, and then once you do know that, that means now you're being forced to add eslint to your project whether you like it or not. And sometimes eslint just stops working for who knows what reason, I have a developer on my team who's constantly dealing with issues where his eslint/prettier setup isn't running correctly.

Yes, not to mention eslint is slow with large codebases. Fingers crossed for the Rust(?) rewrite.


The linting plugin is eslint-plugin-react-hooks[0], which enforces React's "rules of hooks"; it can detect when you access a value inside a React hook and will warn you that you should add it to your dependency array. It enforces some other rules here and there but that's the big one worth caring about the most. I would consider it essential for working with React hooks.

The only downside is that sometimes, the rule can end up being overly aggressive and tell you to add things which you explicitly don't want in your dependency array, but it's helpful probably 95+% of the time and not hard to opt out of or work around as needed.

[0] https://www.npmjs.com/package/eslint-plugin-react-hooks


Ok, these are the linting rules I was thinking of when I mentioned this in the other comment: https://news.ycombinator.com/item?id=37410666

As you already mentioned, it's not a silver bullet.


Can you be more specific which linting rule you're talking about for the sake of readers who may be unaware, and perhaps mention how this completely solves this problem? Because 1. a linting rule does not absolve this from being a footgun and 2. if you're talking about 'exhaustive-deps' there are many cases where following the linter here has caused infinite render loops among newbies across multiple teams I've worked with. Also see this highly contentious thread: https://github.com/facebook/create-react-app/issues/6880


The number of times I still see people not follow the lint rule even when it is installed, or use things like eslint-disable because its not always intuitive how to get around certain scenarios kind of rules this point moot.

In my opinion if people have the rule installed and still feel the need to disable it then it shows there may be an issue with the API itself. This is coming as someone who has written React for a living since before even ES6 classes were supported.

I see it relatively often, including in production codebases where people just disable the rule because they don't know how to write the sometimes non-intuitive way to get around it.


React does full tree redraws (well, unless you use memo).

useEffect is for side-effects.


Class components have "shouldComponentUpdate()" that avoids this entirely.


It does not. Child components will only render when their parent component re-renders or for example, its state changes.

Either way, this is different than what I mean with Mithril.js (it does full tree re-renders on event handler calls or when a manual redraw function is called).


This page[0] lists many situations in which useEffect shouldn’t be used. Some might seem counter-intuitive.

[0] https://react.dev/learn/you-might-not-need-an-effect


That's not a bunch of separate caveats, it's a few principles illustrated with lots of examples.

- Don't use effects for things that aren't genuinely effectful

- Put finnicky state management stuff in reusable utils instead of re-implementing it from primitives over and over again.

Neither is React-specific.


In particular that it's OK to directly call setState inside functional components was a surprise to me (when I read that some months ago), it does make things simpler.


tl;dr Don't use useEffect if there is an alternative.

That's a bunch of examples where there are good alternatives.


It does a shallow compare, not a deep compare. Workarounds tend to involve additional hooks: https://dev.to/vsramalwan/ways-to-handle-deep-object-compari...


I heard that immutability libraries like immer.js [0] help with this. Anyone go this way and had good success? Is this 'the way'?

[0]: https://immerjs.github.io/immer/


Personally, I grew to prefer signals and rx and APIs for heavy things. I wish it were easier to hook up some custom class object to a component to use as state, but it always feels clunky.


> I hope Google is good at searching decade old documentation

Google isn't even good at searching recent documentation, from my experience.


It will turn out to be a great conspiracy where the React team are being encouraged to make it so only LLMs can make sense of React code in future.


Agreed. React is great but I really do not enjoy writing it. Much prefer Vue


It's annoying that the React team announced Server Components without a well defined spec, the wire protocol most significantly. Instead, it's a cabal between the React team and Vercel which obviously is going to give them an inherent advantage and probably steer things in their favor, rather than the broader React ecosystem as a whole.


The wire protocol isn't relevant -- it's an implementation detail of React, and both the client and the server for it are open source and in the React repository. Next uses those. So will Remix, Redwood, and other RSC frameworks.

If you're curious:

https://github.com/facebook/react/blob/main/packages/react-c...

https://github.com/facebook/react/tree/main/packages/react-s...


Dan, you can't deny that most (if not all) the comms from both the React team and Vercel make it obvious that Vercel is not only first-moving on RSC but working intimately with the React team to both vet the implementation and I'm sure offer (opinionated) advice on tweaks/direction.

Also, links to packages with 5 line READMEs and no comments/documentation in the code might as well be called blackboxed. Yes, its open source but that's pretty much it. John or Jane Doe will have to deconstruct and/or assume the team's intention by combing through your links, https://github.com/reactwg/server-components/discussions/5 and the (light) documentation from Vercel on how all this stuff really works.

All I'm asking for is clarity and the ability to potentially create my own implementation rather than watch the "big guys" (read: frameworks) get the advantage by having access to information we don't have.


It seems like there is a project on github called simple-rsc that works without Next.


I will have to give this article a more full read rather than just a skim, but...

I am left with such confusion as to how React/Next arrived at this solution. `useEffect` didn't run on the server when doing SSR, great, easy enough to understand. Now several common hooks don't run on the server because they are considered interactive? And it isn't that they won't run, their initial pass in most cases doesn't do anything, it is that Next now yells at you for having a `useState` hook in a "Server Component".

I would like to write a component, not have to think about where it is running in most cases, and go on with my day. I mostly use React for large SPAs and have adopted Next for doing SSR on the few pages that I needed it for (SEO, etc).


Vercel is how, and Next.js is like a trojan horse at this point.

As much as the React team tries to push back on it, Vercel marketing has become synonymous with React's direction.

Most people do not need a single thing that Vercel/Next.js thought leaders are trying to shove down their throats.

Unless you're competing with Facebook for SEO rankings (good luck) the 1KB you saved by not shipping an un-interactive component down the wire is nothing.

They're actively pushing a set of best practices that align with their hosting model and building a moat around said hosting

Also despite the intentionally confusing wording, Server Side Rendering does work with Client Components!

There are literally *hundreds* of posts where people are unaware of that because the directive chosen by the React team was "use client"... which naturally leads people to believe "use client" will opt out of SSR.

To me that choice, and not allowing a project wide disabling of it, were the last straw that took it from innocent misalignment to intentional.

By having the thought leaders sell these heavily server involved features they've create a situation where technically you can move your Next.JS project anywhere... there's just a scary (for those who don't know better) laundry list of things that won't work unless a given provider goes out of their way to enable it.

They get to play both sides: Next.JS works anywhere (as long as you ignore a bunch if features that we sell as being very important to making a good site)


Do you have terms that you prefer (to Server/Client split)? I agree they're confusing but I haven't seen any proposal that would address the issues with them without introducing other issues.

I've tried to explain our current terminology here: https://github.com/reactwg/server-components/discussions/4


That is also my issue with NextJS -- it is utterly confusing trying to figure out what they mean when they say "server" or "client". Are they talking about the backend server and what client do they mean? These words become extremely confusing when you might be developing a full stack feature or service that has multiple clients, a backend API, etc...


While this may not be satisfying, I tried to write a response to a similar question here: https://github.com/reactwg/server-components/discussions/4


> I would like to write a component, not have to think about where it is running in most cases

you can keep doing this, just put "use client" in the file, Next will SSR them just like it used to. (SSR is an "emulated client" in this model. admittedly, the naming is a bit confusing)

but for some things, you want a guarantee that it only runs on the server, and then you can use server components :) i believe this is also why useState & friends are disallowed there -- they fundamentally don't make sense for stuff that'll only ever run server-side.


> In order for this to work, we'd need to be able to give React a chunk of code that it runs exclusively on the server, to do the database query

This is not true/it’s misleading. What you need is to be able to fetch on the server, and hydrate on the client using data from the server. This is possible with SSR and injecting into the React stream, using isomorphic (client) components.

The article is great, but the reasons for RSC are more subtle/complicated than the flow of the article would imply.


You can see a live demo of this here (using Remix and https://github.com/brillout/react-streaming): https://blessed-ibis-783.convex.site/

The relevant isomorphic data fetching hook source code is here: https://github.com/xixixao/convex-hosting/blob/main/app/hook...


> This tutorial is written primarily for developers who are already using React

Also good for those moving away from React, since we understand React too. Probably won't change it, but may give us things to look for in our new frameworks. A former coworker who was really into React just switched to Flutter.


I just started building a new app in Flutter too after working exclusively in React for the last several years.

I'm not sure I'd use Flutter for the web yet but I definitely prefer it to React Native.


I like how Flutter is truly different. I learned just how different it is because it has a completely different problem, shader compilation jank. It shows what great risks the Flutter team took to create something innovative. It's paying off, and time will tell how big it will be! https://docs.flutter.dev/perf/shader


Jank is addressed with the new engine that replaces Skia https://www.youtube.com/watch?v=vd5NqS01rlA&t=644s


I think it really comes down to Google's level of commitment to Flutter. They've done a lot of great work so far and I think it could easily become the first choice for cross platform app development if they investing in it to the degree they have so far.


Hilarious that the industry is going full-circle back to server rendered pages.


That's because it's not a full circle. There are plenty of subtle details that are new. The ability to interleave components written for Server and Client within a single programming model and with support for in-place refetching that doesn't destroy state is relatively novel — frankly I haven't seen something quite like it anywhere before.


The industry isn't, a few big players are going and trying to bring the industry but I have the impression that React is going to lose its lead in mind share in 1 year.


Same thing every so many years. Waste of life


A lot of people are complaining about NextJS + Vercel lock-in

RSC locks you into their hosting solution and the way they suggest you to use it also locks you into NextJS way of building your backend data layer (ie PHP/JSP/ASP-like with our without MVC)

But their routing things locks you into NextJS as a framework. Just remember how much people complained about migrating from react-router v4 to v5 (or was it v5 to v6?). Migrating from a routing solution is a huge pain in the ass. I am still unconvinced that file-system based routing is better. It is at best a replacement for lack of guidelines in most routing libs on how to organise your components for routing.

I am also very weary of anything that takes over the bundler, bundling is a hard problem and just outsourcing it is asking for trouble. But I understand why a lot of people don't want to deal with it, it takes a huge codebase for bundling to become a big issue.

I am looking forward to Bun: https://bun.sh/

Which promises to reduce the tooling headaches to get a project going without using a bunch of frameworks. Their bundler though is still nowhere near ready so I wouldn't recommend it for frontend projects yet. Using Bun just to run nextjs/webpack/whatever on top of it also doesn't seem like a good solution to me.

Bun might support RSC in the future, but in my opinion RSC is a very specialised tool to be used only in a few critical places in your code and it is scary how Vercel is pushing for it so hard in their docs. You have to remember that the average new frontend developer now uses "create-next-app" not "create-react-app" and is introduced to RSC right away


> But their routing things locks you into NextJS as a framework.

I'm confused by this -- how is a framework supposed to offer routing in a way that doesn't "lock you in"? are you "locked into" React Router if you use that? what's the alternative?

> RSC locks you into [Vercel's] hosting solution

how? you can self-host it if you want.


> are you "locked into" React Router if you use that?

Every dependency if large enough has lock-in. The problem with NextJS is how coupled everything is. In NextJS if you decide to stop using it you need to also rewrite your routing. React-router doesn't care about your bundler, or your deployment flows or your use of RSC, etc.


Server side rendering isnt special any more:

https://joshcollinsworth.com/blog/antiquated-react#server-si...

This blog was posted to HN a few weeks ago. The author does a really good job of summarizing React's relationship with the rest of the SPA ecosystem.


so ... ColdFusion custom tags?


I legit spit out my coffee when I read this.


its actually something thats been discussed before https://twitter.com/floydophone/status/1462928358988156932?s...


Haven’t heard of coldfusion in years!


This is my first time really reading about them, I'd assumed they'd do some ASPX style interactivity where server components updates on the server and send back html, it's interesting that they haven't tried to go for something like that, meaning this is a lot more limited in scope in some ways than I'd thought.


If you mean updating the page as the result of a mutation, Server Actions (https://nextjs.org/docs/app/building-your-application/data-f..., still in alpha) can do that - Next can re-render a route and send the newly updated UI as part of the response (this is a Next-specific feature, but I can't see why other frameworks couldn't implement a similar pattern around RSCs).


Which of these examples show that? As far as I can tell the closest is the server form, but that only seems to allow the form submissipn to be handled dynamivally, but doesn't show updating the server rendered component?


It's not explicitly demonstrated, but the Revalidating Data example (https://nextjs.org/docs/app/building-your-application/data-f...) would do it - calling revalidatePath/revalidateTag from a Server Action will (if the current route matches the path/tag) cause the route to get re-rendered as part of the same round trip.


How does it handle flaky connection or even offline scenario? How does it work with service worker?


> As I write this, there's only one way to start using React Server Components, and that's with Next.js 13.4+

I don't understand how this is acceptable to the React user community, that only Vercel gets to be in this privileged position. Why did they not include other companies, or the users themselves, in the discussion and development of this feature.

> Since Server Components are a new React feature, third-party packages and providers in the ecosystem are just beginning to add the "use client" directive to components that use client-only features like useState, useEffect, and createContext.

> Today, many components from npm packages that use client-only features do not yet have the directive. These third-party components will work as expected within Client Components since they have the "use client" directive, but they won't work within Server Components.

The arrogance of expecting all third-party packages, React-related or not, to buy into the "use client" directive. A humbler approach would have been a "use server" directive to opt-in to this behavior, instead of expecting the entire rest of the ecosystem to adapt to it.


The reason Vercel was selected for this privilege is pretty simple, to my understanding: they volunteered and committed engineering resources to partner with the React team to work through a pretty major and experimental new API with a lot of expected breaking changes.

Child of a sibling comment has more details: https://news.ycombinator.com/item?id=37408918


Further, some react devs are now working full time for vercel.

Money is to be made on both sides which is why this relationship exists.


how exactly is there "money to be made" here for the React team?


Third-party packages don't need any directives unless they specifically want to put a client/server boundary in their component.

The directive marks where the transition from server to client happens. If you aren't already starting in a server component, then you don't need to do anything to use client features.


> If you aren't already starting in a server component, then you don't need to do anything to use client features.

This is a deceptive reply since the only RSC implementation in existence starts from a server component.

That means that having React State, a basic building block of React that's leveraged massively across the ecosystem, is broken by default until the user adds a "use client" directive.

They're 100% right that "use server" should have been the directive, and more specifically it should have been something like "use non-interactive". Server vs Client is already widely understood to be tied to SSR in a completely orthogonal manner.

RSC has a much narrower value proposition that shouldn't have gotten muddied by all this.


The design you're suggesting doesn't work because it would cause server/client waterfalls everywhere, which goes against the entire purpose of the architecture.

The data flows in one direction: from the server to the client. It's just physics — your server is where the data is. You're trying to move it to the client. So the code that needs to run first is the server code.

This is why the data flow starts at the server. You can't fix this by changing how opting in works, or some other API design workaround. What you're fighting with is the arrow of time.

As for the terminology, I agree it is confusing, but I have not seen a better suggestion yet. I shared our reasoning here (https://github.com/reactwg/server-components/discussions/4), hope it's helpful.


There's nothing about it "that doesn't work": it simply isn't as effective at getting people moving to the mental model you're prescribing. People would have mostly trucked as they have.

Except people not wanting to do that mental shift was largely why they chose Next.js over a framework like Remix.

Choosing a default that breaks things (forcing npm packages to label their components...) was way too heavy handed considering the tenuous nature of RSC as a whole with its single implementation at this point.

Making client components default (as they have been since the dawn of React) and saying something like "server only" would have done the trick.

But with the directive "use client" you hit the mental pathway that people have around SSR. It adds just enough friction to feel like you're missing out even though it's almost orthogonal to the concept of SSR.


The problem with the approach you’re suggesting is that it creates client-server waterfalls. Avoiding those is the entire point of the design — otherwise it’s dead on arrival.

Suppose that, as you say, client is default, and marking a component as server is opt-in. This means that in any given tree, a server component can appear in the middle and be rendered by a client component. That, in turn, means that any time that client component re-renders, the server component rendered by it need to refetch. But that server component may render another client component under it, which may render another server component (under the design you’re proposing). As a consequence, a simple state update in a client component somewhere can trigger an entire chain of repeated client/server waterfalls just to resolve the resulting tree. This is not a feasible design.

The way RSC solves this problem is by (1) starting the data flow from the server, and (2) ensuring that during rendering, the data always flows in one direction — from server to client. This restriction avoids the client/server network waterfalls I’ve described in the previous paragraph. But it does require that client is opt-in rather than server.

Hope this helps.


Let's be clear, the design I'm proposing makes no changes to which boundaries are allowed. It simply switches the default from shared to client. Nothing stops a given bundler from requiring a shared component where one is expected now.

What that compromise would have done however is caused significantly less adoption, because "out of sight, out of mind". Most people would not opt into shared/server since RSC is not providing enough value for them: They don't mind the waterfalls, they're stuck in their ways, and above all they highly value reactivity bring sprinkled everywhere since that's what they were all running away from in template based frameworks.

I think where we likely won't see eye to eye is if that was a fair decision to make. I think especially given the singular implementor, the React time should have left RSC as a default disabled and left the uphill battle to Vercel to convince developers to opt into RSC in their own bundler.

Forcing NPM packages to need to think about appending "use client" to be is the smoking gun that this was the wrong move.


The default is client components. You need to opt-in to server components by running a server that serves up your root component as a server component.


No, the default is shared. It's literally in the RFC.

That's why using any interactivity in a component default breaks until you add "use client". The alternative would have been to have state, a core concept of React, work correctly by default, and make server/shared components explicitly marked.


> Why did they not include other companies

AFAIK: the initial stages of the project involved Vercel and Shopify [edit: and Gatsby]. these companies maintain frameworks and hosting solutions for them, which makes them good partners to work on full-stack features like this. notably, the convention ("use client") came about based on early feedback from Shopify.

the reason to work with framework authors first is that RSC requires a high degree of coordination/wiring across client code, server code, and the bundler. React itself only implements a couple of low-level primitives that need to be wired together by something like a framework to actually do anything useful

> expecting all third-party packages, React-related or not, to buy into the "use client" directive

AFAIK: non-react code has no reason to care about this directive. and on the react side, couldn't you make the same complaint about most breaking changes?


Facebook open source has been quite good (React, GraphQL, Buck, PyTorch).

This is...weird. How did Vercel manage that?


Dan Abramov (React core team, formerly of Meta) talks about this in a Twitter thread: https://twitter.com/dan_abramov/status/1654688751342301184

In short, it's because Vercel invested engineer-years into piloting RSCs as part of a production framework and making Next the guinea pig. Next wasn't actually the first framework to try RSCs - Hydrogen (Shopify) and Gatsby both tried early versions of it and provided feedback, but Next is the first to implement it to the point that it can be considered stable.

The long-term goal is that RSCs aren't a Next-only concept - for example the Remix team said that they wouldn't implement them until they became more stable (https://twitter.com/ryanflorence/status/1678416472010260480), but are working on it now (https://twitter.com/ryanflorence/status/1697253955376722092). But the React team has always historically favoured trying new features in production to iterate on the API (this happened a lot internally at Meta), and this was an opportunity to do that.


Also worth noting that after experimenting with RSCs in Hydrogen, Shopify acquired Remix and is now building Hydrogen using Remix.


Also worth noting that Remix is working on RSC support right now.


Reading further, use client is viral, and propagates to children components.


"use client" is more of a bundler feature. It doesn't propagate to components per se, it affects the import tree. You can think of "use client" as a directive that says: the entire module graph below gets shipped to the client bundle.


> Instead of bouncing back and forth between the client and server, we do our database query as part of the initial request, sending the fully-populated UI straight to the user.

> But hm, how exactly would we do this?

Couldn't help but chuckle. What mysterious technology could allow us to send an entire fully-populated UI over the web?


“As if a million voices cried out in terror and were suddenly silenced” - The full circle. When they left us, they were but learners, now they have become masters. Realizing that this nirvana exists, that people have been doing it this way for ages. That a combination of interactivity with the offloading of compute and rendering is a good thing for their performance. That grey beards really do know a thing or two. /s

All sarcasm aside, having composable components server-side is only a good thing. I welcome it.


We already had composable components server side with SSR, they just didn't query data inside the render loop. But that doesn't really gain much, if anything leads to slower overall performance as now instead of doing something like GraphQL and resolving your entire tree in a single optimized database call, you're probably waterfalling all over the place.


GraphQL hype is (and always has been, really) out of control.

GraphQL doesn’t do a damn thing to help you optimize your database calls. It also doesn’t do a thing to help with security or preventing DOS-inducing queries, which is the first question anyone who’s been around this block before (exposing remote query interfaces) has about it.

Some big frameworks that happen to speak graphql will automagic that stuff for you in simple cases, but we don’t claim e.g. the capabilities of the Rails framework with rails-api et c. as capabilities of REST or SQL or whatever.

This stuff is really annoying because you get half-technical leadership (or non-technical) thinking graphql is magic and that it’s good for all kinds of things it’s not and that it’ll save lots of time that it won’t, between the hype and the name.


GraphQL is for (1) static typing (2) front-end developer productivity and (3) front-end latency.

Maybe it does those well, maybe not, but if you expect it do anything else, you'll certainly be disappointed.


GraphQL out of the box doesn't make things optimized, but with any decent setup like Postgraphile, Hasura, or just DataLoader you do get highly optimized single-pass queries vs something like having many REST endpoints where the queries end up coming in piecemeal. I know this as I've been deep into these problems and it makes a huge difference in performance.

And the comparison is valid because these optimizations do happen and do make a difference, and are pretty easy to get if you just for example choose Hasura which is a very popular library (and there are many many others too). GraphQL makes this possible in the first place, whereas RSC and REST basically don't.


it does fix waterfall HTTP requests though in 99% of the cases it does not fix waterfall database queries (by itself) and often will hide it from the developer when it is happening

I am a big fan of GraphQL, but it is no magic bullet. One of the biggest pluses for me is that it replaces Swagger with something that is sane. People often complain about GraphQL requiring a lot of tooling and libs to get working, but if you make a REST api with Swagger support you either have to maintain a swagger spec manually or have just as much (and depending on the language, more flaky) tooling. I think the only framework/language that made swagger reasonable to use is C#.

Also technically GraphQL is inherently more powerful than REST apis, so you can (technically) make a non-connected GraphQL api that mimics how a REST api works (no going from one model to another without making a new query). Losing a lot (but not all) of the advantages of graphql but keeping all the advantages of REST. Nobody does this of course because it is reaaaaallly nice to have that extra power.


who's to argue that you didn't fetch your entire tree in an optimized way and passed that to your render loop as props? I think there's patterns for this. I'm not arguing that we haven't had the capability of composability with SSR, we definitely have. What I'm arguing is that merging the two worlds of front-end and back-end in a singular nomenclature (besides just typescript) is good for velocity. Or maybe I'm just inviting more pain as mixin's become traits and the waterfall of bugs comes our way.


> What mysterious technology could allow us to send an entire fully-populated UI over the web?

WebAssembly? Just kidding. But I think a lot don't realize that you could. It's pretty well supported now and a small WebAssembly library can fit in about the same size as a full page photo.


Vugu?


The Star-Bellied Sneetches


I'll do what I did the last time one of these articles popped up. Word counts:

complex: 2

confusing: 3




Consider applying for YC's W25 batch! Applications are open till Nov 12.

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

Search: