5 Ways To SSR/RSC on TanStack Start

Jack Herrington| 00:10:06|Apr 20, 2026
Chapters8
Introduces the five different data fetching approaches (SSR, non-SSR, RSC, and variations) that will be covered for rendering a page. Sets the stage for comparing these methods using TanStack Start.

Five distinct data-loading strategies with TanStack Start, from CSR to RSC composites, plus data-only SSR and classic SSR hydration.

Summary

Jack Herrington breaks down five practical data-loading patterns you can use with TanStack Start, spanning CSR, SSR, data-only SSR, React Server Components (RSC), and the innovative Composite Component approach. He emphasizes that React has always supported server-side rendering, and shows how TanStack Start coordinates loaders, data fetching, and rendering across both client and server contexts. The CSR variant demonstrates client-side data fetching with SSR disabled, while the SSR variant renders on the server and hydrates on the client. Data-only SSR fetches data on the server but renders on the client, useful for dashboards with secure data transfer. The RSC section highlights two modes: low-level RSC via renderServerComponent and the Composite Component API that allows page shells with interactive slots. Herrington demonstrates these with a Pokemon API example, referencing code like ssr.tsx, rsc-renderable.tsx, and rsc-composite.tsx, and notes how loaders orchestrate data flow. He also mentions enabling RSC through the Vite plugin and clarifies that RSCs are server-rendered and then sent to the client, unlike hydration-based SSR. Throughout, the emphasis is on architectural flexibility: mix and match what you need, and leverage TanStack Start’s loader-centric approach to compose data and components.

Key Takeaways

  • CSR (client-side rendering) in TanStack Start disables SSR, letting the client fetch top Pokemon data via a loader and Route.useLoaderData to supply data to CsrPage.
  • SSR variant in TanStack Start still uses the same loader-based data flow, but the page is server-rendered then hydrated on the client, evidenced by ' Bulbasaur' appearing in the HTML source on hard refresh.
  • Data-only SSR fetches data on the server without HTML payload, causing a small flash on load, and is ideal for dashboards needing secure server data before rendering.
  • React Server Components can be enabled with the Vite plugin and used in two modes: a low-level RSC render flow (renderServerComponent) and the Composite Component API for shell layouts with slots.
  • Composite Components return a shell with a client slot for interactive content, enabling a modular page structure without forcing client components for the whole page.

Who Is This For?

Frontend developers exploring TanStack Start, React Server Components, and SSR strategies who want concrete patterns (CSR, SSR, data-only SSR, RSC, and composite components) for data-heavy pages like dashboards or lists.

Notable Quotes

""You can see over here, if we look at the page source, we can do a find. \"Bulbasaur\" and it appears in the HTML. So it is rendering on the server and then passing it off to the client, and the client is actually going to do another re-render on the page. That's really important to know. That is the standard react hydration cycle, and it happens when you're not using RSCs.""
Illustrates the traditional SSR hydration cycle when RSCs are not used.
""The first thing you need to know is that I've enabled RSC support by bringing in the ViteJS plugin for RSC, and then simply enabling RSC on the TanStack Start Vite plugin.""
Shows how to enable React Server Components with TanStack Start.
""Composite Component API, that is unique and specific to TanStack Start.""
Introduces the specialized Composite Component API and its shell/slot pattern.

Questions This Video Answers

  • How does TanStack Start coordinate loaders with CSR vs SSR vs RSC approaches?
  • What is data-only SSR and when should you use it in TanStack Start?
  • What are the benefits of TanStack Start's Composite Component API for page shells and slots?
  • How do I enable React Server Components in a TanStack Start project using the Vite plugin?
