Are React Server Components a Mistake?

Adam Drake
9 min readMay 6, 2024

--

Produced by Artic Imps

I don’t ask this question lightly. I am a huge fan of React. I think the library itself and the ecosystem around it is amazing and I feel privileged I get to work with such a tool on a day to day basis. React has revolutionised the way Frontend work is done and the team behind it should be immensely proud of their achievement.

Having said that, I don’t believe everything they touch turns to gold. RSCs have been out a while now and as a React Developer I still haven’t had the need to use them. I wanted to share my experience I’ve had with the small exposure I’ve had with them and some conclusions I have reached.

Why Did the React Team Make Server Components?

First off, what was the problem that existed that motivated the React team to make Server Components in the first place. The primary answer seems to be around performance.

Traditional React apps receive a bundle of JS on the client side, which comprises of React and other application code, that React renders in the browser. When this is done on a large app with data fetching, not only does the user have to wait for a large bundle of JS to be sent to the client, but only when the app is loaded can the data fetching begin. This is not a good user experience as it often leads to users waiting a long time before seeing anything useful on their screens. (They should see things straight away as we live in an age where we expect to have everything we want instantly, and having to wait 1600ms is just damn right unacceptable.)

There have been a variety of solutions to this problem including code splitting, Server Side Rendering (SSR) and Static Site Generation (SSG). All effective and all suitable for specific use cases.

Code Splitting

Code splitting enables an App to split its bundled JS into smaller parts so depending on what section of the app the user is on they will only download the required JS for that section. This leads to quicker page loads.

SSR

Server side rendering renders the app on the server side to generate HTML which is then sent across the wire so the user sees the content as soon as it’s sent over. A <script> tag is still included because React still needs to be able to work on the client side for any interactive elements. Adding this interactivity to the page is know as hydration.

SSG

Static Site Generation happens at build time. Your pages get transformed into static html documents at build time so when users hit specific urls for your app an html document will be sent over the wire to the client side leading to much quicker load times. Perfect for sites like blogs where the content doesn’t changes often and each user should see the same content.

This is a very crude explanation of SSR and SSG. For a much better and fuller explanation I strongly suggest you check out this blog post from Josh Comeau which goes into far greater detail about all this and RSC.

RSC are the next evolution of this optimisation journey.

Theoretically It Makes Sense

In theory what the React team have tried to do and delivered makes sense. Who doesn’t want their Website or Application to be quick and performant? Who doesn’t want to make DB queries on the server side and save a bunch of needless round trips from client to server in order to serve the required content? The theory makes perfect sense. Where I think it goes wrong for me is in the implementation and the DX (Developer Experience).

Every time I made a component I had to decide if it’s a Server component or a Client component. The extra cognitive overhead is non-trivial.

Server Components vs Client Components

This was the first thing that put me off. Not only did I have to think about UX/UI, design, how to structure components, which components should hold logic and which should be presentational components, local state vs global state, useEffect side effects, memorisation… but now every time I made a component I had to decide if it’s a Server component or a Client component. The extra cognitive overhead is non-trivial.

OK… initially this didn’t seem like such a big deal. Highest level component can be a server component and it can do DB queries (actually really nice!) albeit I was having to use Nextjs to experience this (I’ll get to that in a minute). Makes sense so far. I have the data, now I want to start rendering it out in different parts of the UI. Ok so I render a container component (which is a child of the highest level component)… so is this a “Server Component” too? How to decide? Will it have any client side interactions? I don’t think so, at least not at the moment. Ok cool this can be a “Server Component” too!

React is over 10 years old and most React developers are used to working with “Client components” and things working client side. Why would this suddenly become the “opt-in” option and not the other way around?

Now comes an interactive component… right ok so now I have to explicitly state that this is a “Client component” by writing the "use client" directive at the top of the component. OK… tedious but fine. Ok this is rendering another component which isn’t interactive so is this now a “Client component” or a “Server component”? Can “Client components” have “Server component” children? Doesn’t seem to make sense to me. Do I have to write this "use client" on every child component of a “Client component”. Wait… Why am I doing all this again?

Check out this nice article from RedwoodJS which goes deep into their implementation of React Server Components. There is one drawing which just summed it up for me:

Number.tsx can be both a server component and a client component.

Number.tsx can be both a server component and a client component. I’m sorry but even though I’m sure it’s all very logical if you’re deep in the theory of RSCs, for a Frontend Developer who has tight deadlines and a ever growing queue of tasks to complete this just isn’t what you want to spend your time thinking about.

