Tanstack Server Components

By Jeremy Harland
Apr 19, 20263 min read0 views0 comments0 likes

Tanstack Start just released their implementation of React Server Components (RSC), the highly pushed feature by the Next.js team, with quite a different approach to its application.


What is RSC

RSC is the React feature that allows the server to do the rendering for the client, leading to less JS that the client has to download.
I won't debate on whether or not people require it, this site does not but I like to use this as a place to try new things.

TanStack vs Next.js

The team at TanStack have driven a client-first framework with the ability to opt into RSC where you see fit. Conversely, Next.js is RSC-first and then components are marked as use client when they are meant to be rendered on the client. This, by design, leads people to offload extra compute onto their server, which conveniently is likely to be Vercel.

At work I am dealing with a semi-large Next.js project with all the bells and whistles, and I come across this pattern that people seem to fall into:

Fetching data on the server then rendering some view component ✅

async function FaqPage() {
  let data: FaqContent;
  try {
    data = await getFaqContent();
  } catch (e) {
    redirect('/error');
  }

  return <FaqPage data={data} />;
}

export default FaqPage;

So far so good! Then diving into the actual Faq page, I will often see the opening line:

'use client'

Hindering all the setup involved with the power of RSC, likely because there is a button somewhere on that page. The nature of the framework being server-first strips the significance of RSC, as it is not a conscious thought that devs make when building.

Whereas on the TanStack side, I had a go converting the bulk of my pages here into server components, as most of my pages have no client interaction whatsoever, so it makes sense.

Here is my blog page using RSC now

const getBlogPageContent = createServerFn().handler(async () => {
  const Renderable = await renderServerComponent(<Blog />)
  return { Renderable }
})

export const Route = createFileRoute('/_layout/blog/')({
  loader: async () => {
    const { Renderable } = await getBlogPageContent()
    return { BlogPageContent: Renderable }
  },
  component: BlogPage,
})

function BlogPage() {
  const { BlogPageContent } = Route.useLoaderData()
  return <>{BlogPageContent}</>
}

Everything still reads like it did before, but it just feels more deliberate that I am loading things on the server for a purpose, and it is not just magic happening under the framework's hood. I am yet to do a more complex page that requires both client and server components, which will be a fairer test - but for now I'll just stick to drinking the TanStack Kool-Aid!


Comments

No comments yet. Be the first!