RSC Server Functions and Libraries

Overview of TanStack RSC

With the release of React Server Component's for TanStack Start, pretty much all of the major React meta-frameworks now support RSCs. While the release for TanStack has generally been met with an overwhelming positive reaction, there was some criticism of the decision to intentionally not support the use server directive. They have a few reasons for this:

Jack Herrington, who has done a lot of work with Start over the past year, summed up the argument in more detail in this tweet0:

Jack Herrington on Twitter / X

1. You are free to decide if it's "RSCs" for yourself of course. But it's not a protocol. Protocols like the File Transfer Protocol (FTP) or HTTP, have documented standards, grammar, compliance tests, RFCs, and are demonstrated as language neutral across all the popular…— Jack Herrington (@jherr) April 14, 2026

For most people building apps, this is two ways of boiling a fish. If you're building an app in Next.js or React Router, you use use server, If you're building an app in TanStack Start, you'll use the createServerFn function instead, they'll do mostly the same thing. Or your can just use tRPC/oRPC, REST with OpenAPI0, or GraphQL if that's what the shape of your system demands.

But one question that popped up during the discussions of the split between the use server directive in React and TanStack start server functions was that it would bascailly make having Use server directive functions exported from libraries impossible in TanStart. This question intrigued me a bit so I decided to investigate it.

Now, directly exporting a function that just uses the use server directive doesn't seem that useful. Simply exporting a normal node function for the user to wrap in a use server/createServerFn on the application side is simple enough, and you probably don't want the extra "magic" of use server hoisting the function onto an implicit RPC endpoint to be encapsulated on the npm package side. But one thing RSC's can do with use server is pass down a Server Function into a child component, or render a form that uses the Server Action as a Form Action. These seem to be the most useful cases so I decided to focus on those.

The experiment

My experiment resulted in this monorepo . It consists of one RSC package and four applications (Next, Waku, React Router v7 Framework Mode, TanStack React Start). It exports a write function that doubles an number and persists to a file, along with components that when combined together form a card that reads from the file with RSC and also displays a form that uses the write function as an action. There are two ways this comes together in the apps:

Reading the code, you can see while the Next, Waku, and React Router apps use the RSC's in the same way, Start is a bit more verbose as it uses it's RSC helpers to return components from Server Functions, where it can be loaded into the app with either TanStack Router loader's or, in the case of this app, use TanStack Query to cache the RSC's.

The results

Here's what we got:

So what's going on here?

Remember that use server will end up being translated to a POST0 call to the server, and mutates the file with the number times 2. After the mutation, you need to update the web page, and that's where the interesting differences begin to emerge. So, this is what the decomposed versions all do differently from the internal use server action:

It turns out if you make Server Functions without reference to your frameworks caching strategy, your results are not gonna be great, and that means that having an RSC encapsulate a use server function from an external package that expects to be framework agnostic probably isn't that useful. That's even leaving out the question of whether application devs want there NPM packages setting up hidden POST endpoints that are a bit to implicit for comfort.

Closing thoughts

So, given all of this, my personal guidelines if I was looking to publish an RSC package on NPM today:

This might change down the line, and I'm open to corrections on the points above if I missed or misunderstood something, but that's where I am right now.

RSC adoption has been pretty slow to my mind, even in the NextJS space, and I'd be of the opinion that they have been overhyped a bit. TanStack Router and Start's flexibility and no-compromises attitude to end-to-end type-safety, alongside Query's excellent handling of async state, have always seemed to be more useful to me then RSC's over the last 4 years.

To conclude then, RSC's exported from packages will have some restrictions vs ones defined in application, but I doubt they'll be a major issue for the React ecosystem. If your interested to read more on this topic, I'd like to shout out the list of links that Ryan posted 0, especially "Are Server Components Actually That Composable?" for a lot of good thinking on the benefits and limitations of RSC and how that's ultimately affected where TanStack landed.

Happy rendering your components where you deem most appropriate.