At this point where quite far down the component tree I have just ended up making everything a client component because the benefit potentially gained from making this a server component just isn’t worth the cognitive overhead required. In the end it’s just annoying because I have to remember to put "use client" at the top of every component.

Opting In for Client Components

This “opting in” for “Client components” is annoying and I don’t understand why it was this way round. React is over 10 years old and most React developers are used to working with “Client components” and things working client side. Why would this suddenly become the “opt-in” option and not the other way around? This decision doesn’t make sense to me.

RSCs Only Serve the Exceptional Cases

There are definitely good use cases for rendering components server side. Especially when it comes to bigger JS bundles. Being able to render these big bundles server side means we don’t have to send them over the wire so we can do some very nice things in our apps without having to compromise on bundle size.

When Vercel is sending 6MBs over the wire on their home site you have to start questioning things.

However, I thing this sort of thing is an exception rather than a rule. Most React Apps will be dynamic applications that are fine to render on the client side and do all CRUD related activities client side. If bundles are getting too big they can use code splitting to alleviate the size issue. They won’t be needing exceptionally large chunks of JS for special features.

Little side note: I really do question this supposed requirement that bundle sizes must be super small and everything must load almost instantly. See this great blog post from tonsky.me about the size of JS bundles in some very well know sites. When Vercel is sending 6MBs over the wire on their home site you have to start questioning things. Are we living in a world where people are just not practising what they preach?

So why make “Client components” “opt-it”? It seems to me many more developers will have to be writing "use client" than it would be if it was the other way round.

Forms

This one really shocked me. Everything above is annoying but you can get over it and continue with your work. However, when it comes to forms and the hoops you have to jump through just to submit a normal form using RSCs I decided RSCs weren’t for me. It seemed like forms were an after thought in this whole RSC transition.

In my experience forms can get pretty complex and there are a host of things that you have to take care of — validation, error messaging, clear UI/UX. Working with forms on client side is tricky enough.

With RSCs you get introduced to “server actions”. This will be a function that will be called on the server when the form is submitted. However, you have to make sure to use use server in the function body so it’s declared as a “server action”. This is associated to the <form> by the action attribute. Something like:

async function submitForm(formData: FormData) {
'use server'

await postMessage({
message: formData.get('message'),
name: formData.get('name'),
})
}

// Form Component with action attribute
<form action={submitMessageForm}>

However, there are some issues like not refreshing the whole page upon form submission, resetting the form, client side validation…

Basically in the end, it’s just easier to do the whole thing client side and use libraries like react-hook-form and validation libraries like Zod. There is so many client side things happening with forms it’s easier just to avoid RSCs completely in these scenarios if you ask me.

Am I Stuck In My Ways?

As much as I like complaining about RSCs, it does still occur to me that maybe I am just acting like an old man and I’m too stuck in my ways with React. Maybe I need to let go of my current habits and embrace the new! Maybe these RSCs are the eternal light and I am just blind to the greatness that they bring to the React world. I’ve seen enough devs I respect singing their praises so there is definitely something there. Whilst I don’t think this is the case I am open to this possibility.

Conclusion

With React-19 around the corner more and more frameworks are going to be including RSCs so maybe I should jump on the bandwagon. In all honesty I haven’t really had a use case where RSCs feel like the obvious solution. From my experience the apps I work on are fine performance wise. Users tend to care about UI/UX and the app doing what they expect in order to bring value to their lives. If they have to wait a few more seconds for something that is good, they are willing to wait.

I didn’t write this article lightly. I know how hard developers work and I can only imagine the sweat and tears that went into the release of RSCs. But I also feel it’s important that different developers share their experiences online so others in the community can get a broad perspective of how certain tools and libraries are landing with real developers.

Subscribe

If you liked this blog post you can subscribe to receive emails every time I publish. I publish every Monday morning at 8:30 Central European Time.

About me

I am a Frontend Developer working mainly with React and Typescript in my day-to-day professional life. I love exploring new tools and libraries and love the Javascript Ecosystem.

I like to write blog posts that share exciting new tools I’ve discovered, how-to articles, and also the occasional opinion piece.

I live in Prague in the Czech Republic with my family.

Check out the original blog post on my blog.

Check out my LinkedIn and Github if you are interested.

--

--

Adam Drake

I'm a Frontend Developer and I write about all things Frontend Related - Think lightly of yourself and deeply of the world. Based in Prague, CZ