CHARLES MAX_WOOD:
Hey, folks! Welcome back to another episode of JavaScript Jabber. This week on our panel we have Dan Shapir. AJ O'Neil?
AJ_O’NEAL:
Yo, yo, yo, coming at you live from the clouds.
CHARLES MAX_WOOD:
Steve Edwards.
STEVE_EDWARDS:
Yo, yo, yo. Sorry, AJ, didn't mean to step on you there. Hello from a still cool and cloudy Portland wishing it was sunny like Tel Aviv.
CHARLES MAX_WOOD:
Nice. We also have a new host. That's Tejas Kumar.
TEJAS_KUMAR:
Hey from kinda cold, kinda nice, Berlin.
CHARLES MAX_WOOD:
I'm Charles Max Wood from Top End Devs. We have two guests this week. We have Dan Abramov.
DAN_ABRAMOV:
Hi there.
CHARLES MAX_WOOD:
We also have Joe Savona, Joe.
JOE_SAVONA:
Hey, thanks for having us.
CHARLES MAX_WOOD:
Yeah, well we got you on to talk about React server components. And I know you've been on a few other podcasts kind of talking about the topic, but assuming that there are people that may not know what they are and assuming that they haven't listened to any of those episodes, do you want to kind of give us the 10,000 foot view what they are and why they matter? And then we can ask you all of our deep and scary questions.
JOE_SAVONA:
Sure, I can maybe start and then Dan can provide some more detail. I guess, so server components are React components that run only on the server. So that's a kind of common point of confusion. People think of server-side rendering, right? Server-side rendering is where you take your existing client application and run it, kind of pre-run it on the server. Server components are components that run only on the server. And so that means you can do things that a regular client component can't because that code is running on the server. So for example, you can access your database. You can access back end services that you don't want to expose to the front end. And then you can write async components because these are components that are running on the server. They're going to run just once. And so you can write async await and then choose which client component you actually want to render and then pass that data down to the client. So you can kind of think of it like, Server components are fetching data and then passing that as props to handing it off to client components for interactivity. And so this kind of opens up a model where you can really use a server for what it's good for, use the client for where you need an interactivity. And a lot of the complicated things that you might have to do today, like using an effect or storing server data in state and kind of managing that lifecycle. happens a lot more naturally because it just falls out of the server components model. That's the sort of high level view. So if you're familiar with using, you know, PHP with React on the front end or Rails with React on the front end, this is kind of like that except with server components taking the place of that traditional server-side framework. But kind of depending on where your background and where you're coming from, you might have different analogies for that. But I'm sure Dan has more to add.
DAN_ABRAMOV:
Yeah, I think it's, it's a little bit tricky to explain because the way you explain depends on the audience. Um,
CHARLES MAX_WOOD:
Hehehehehe
DAN_ABRAMOV:
I think maybe one way to say it is it, uh, it kind of lets you build full stack react applications, uh, where by full stack, I mean that it's kind of like the react backend as well as the react front end and the idea is that instead of writing. the backend and the front end in two different ways, or thinking about it from the perspective of exposing an API, you kind of think of the whole thing as a component tree. And so if you have been building web apps in early 2000s, you probably remember this simple mental model of, and if you haven't, I'm just describing, the way you would write it is, for example, page called About PHP or Index PHP or Feat PHP or whatever, just the entry points, the pages that the user can visit. And then you could call some kind of API or you could read the record from the database. You could use the database in abstraction, so some kind of database access layer. But the mental model would be... You can just grab some data, map it to some kind of output. And, you know, and that is the, that is the page that the user is going to see. And so this request response model is very nice to think in, because you only think about, you know, here's the page that the user wanted and, you know, here's the data that it needs. Here's how to get this data. And then let's just return the page. But the downside of this approach with traditional server-side technologies like PHP and Rails and so on is that the page cannot update. So if the user clicks on another link or the user modifies something in the database, the only way to, kind of like in this traditional model, the only thing you can do is really reload the entire page. So you know, the user clicks the link and then the browser... goes, fetches the new HTML page and displays it. And so this lets you stay in this kind of request response model. But from the user perspective, all the, you know, if you had a video playing, it will reset. There, there was no way to like animate this transition. If you have some kind of a shared state, it would get reset in the components because you're always replacing the page. And so in the, I think in the 2010s, the dominant paradigm for a lot of apps has shifted to client side, where you kind of, you know, you describe the UI on the client and then that allows you to do client side routing. So when you click a link, we just, we keep the page intact, but we just replace the parts, you know, maybe the part of the route that has changed, but we don't reset the state of the entire page. But then the, from the developer point of view, this is a much more complex model because you have to kind of think about state like server data as part of your state management, you have to think about how to cash it. You have to think about how to actually like trigger the fetches, how to do progress indication and a lot of those things, uh, they become pretty complex. And I think that's when people say they're kind of tired of SBAs. I think a lot of it is about, it's pretty hard to think, you know, in this model of continuously running app, as opposed to the model of I just go to the page and it returns some UI to me. And so really the, I think the idea of server components is what if we could really have both? So what if we could, you know, describe the UI as a React tree? But in parts of that React tree, they execute. uh, in the place, you know, on the server where you have the database, you have, uh, you know, easy access to like, uh, you can call your microservices without ever exposing them. You don't need to, you don't have to like build a publicly consumable API. So those components kind of live on the server, but then you can kind of progressively enhance them with the React you already know. So you can have, uh, you know, client components that have stayed. You can have like animations, you can have videos, you can have forms, like all these things that you use React today for. And then these things are a single tree. So you kind of write your code from the perspective of, you know, here's the request, here's the response, but then the user experience is more similar to single page apps where you can continuously navigate or continuously do something. uh, within the app and the page does not reload, you know, state does not get destroyed. Uh, it just stays there. And one, one thing that's worth clarifying because I feel like, uh, uh, a lot of discussions kind of miss this is when we say the word server, we don't necessarily mean, uh, like a literal server. It's more about, um, the fact that they run ahead of time. So for example, um, So Next.js is one of the frameworks that implement the server component specification and in Next.js the default is actually build time. So server components run at the build time by default. And in that case, your machine is kind of like the server and, you know, it happens during the build. And so this lets us, you know, if you're, you can do like fs.readfile in your server component and read something from, from a file, for example, markdown blog with a bunch of files, you can just read them in your React components. But then when you want to add a little bit of like, you know, like a theme toggle or some kind of, like a comment form or something like this, like that can run on the client. And then for some other routes, like maybe like a settings page, that could actually use the server. So it is flexible in terms of when exactly it runs. And then if you... So this is like from the perspective of like old school web development, but it could also contrast and compare it with today's approaches because it could also look at server components as kind of a react take on how to do like data fetching. And it has some, like, I think it has some overlap with things like get server side props in next GS. or remix loaders or Astro and Islands. So those are like different newer technologies that kind of address the same space. And then you could think of React server components as kind of what does it look like to address the space in a way that feels native to React? So what is the React take on how to do these kinds of things? instead of kind of bolting them on top of existing React. Maybe I can start answering and then Joe will follow up if there's more. I think I don't fully agree with the characterization that you could do everything. I think that's not strictly saying true, but it is in details. So one example is that you couldn't really componentize server or build logic. And I'll give you like a concrete example. So let's say you have a Markdown renderer component. And let's say that your site is static. So it's essentially like a blog. And currently, if you put this as a component on NPM, so you put a component on NPM, your Markdown renderer, that imports some markdown parsing library and turns that like into React tree or HTML. If it is on NPM, then the only way you can consume it in React is to consume it at runtime. So the only kind of way you can, you know, if you add it to your React powered site, even if you build it with a server enabled technology like Remix or Alt and XJS, then this markdown library will be shipped to the client bundle, unless you completely get rid of JavaScript and then you have nothing is interactive. But if you even have a little bit of interactivity, the default way to consume components is they're always shipped to the browser. And so this is pretty, for some cases, this is pretty inefficient, with this markdown example. If you're building a blog that is static, why can't you just do this at the build time? And we do not have any way to put a react component on NPM that does a bunch of stuff at the build time. Like there is, it's just not possible. Uh, you can put like a function that then you can tell, you know, call it from your remix loader, but then that's not composable because, uh, you know, like what if, what if you have like multiple, uh, places where you want to do that? What if you have like code highlight and you want to. do parsing of the code during the build, because it doesn't make sense to ship like a JavaScript parser to the client just for code snippets, if your code snippets are static. And so you can't create a code highlighter component that does this kind of work during the build. And then the thing is with server components, you have this flexibility. And the beautiful thing about it is that you don't actually need to do anything special for it. So if you have an existing code highlighter component that just takes some props, we're trying some output in the server components model. If you import it from a page that runs or like from a server component, then it will also run in the server only environment. And so it will not be shipped. Like the parser for JavaScript would not be shipped to the client as a dependency. Whereas if it's, uh, you know, if you import it from, uh, like a blog post preview page where. you're typing into an input and it's a client component and it updates the state and you want to render a live preview, in that case it will be included as well. So that's an example of a new capability. But you can also think bigger because, well, it would also enable you to componentize server-only logic. So imagine you could have a... some kind of like a dashboard component. Maybe your internal company, you know, you're building a bunch of components. You can have a dashboard component that you put you on your like private NPM registry or whatever you use for sharing code. And the dashboard component can call your microservice or can call your, like it can, you know, read from your database layer, but it is isolated. So it can specify, you know, how to load data for itself. And you could still compose it in the React tree of components. So I think that is a new capability. That's like, what if you could put server only or like build only logic on NPM and compose it and componentize it the same way that, you know, what React enabled for the client ecosystem to enable this same kind of composition for the server and build ecosystem and have these things work together because it's really, it's not... you know, one or the other world is it really lets you combine them seamlessly and have a component and PM that has both client and server parts. And you just, you know, just import it and render it. So I think that that is a new capability. Uh, but as for like the people being happy with react today, I think that's also an overstatement, like sure. Uh, it's, you know, uh, it is, it is usable, but If you listen to what people are complaining about, like a lot of the complaints, yeah.
CHARLES MAX_WOOD:
Hehehehehe
DAN_ABRAMOV:
But like, you know, people are complaining about like data fetching in user packs, for example, you know, that's, that's a big one. And in general, like people say, Oh, I wish it was easier to do data fetching. And if you try to do data fetching, well, like you have to think about, you know, how do I deal with loading indicators? Uh, how do I avoid, uh, loading waterfalls? Uh, how do I, um, you know, how, how do I, uh, kind of compose these things together? And then it's actually funny because one of the, like very early feature requests, uh, was for react, you know, as soon as promises was a thing, one of the earlier requests was like, why can't they just like async await and just like, do that affection this way? Like, why can't you just like a wave, a fetch and then return a bunch of UI and. That's something that people, yeah. So you, well, that's what it took us a long time to figure out. Because if you want to do that, you have to figure out, okay, what should happen while it's fetching? Like how do you express, uh, you know, where to put the load in state. And that is, uh, you know, that is suspense. So we figured out you put the load in state in your tree. That is how you solve it. Then you have questions of like. Well, but what if, you know, what if your component is waiting for something, but then the parent component updates a state. And so you have to like synchronously, uh, do something like typing into an input. How do you deal with a part of your UI kind of being stuck because it's fetching something. And so the answer to this, well, you just spread apart your tree into parts that run on the client. And so they have to be able to synchronously respond to input. And then parts that run on the server or during the build, uh, that are, that can be async because they're only triggered when you, you know, on the first load and then during a refetch or a navigation. So the, you know, when you just type something, it doesn't, it doesn't force them to the refetch. So there's a lot of kind of, you know, uh, complex questions, but I think one way to think about it is like, yes. Uh, You should be able to async away the inside components. It just took us seven years to figure out how. And that is our answer, like server components is our answer to how. Joe?
JOE_SAVONA:
Yeah, I think you really covered it. Um, people have been asking for, you know, how do I do data fetching and routing and kind of, and those things are very related, right? They've been asking for a long time and this is, this is like our answer. So. But yeah, Dan, Dan really covered it.
DAN_ABRAMOV:
Joe, do you want to take this one?
JOE_SAVONA:
Um, no, I think you, I think you're probably going to summarize it better. Um, I,
DAN_ABRAMOV:
Okay,
JOE_SAVONA:
I feel like to me it
DAN_ABRAMOV:
I can
JOE_SAVONA:
is like
DAN_ABRAMOV:
try.
JOE_SAVONA:
so many things, but yeah.
DAN_ABRAMOV:
I can maybe try like the thing that comes to my mind first. Um, I think it is, um, like there is a, the markdown example is, um, is a nice illustration of optimization, right? Because, uh, it's something that could run on either side, but really it's not necessary to run it on the client. If, you know, if they, if the, uh, if the blog is static, one thing to note there though is, uh, There are solutions that, for example, like Astro, where they really go into this direction of, well, if it's static, then it should just run only at the build or only on the server. And then you have, like, hydration only happens for so-called islands or like pieces of interactivity. that is important here is in the react, we want to be able to reuse code. Uh, like if it can run in either environment, so like with the blog post example, maybe on the blog post page, we want it to behave kind of statically because it doesn't change. So it doesn't make sense to ship code there, but it could use the same exact component, like you can literally import the same component in your, uh, in the part that like has a live preview. And so they are the same exact code. could work on the client so that it responds as you type. And you wouldn't download that code, you know, unless you're actually like on the compose post route. So unless you're actually like the author of the blog post. So I think that's one important difference with these approaches that try to be like, let's make it, you know, as much as possible static is that in Astro, for example, you couldn't do that. Because if in Astro, you start with like a static blog post and then you're like, Oh, I also need to show a preview of this on a different route as the user is typing. Now you have to rewrite it from like Astro piece to React piece. And either you have like code duplication or you have to deopt and like ship client code in the first example too. But as you said correctly, I think that's, that's just an example of just, you know, it nicely comes out of the paradigm. I think a, a more important piece. some components can only work on the server. And so this is different from optimization, right? It's like, imagine if you're familiar with Django, you know, this Python framework for creating apps, there's this plugin called Django admin that lets you generate these tables where it just connects to your database and kind of gives you like a default admin template where you can... like see different browse, you can like edit individual entries and so on. And that is something you couldn't really put into a component before at all, like in react land, because it has associated, uh, you know, server code with it that is server only. And so I think that that's like one key feature is that now you can write like componentized pieces that are server only. I think that's, uh, and, and we use them. I think that is important. Uh, But if we talk about like the big picture and like, why we're doing this at all, I think it's, uh, it's, it's just because, uh, the, like, we want to, we think the mental model of, um, just, just writing, um, you know, the, the mental model of like request response is incredibly powerful, incredibly like, uh, you know, It's very nice to express a data logic in it where you just take some data, wait for it, and then return the response and kind of don't think about it. Like it's not stateful and that's very nice for data fetching. And then the clients, you know, the parts that we use react for, where you're like typing something and something updates as you type, or you have some kind of complex forms or some kind of complex flow that like drag and drop, like something interactive. Like that is something that state is really, uh, is really good at and something that it's very useful for. And so it's, it's just about, why don't we give you a way to, uh, do data in a way that feels stateless. And so it's easy, easy to write, easy to wait for, and to keep using state for things that are, you know, uh, actually like stateful as in the user did something and we want to remember it, uh, and then. have those things combined together very seamlessly. So I think it's mostly about enabling this way of writing where you use both sides for what they're best at and then can compose them together seamlessly and you can componentize them so that, you know, different people, different teams can write pieces of it and like put them on NPM and just put them together. And that's kind of like what people like about React already. We want to do that for the server and for the build time as well.
JOE_SAVONA:
Yeah, I would just add, I think when you're faced with like, uh, kind of multiple different use cases, one way to approach that is to kind of have targeted solutions for different use cases. So you can say, oh, okay. Um, we're, we, we, you know, we want to make it possible to not have to download the Markdown renderer to the client in this use case. So let's try to find a targeted approach there. And then we want to do a nested routing. Let's find an approach there. And then we want to do, uh, make it easier to do data fetching. And know, that's often a reasonable approach is to think, you know, various, uh, kind of use case by use case, because you often don't need a more general solution. But I think in this case, it really was something where we began to see like that these, these are all related routing and data fetching are related. Um, the reason that we're downloading this extra code is related to the data fetching. Uh, and so then that all kind of having, having this kind of unified solution is actually simpler than attempting to solve each of these on their own. Um, And then once you build that, then a lot of things just naturally fall out of that different programming model. And so it kind of looks like, oh, like you're solving all these different things, you're getting all these different benefits. I think, as Dan said, the real key is the sort of data fetching piece and enabling to kind of have you access the server data easily. And then the kind of single rendering kind of programming model. And then yeah, a lot of nice stuff just solving the problems that people are facing. That's right. Yep. That's right.
DAN_ABRAMOV:
Yeah, I think that's, uh, you know, if, if we talk about, uh, like a classical data, you know, if you think of like a classical react, that are fashion example that, you know, people learn like in boot camps, it's usually, uh, a piece of state and some use effect that kind of fills in that state and it already kind of feels weird to write, like, you know, the shape of it looks kind of strange, but, um, even if we set that aside, Like why are we thinking of that data as state? Because state is something that is, uh, that you need to like, the UI needs to remember, right? Like classical examples of state are for example, like a toggle button, right? So it's stateful because it was in some initial states, like it was untoggle. The user did something and the button has to remember what the user did. So the button has its own memory and that is what state really is. And then this example of like a comments widget that has a state of comments that's like initially empty and then somehow just making that component appear causes it to like go to the server and fill in and then set some state. Like the user did not really do something to fill in the comments. It's not really like the comments widget trying to remember something. It's, and I think that is like the conceptual idea here is What if instead of thinking of a server as like a different thing or, you know, you, the file system, right? Like it doesn't have to be a server. It could be during built, but what if instead of thinking of this, like as a piece of state that the component remembers that somehow the component tries to keep synchronized using effects with something external, what if we just think of it as we just want to pass some props from the server and so it's, it's still this top down data flow where. things are, you know, the component takes comments as a prop. It just happens as this prop is not filled in by the client. It's filled in from the server. And then if you want the, uh, the other thing that's like really important about server components, and I think, uh, a lot of explanations miss it because, uh, of confusion, the server rendering. So this is, this is like a, uh, I think there's something we need to be very clear about that server components. doesn't really have anything to do with server rendering. These are like completely different technologies. It just happens that server rendering or rendering to HTML is beneficial for the first load. So we recommend to use server components together with, uh, rendering to HTML for the initial page load. But the important difference is that, um, in traditional server rendered solutions, uh, for react. So like the old next three S, uh, the, you know, Remix, Gatsby, like all of these solutions, server rendering or rendering your React tree to HTML, it only happens once, right? So it happens when the page loads. That's when we turn it into HTML. And then from that point on, the only thing that re-executes on the server, like for example, during navigations, or if you update some data, in this frameworks usually you can... only kind of refresh small pieces of code. So in Next.js, it's like get server-side props. So you can tell that thing to re-execute and give you new props. In Remix, you can tell the loaders to re-execute and give you new data. And then Server Components extends this to the entire tree. So in Server Components, you can say, I wanna refresh my tree. And so if the comments that they get from the database is different, the server is going to pass you new comments as a prop. So it's kind of server components puts, if you know this concept of unidirectional data flow in React, where things fall down as props, and then when you want to change something, you update state at the top and then things fall down again and they flow always down, you can think of server components as adding the server to unidirectional data flow, where you can say, I'm changing the comments on the server, like in the database, and it just like refreshes that, you know, your server component route and the new comments just flow down and you don't need to do anything extra to keep them in sync or like to refresh them. It's just like a unidirectional data flow refresh, the same as it happens on the client when they change the state.
JOE_SAVONA:
Right. So that, and that means like that we're, we're not blowing away client size state you're keeping. So if you had it, uh, you know, some, some interactive, uh, component within that, that's going to in, in kind of react terms, reconcile. Um, so it's just as if you'd re-rendered the, as if all that had happened on the client side, that, that existing interactive state is not lost as this, this server side refresh happens. Which is the thing, another, another piece where like, you know, you talked about other solutions can maybe approximate some of what server components can do. That's the kind of thing that often gets lost in these other approaches. Whereas server components are able to keep that, keep kind of the existing client side state around.
TEJAS_KUMAR:
Yeah, I had a question about all of this. Thank you, by the way, Joe and Dan, for answering. This is really educational. And the question I have, I think, is something that a lot of the listeners and developers would be interested to hear the answer for. So I'd previously thought wrongly, probably, that server components was the primary solution. Server components was aiming to make was around bundle size, because indeed, shipping, like this Markdown example, instead of shipping the entire. MarkdownRenderer, you ship just the output of it, as it were. And I always thought bundle size was the main theme. But hearing this conversation, it seems like data fetching is also just a huge part of it. So it sounds like Server Components is designed to solve a whole bunch, as it were, of problems. But you said, Dan, talking about data fetching, and the community asking, why can't you just do Async Await as function components? I hear that data fetching can be an answer. But at the same time, the question, and this may be a very silly question, but the question in my mind is, the async primitives on Node.js on the server are very close, if not the same to Node.js, if not the same to the browser, such that we can do async 08 in the browser also for client rendered applications. So the thing that I don't fully understand, I think a lot of developers listening would also probably like to know is, why can't we or indeed maybe we can have React function components called asynchronously at the render phase? Is that like it sounds like from the previous conversation that it was impossible, but what I know tells me this is actually possible is just probably not ideal. Yeah, yeah, definitely. And I think, so again, my assumption that may be wrong is while async components on the client side are technically doable in the browser runtime, the server component approach as it is right now solves that, but also a whole bunch of other problems like data fetching at L that we've discussed. But now I'm curious to hear what the team has to say.
DAN_ABRAMOV:
Yeah, do you want to go first?
JOE_SAVONA:
Yeah, I mean, I think you already kind of touched on this in one of your earlier answers, Dan. There's just because, so yeah, for sure. I mean, obviously, async-a-wait works in the browser, but that's only part of the problem. What do you show while a component is loading its data? You need a way to represent that. I think in the past, frameworks would basically, I think... and some, some version of Angular that you just have a, a promise. And if it was a loading, it just that, that, that property was null in your template, right. Um, which, okay, that works. Uh, but we really want to give the user more control over what is being rendered at any given point in time.
TEJAS_KUMAR:
But
JOE_SAVONA:
So.
TEJAS_KUMAR:
sorry to interrupt, but Suspense has a fallback prop that also runs
JOE_SAVONA:
Right.
TEJAS_KUMAR:
in
JOE_SAVONA:
And
TEJAS_KUMAR:
the.
JOE_SAVONA:
that's the point. That's what I was getting to. It's like, so you need suspense, right? And so, yeah, we had a needed, at least like the server components, like infrastructure to even make a lot of that possible. But yeah, I think like, Dan, you'd know like a little more the details of like the internals for why it's a bit more challenging.
DAN_ABRAMOV:
Yeah, yeah, I think I would maybe approach it slightly differently. And I think the question of like, why can't you do just do this on the client? The answer is primarily related to waterfalls. So if you imagine and it's the same, I think it's also an answer to the question of, well, why can't you just put a server component in the middle of your client tree. So it's also like a question about incremental adoption, because I think the first thing that people kind of run into is if you try to, you know, you know, they want to think like, how do I adopt this incrementally? Like I have this react tree, you know, it's like completely client. And then I want to like put the server component somewhere in the middle. And we have a rule that says you cannot import. And. Note I did not say render, I say import. These are two different things. But you cannot import a server component from a client file. And the reason for the simulation is, and it's the same as answer to like the question about, can't we just do async await on the client? Well, let's imagine that we do that. And let's think through the consequences. So I think there are... There are two different ways you could imagine, there are two different ways you can imagine the, this kind of playing out. So maybe like the first way is let's say everything is on the client, right? So there is no concept of like server components, your, you know, async await. We just add support for async await in component functions. And I think what you'll see is that it encourages incredibly inefficient. user interfaces, inefficient loading. And I mean, it's, it's fair to say that this is already status quo with use effect and data fetching, but we kind of have a slightly higher bar than because like when people do use effect for that affection, it's usually because they have just like two or three fetches or something like this. It's not that they, you know, they write their whole component tree where like fetch on every level. Because if you try to do that, like if you try to scale it up through any non-trivial app, then you'll see it's incredibly slow because you start rendering the, for example, the parent component, like a profile page and it does, you know, let's say it's like async awaits, it like awaits for some basic like profile data. And so that goes to the server. So you introduce a latency right away. Like that has to go to the server anyway, because that's where your data is. And then like it comes back and then you continue rendering and then you know, you bump into like profile timeline You know like oh no I have to go back to the server again and imagine if there is there's like some latency between server and client You know like it's like if there's like three seconds It's like three seconds on every single level that you want to get something and so it just doesn't scale the you know Do any non-trivial? data requirements. So I think that that is the answer to why we can just do this on the client only. But I can also answer the rest.
JOE_SAVONA:
Well, I think what you really want, what you're really describing there is to say, you want to be able to run, how do you avoid that waterfall? How do you avoid that? Well, the only option, when you think about it, from the implementation perspective, is that the server has to know, after I finish fetching this one query in the parent component, I'm gonna have to go and execute these next async functions in the client. How does it know what they are? Well, you have to actually render the parent component. on the server to figure out what client components are gonna be rendered with what props and what async functions are gonna get called there. And that's server components, right? So yeah, it's just that if you wanna avoid those waterfalls, you have to move all the appropriate logic to the server or else, yeah, there's no way around it. There isn't, there kind of can't be a JavaScript solution given that there's user code. Right, that's actually running in between the data fetches.
DAN_ABRAMOV:
Yeah, I want to clarify because I think it's, it's not about promise that all or something like this. It's, it's not sufficient because if you have a parent component and like, it wants to fetch something like, even if there is a, like, I want to clarify. sometimes waterfalls are just inherent. Like you can't avoid waterfall in principle in the sense that sometimes you need to, you know, go to like, imagine you need to go to one service. You know, that tells you some information that you can like start rendering stuff. And you use a piece of this information to go to another service, to fetch something. Like that's a waterfall you cannot avoid because the result, what you, you know, the child you're going to render depends on the data that the parent receives from the fetch, like it's kind of unavoidable. But the question is more about, uh, uh, what, like, where does that waterfall happen? And so the problematic case is like, we go to the server, you know, like we. Like there's like a lot of latency in between potentially. We figure out, okay, like here's what we're going to render. And now like we return that to the client, like return the data to the client. And then the next, you know, the component inside, now we know what the component inside is because now we know it's props. Now we know what's being passed down through it. We've found the parent components logic. And then that component is like, Oh no, I need to go to the server. It's like, imagine if you were. Like you had a shopping list and like the person who figures out, you know, the person who kind of, you know, the person who's who's cooking is like, I need, you know, I need you to like go go to the shop and tell me what you know, what fruits are are in the shop. And so you go to the shop, you figure out what the fruits are, you come back and you tell me, you know, they have like bananas and oranges. And then they're like, Well, I'm going to need some oranges. since they have oranges, let's also get some spices. And so you can't go back to the shop. That is silly. Ideally, you want to know, here's the entire list. If they have oranges, then get oranges and spices. If they don't have oranges, let's get some apples and some, I mean, I know this doesn't make sense as an actual shop on this, but yeah. But what I find is just like, you don't want to keep coming, you know, there is some inherent waterfall sometimes, but you want that waterfall to happen at the shop. Like you want to, you know, you have your list of like, what, you know, what thing you're cooking, you go to the shop and that's where you make the decisions about, you know, they have oranges, great, I'm gonna have some of those, or otherwise I'm gonna have these things. And so even if there's like inherent waterfall. We just want there to be just one request between client and server, but then on the server is where we actually like run this logic. And so this, this avoids going back between server and client, server and client and spending all this latency because that, that can be really slow. And that it happens at every single level of nesting.
TEJAS_KUMAR:
That
AJ_O’NEAL:
So my
TEJAS_KUMAR:
makes...
AJ_O’NEAL:
con-
TEJAS_KUMAR:
Sorry, go ahead. I was just going to say that makes a lot of sense. So the answer to the question that I asked, it sounds like theoretically you can do these async components on the client side, but it's probably not the ideal solution because server components solves the problem of bundle size and data fetching in its peak efficiency way.
DAN_ABRAMOV:
There.
TEJAS_KUMAR:
Correct. You mean...
DAN_ABRAMOV:
Yeah, but I also... sorry.
TEJAS_KUMAR:
I was just going to say, you mean the async client components encourages a less, if yeah, okay, that makes sense.
DAN_ABRAMOV:
There's also another part of it, which I think is really important and that, uh, that kind of answers the question of why can't you just nest, uh, why can't you just like put the server component in the middle of your tree as an adoption strategy, right? Like, why can't you just like render a server thing, uh, like somewhere in the middle? And the answer to this is actually related to, to this problem, uh, of imagine that you could. So imagine that you have like an async function in the middle of the tree, of your existing tree. Whether it runs on the server or in the client, it's actually the same problem. So imagine that you have this async function and then some state updates above this component. So like normally in React, when the state update, like you have a state update, you want to re-render the tree, right? Like the tree needs to be consistent. So if you're updating the state, you want to do rerenders. Like sometimes maybe you bail out of rerenders for performance. But still, like if you're passing some piece of state down as a prop, it has to go through every level and it has to re-evaluate, you know, the, what is the UI given this new value of this prop. And so imagine that you put like a server, you know, you could put an async function somewhere in the middle of the tree. And then let's say you add an animation or you add the form above it, or you add something that, you know, a theme toggle, like you add any piece of state above. And so now what do you want to happen when the property pass kind of flows down through that tree? And, but, you know, the naive way to do it is like, like you would actually, if we allowed that, you would accidentally trigger this async refetch all the time. And it would get stuck all the time because, you know, it takes some time to, uh, to kind of go to the server and back. And this is especially a problem if, uh, this component just does some prop plumbing, so, uh, maybe it just takes, you know, some prop from above and it passes down to something that actually needs it. And so now, uh, like maybe that prop has not changed, but it's still kind of like having to refetch this stuff. And so what server components forces you to do is to decouple these two data flows. So you have the server data flow that is only, you know, the data that's needed by the server. It flows down, uh, you know, on the server. And so you don't actually know what the client state is on the server. Like you don't know what, you know, you only know the URL or some kind of like where, which page you're rendering, but you don't really know, like, what is, what is the value of every checkbox and so on. And then you have the client logic that, you know, that, that response to state changes in, in real time. And so the, the beauty of this is that with the server components model, it's really like the server is kind of the first pass. So the server just figures out the data. And then like on the client, if a state updates, it never needs to, like it never triggers server refetching by default. Like if you update like a theme toggle. It just updates the client components because on the client, from the client's perspective, the server has already ran. So like the client only sees kind of the server output. So it just keeps over it. It's like it's pre-computed. So you don't have the situations where you just added a bit of state and suddenly you're refetching like 10 times more often. And that is like a big, you know, that's like a big thing we're avoiding. Yeah, I think