00:00:00React server components. Love them or hate them. Seems mostly hate these days but that
00:00:04might be about to change as TanStack has entered the game. That's right we now have TanStack
00:00:08server components and they've taken quite a different approach from Next.js. Let's take a look.
00:00:13Now I'll start with a paragraph from their announcement post that I think will put a
00:00:21lot of people at ease. It says "Most people now think of React server components in a server-first
00:00:26way. The server owns the tree, useClient marks the interactive parts, and the framework conventions
00:00:31decide how the whole thing fits together. This turns React server components from a useful
00:00:35primitive into a thing your whole app has to orbit. We do not think you should have to buy
00:00:40into that whole model up front just to get the value out of React server components."
00:00:45Essentially what they're saying is they don't want to go down the Next.js route where it's
00:00:48server components by default and then you need the useClient directive where you want to have
00:00:52interactivity. Instead TanStack wants to think of it as what if you could use React server components
00:00:57as granularly as you could fetch JSON on the client. With that goal in mind then let's take a look at
00:01:01how they actually implemented server components because spoiler alert I really like the way they
00:01:06did it. What I have here is a normal TanStack star application so everything at the moment is going to
00:01:10be a client component and the only thing I've done is some small install steps that you need to get
00:01:15server components running which is essentially just installing some packages and modifying your
00:01:18vconfig. This is what the page currently looks like we have our greeting component here which
00:01:22should see currently a client component and in the code it literally is just a single React component
00:01:27then down here we have a normal TanStack route and we're using the greeting component here. Now let's
00:01:32say in our greeting component you wanted to do some logic on the server. In my case I want to get the
00:01:36operating system hostname then also some environment variables that are only available to the server
00:01:40just to show you that this is actually running there. At the moment if I try and use os.hostname
00:01:45it's not going to work since this is a node function and it's not available in the browser.
00:01:49What we need to do then is take our greeting component and render this on the server and the
00:01:53first step to doing that in TanStack star is with a simple server function. As you can see I've got
00:01:58one here called get greeting and all we're doing inside of here is using the new render server
00:02:01component function putting our component inside of it and returning the renderable server component
00:02:06that we get back. You can think of this as simple as we're creating a get request for our component.
00:02:10Next then all we need to do is simply fetch the component from the server function that we created
00:02:14and we can do that inside of a loader on a route here so we're just awaiting get greeting and then
00:02:18returning that as well and it's still a renderable server component. Then we can use that inside of
00:02:23our route here by using use loader data and we just use the component down here like this. With that we
00:02:27now have our first TanStack server component. You can see os.hostname is now working and it's also
00:02:32pulling in environment variables that are only available on the server. Now the thing that I
00:02:36absolutely love about this implementation is if you notice the only new thing in here is the render
00:02:41server component function. The rest of this was just normal TanStack start. You could replace this
00:02:46with just some simple JSON data being returned and you'd fetch it in the exact same way. I also think
00:02:51this implementation is just really explicit about where your code is actually running. You do it all
00:02:55inside of a server function so it's pretty clear that's going to be run on the server and also
00:02:59inside of the render server component. In fact I think I can improve my demo code here by simply
00:03:03taking the functions that we're running up here that I want to run on the server putting them
00:03:07inside of the server function and then simply pass that values into my greeting component as props so
00:03:12now my greeting component is essentially just a bare component that can actually be used on the
00:03:16client or the server. I'm running all of the logic that I want to run on the server inside of my
00:03:21server function here. Again it's pretty explicit that's going to be running on the server. This
00:03:25literally feels like the exact opposite of the logic used in Next.js and I absolutely love it.
00:03:30It means that I can think of React in a normal way it's all client first then adding server components
00:03:34on top when I want them. But what if I wanted to use a client component inside of my server
00:03:38component so nested within the tree. Say I wanted to add on a counter button here that each time we
00:03:43click it just adds to the counter. Well if I simply try and add this into my server component here with
00:03:47an on click and then also a use state call you can see it completely breaks. It says use state is not
00:03:52a function or its return value is not iterable and that's because greeting is being used as a server
00:03:56component. We can't use this client functionality. To fix this you have two options and the second
00:04:01option is definitely the best one to use but the first option is going to feel familiar to those
00:04:05of you that have used Next.js. We can simply move that logic into its own component and then use the
00:04:10use client directive. This works in tan stack start server components we can simply just use the
00:04:14component inside of the server component now and as you can see it is all working nicely. The downside
00:04:18of this approach though is that we now have a server component controlling the rendering of a
00:04:22client component and this can start to get a little bit messy aka if I wanted to find out where my
00:04:28counter component was in the tree I would go to my route here and see that we have a greeting server
00:04:32component then go back up to the greeting server component and inside of the server component see
00:04:37that we have a client component and this mess can really start to add up and just make that boundary
00:04:42of server and client become a little bit unclear. Tan stack wouldn't settle for that though. They
00:04:47asked themselves what if the server did not need to decide every client shaped part of the UI at all
00:04:51and this led them to create something entirely new - composite components. To use one the first
00:04:56thing I'll do is remove our client component from our server component and I'll simply replace it
00:05:00with any children of our greeting component. Next we also need to change what we're returning from
00:05:05our server function here. Instead of returning a renderable server component we need to return
00:05:09what's called a composite source. To do that we can use our second tan stack server component helper
00:05:14function - create composite component. In here we're essentially just building out a component
00:05:18where props here are considered the slots. I'm just using a very simple children slot so it's going to
00:05:22pass through anything that we put as a child of my composite component into this props.children which
00:05:27I'm passing through to the greeting component that we just had. With that again what we need to do is
00:05:31simply fetch our composite component from our server function and we do that in the exact same way in
00:05:36the loader here. You see I'm just renaming source to greeting then I'm loading it with the use loader
00:05:41data. The only difference here though is we can't use this as a component. You see it's throwing an
00:05:45error here. To actually render a composite component we need to use the composite component helper
00:05:49component that we get from tan stack server components and as the source prop we pass
00:05:53through the composite component that we fetch from our server function that we created earlier.
00:05:57With that my server component is now rendering in like we had before and I'm also passing through
00:06:01the counter as children of this composite component and that is getting passed to the greeting where we
00:06:05had it set up here so it's passing it through into the props.children so everything is now working
00:06:10nicely. I can also go into my counter component and remove the directive that we had here since that is
00:06:14no longer needed since it knows this is going to be a client component as it's inside of a composite
00:06:18component. This is being used as a slot. Now it might seem like we got the exact same result there
00:06:23but with more work than just using the use client directive but the power is really coming from the
00:06:27developer experience and it's a bit of a switch from the use client model. Instead of having our
00:06:32server decide where our client components render in like when we had the counter component inside
00:06:36of the server component itself instead what we're doing with composite components is saying hey
00:06:40there's going to be a slot here we're going to render in a client component but the server
00:06:44component itself has no idea what that's going to be. We add that in later in our client code
00:06:48so we handle all client-based components within client code itself. That's only the beginning too
00:06:53if we take a look at a more complex page like this post one where this post is being server rendered
00:06:58there are two issues that I want to solve. The first one is that I want to add in some actions like
00:07:03liking the post and following the author but I want to add them in above the title here and I want to
00:07:08use a client component. At the moment I'm simply using this children slot pattern that means if I
00:07:12added in my post actions down here it would just go where the comments are since this is how we have
00:07:17the component set up so I want some way to tell my server component where to put specific client
00:07:22components. Then we have a second issue of if I do have a follow author button. At the moment this
00:07:27post page actually has no idea who the post author is. We actually offloaded all of that logic into
00:07:32the server component itself. If I wanted to get the author in a client component down here I would
00:07:37actually have to fetch the JSON for the post and get the author that way and that's not really a great
00:07:42pattern we'd be double fetching the data. Lucky for us then tanstack actually has two other slot types
00:07:46that we can use on a composite component besides this children one and the first one is going to
00:07:50be render props. This is essentially just any prop that is a function that returns a react element so
00:07:56this can be called anything it doesn't have to be called render actions and here I'm just saying what
00:07:59data I want the server component to pass through and that is going to be the post id and author id.
00:08:04Now all we need to do in our composite component is simply use this function that we're passing
00:08:08through as a prop wherever you want the component that's eventually going to be rendered to be.
00:08:12In my case I want that to be underneath the card header so I can call it with props dot render
00:08:16actions we can use an optional so if it's not passed through it doesn't break it just won't
00:08:20render in then we can also pass through the information that we want from the server component
00:08:24to our client component. After this our composite component is going to accept the render actions
00:08:28prop that we just created and for the value we simply pass through a function which has a post
00:08:32id and author id as the argument which the server is going to fill in then we simply render in our
00:08:36post actions client component and we can pass that data through as the props. So now I have a button
00:08:41up here where I can like and copy the link to the post and also click on follow author here where
00:08:45it's aware of the author name despite the fact that I've actually never fetched it on this page.
00:08:49I only fetch it in the server component and the server component is passing that data into the
00:08:53client component for me. Now you might think this breaks the logic that we had before where we said
00:08:57we don't want any server components to be in charge of rendering client ones but it doesn't and that's
00:09:01because slots are actually opaque. The server component up here has no idea what's inside of
00:09:06this it just knows that something goes here and that needs to pass through these values which in
00:09:10this case is the post id and author id. This function doesn't run on the server instead the
00:09:15server simply sees that it needs to pass data through and then down in our client this is when
00:09:19the function is actually run and the component is rendered in. The exact same thing also applies to
00:09:23our third slot type which is component props this one is actually a little more simple than the
00:09:28render props all we're doing is instead of having a function which then returns our client component
00:09:33we're just passing through the client component as a prop itself then on our composite component
00:09:38definition up here we're saying that we want to accept a prop which is a react component that has
00:09:42the props of post id and author id then we can use this within the component itself. You can think of
00:09:47component props like a placeholder the server component knows there is going to be a component
00:09:51there that needs some data in our case the post id and author id but doesn't really care what that
00:09:56component is as long as it accepts those props so i changed my post actions component down here to
00:10:01another one i've made called fake post actions and then we save that you can see that this is
00:10:05still going to render in because it's the client that's responsible for rendering this component
00:10:10it's only the server that provides the data looking at the documentation it doesn't seem like there's
00:10:14any real difference in which approach you take whether you go with component props or render props
00:10:18it might just come down to preference the only difference that i can see is that maybe you want
00:10:22to modify the data that you get from the server so in this case we can do whatever we want with
00:10:26post id and author id since it's just a function and then we can pass that on to our component
00:10:31whereas if you're using component props you just pass through the component itself and the server
00:10:36handles passing through the props now that's the basics of tan stack server components but there's
00:10:40still so much more to like for example if you wanted most of your page to be server rendered
00:10:44maybe you have a header component content component and a footer one and you want them all server
00:10:49rendered you don't have to bundle them all into one render server component function you can actually
00:10:53use promise.all split them out into three different functions and then simply return them as an object
00:10:58from a single server function but what if one of those components takes long to load that would
00:11:03mean the entire server function would and therefore the entire page would well don't worry there either
00:11:07what we can actually do is instead of awaiting the render server component function we can actually
00:11:12return the promise that it creates and then on the client we can take advantage of the use hook and
00:11:16suspense boundaries to load in skeletons so server components will just load in when they're ready
00:11:21i just really like the approach that tan stack has taken here it doesn't feel intrusive i'm not forced
00:11:25to adopt it and i can adopt it without any weird workarounds plus when i do actually go to use it
00:11:31the server components themselves are actually only three new functions the rest of it is just simple
00:11:36tan stack start server functions something that i was already using and it's as simple as fetching
00:11:41data this also means that it integrates nicely with tools like tan stack query something i will
00:11:45definitely be doing and it also makes things like caching simpler if you wanted to you could literally
00:11:49just cache the response of the get request on your cdn i'm definitely going to be exploring these more
00:11:54so let me know in the comments below what you think of them and if you'd like to see more videos on them
00:11:59well yeah subscribe and as always see you in the next one