TanStack StartReact Server ComponentsCSRSSRData-only SSRRSC renderServerComponentRSC Composite ComponentVite plugin for RSCloader architecturePokémon API example
Full Transcript
now that we have React Server Components and TanStack Start, let's talk about the five different ways to get data onto your page, including SSR, non SSR, RSC, all of the different variations. Let's get right into it. Now before we get into it, I'm gonna take a quick digression and just answer something that's been coming up a lot recently. I'm not sure who needs to hear this, but React Server Components were not the first time that React did server-side rendering, not by a long shot. React has always had server-side rendering even before NextJS. In fact, it's a pillar of the React architecture, that it is isomorphic, meaning that React can render in both the server context and in the client context. And that has been since day one. Next came around because before Next everyone was using their own DIY bespoke versions of SSR. Next, just standardized those with what they're now calling the "Pages Router" and then added RSC support with what they're now calling the "App Router". But at every point along that line, React has always been capable of being server-side rendered. It was just a question of how you want to do that server side rendering. So let's talk about the five different ways that you can do them with TanStack Start. Starting off with not at all the client side rendering version. So here is our example. It's five different ways to go and get the Pokemon list from the Pokemon API. You're not gonna see any difference in terms of the UI really, other than just explanations of what's going on. So what's interesting with the client side rendering version is that the request to the Pokemon API is made off of the client in a classic Single Page Application or SPA way, but it's incredibly clean in TanStack Start, as you'll see. Here is the "ssr-variants" project code. All of this is available to you for free in GitHub and the link in the description right down below. Of course, the index page simply has links to all of the variants. The one that we're looking at right now is the CSR page, and the critical component here is we've set SSR to false. That means that TanStack Start is not going to server side render this page. It's only going to send the code for the page out to the client, and the client is gonna do all the work. Now what happens here is that the TanStack Router then kicks in and the loader is fired, when the page first starts up, that loader goes and makes the fetchTopPokemon from the API. And then the page component, in this case, CsrPage uses Route.useLoaderData to just go and get that Pokemon data that the router has choreographed in terms of loading this route. So it doesn't really matter if this is a hard nav or a soft nav to this page. The effect is always gonna be the same. The router is gonna manage getting the data from the loader before it actually renders the page. All right. Now let's talk about the server side rendered version to this. So if I go over to "Server-side rendering" here, we're not actually going to server side render. That's actually a SPA nav between those two things. And in that case, TanStack Start is going to do exactly what we did with CSR, but just call it SSR. That's a feature that's not a bug If you do a hard nav, like what I'm gonna do now by refreshing the page, you can see over here, if we look at the page source, we can do a find. "Bulbasaur" and it appears in the HTML. So it is rendering on the server and then passing it off to the client, and the client is actually going to do another re-render on the page. That's really important to know. That is the standard react hydration cycle, and it happens when you're not using RSCs. So when you look at the RSC variants coming up, those are only ever going to render on the server and then get sent down to the client. All right, now let's go take a look over at the code. In this case, you might take a look at "ssr.tsx", and in this case, "ssr" is true, which is also the default. So you don't need to necessarily put that on there, but check out how we're doing the loader and the getting of the Pokemon. It's exactly the same between those two. How cool is that? Now there's another SSR variant called "data-only", and we actually had this before we had RSC. This is really cool, especially if you're doing something like a dashboard. Let me show you. So if I go back here to the homepage and I go to "Data-only SSR", and again, I do a hard refresh. You can see that there's a slight flash here as it brings in the data. So what's actually happening? Well, again, if we look at the page source. We can see that we have no HML for bor, but we do have data for Bulbasaur, so the data is being fetched on the server, then being sent down to the client for rendering, and that's why you see that little flicker there. Is that the server is getting the data, it's sending it down to the client, and then the client is the only place that's actually doing the rendering. Again, super handy for something like a dashboard where you don't want to go and CDN cache the contents of the page, but you do want to get data off the server, probably more securely than off the client and then pass it down to the client for rendering. So let's take a look at the code for this. Again, exactly as we had before, but in this case we're just saying SSR data only, but it's exactly the same code otherwise. All right, now let's talk about React Server Components or RSC support in TanStack Start. So the first thing you need to know is that I've enabled RSC support by bringing in the ViteJS plugin for RSC, and then simply enabling RSC on the TanStack Start Vite plugin. Super easy. Let's go take a look at the page and we'll try out "RSC - renderServerComponent". So this is using the RSC low level API. TanStack Start actually has two ways of doing RSC. You can do them in combination. You can do one or the other, or you can not use any RSC at all. Totally up to you. All the options are open. Let's go take a look at how this is done. So we'll go over here to "rsc-renderable.tsx". And we can see that we have a new component at the top of the page, PokemonServerList. And importantly, it's an async component. Just like you'd have in the NextJS App Router. We're gonna go and await fetchTopPokemon. Super easy way to go and get your data in here. Then we render those top Pokemon. And that server function uses renderServerComponent with that component. Just JSX, like you'd expect. And that gets back a renderable. We're gonna return that as PokemonList. And then you can start to see where this intersects with the architecture that we had before, we're using the same loader like we had before. SSR is true, and we are getting that Pokemon e, we're turning that as loader data, and then we break that out in our page, we get PokemonList back, and then we just simply drop that into our JSX wherever we want it. It's just a remarkably clean and simple architecture. Now if we go back to the route definition again, think about what the loader is doing here. It's just getting Pokemon renderable. It could actually go and get other data. It could render other RSCs. Whatever you want, you can go and just have as much of a combination of architecture as you want in that loader, and TanStack Start gonna do all of the work of coordinating all that. Now let's take a look at our final fifth version of this, and that is the Composite Component version. So I'll go over here to "RSC - createCompositeComponent", and as you can see exactly the same output as that we have before. But what in this case we're doing is we're returning a shell of a component to the page, and then the page can go and actually slot in dynamic components. In this case, we've created. Client slot where that composite component can go and take interactive components. So when you think about how you might want architecture, like a page shell, that's where you want to think about this. RSC composite API, that is unique and specific to TanStack Start. Let's take a look at the code. Over an "rsc-composite.tsx" We can see again the PokemonServerRows, just like we have before. An async function. Now in this getPokemonComposite server function. We call that createCompositeComponent and give it a component. This component is actually defined right here. It defines the shell of the layout and then takes any children and pops 'em in there wherever you want. You can also have as many slots as you want defined on these composite components. So you can have like a call to action section or an ad section or however you wanna lay out your shell. Then down on the createFileRoute, again, we're calling that loader. If you haven't noticed, loaders are really important here. We grab, in this case, the "src" for the composite component, and then we pass that to the CompositeComponent component as "src", and then we give it any kind of slots we have. In this case we have the children slot, so we just drop in a paragraph tag and that's what would go in there. If you have interactive components, those can go in there and you don't need to use "use client". But I'll get into more of that in another video where we really drill down into the differences between the Low Level API and the Composite Component API, and how and when we want to use those two. In the meantime, I hope this is giving you a nice overview of how TanStack Start manages server side rendering and RSCs and five different combinations of the above. If you have any questions or comments, be sure to put that in the comment section right down below. In the meantime, if you like this video, hit that like button. If you really like the video, hit the subscribe button and click on that bell. You notified the next time new Blue Collar Coder comes out.

Get daily recaps from
Jack Herrington

AI-powered summaries delivered to your inbox. Save hours every week while staying fully informed.