Why React Server Components?
Since React Server Components (RSC) became available, there has been ongoing discussion about whether they are good or bad. Some developers hate them, while others find them too complicated and confusing.
However, there are many benefits to using RSC. In this post, I want to highlight what they give us back and how they improve our codebase, especially when it comes to data fetching. With RSC, we can now fetch data on the server without needing useEffect
, then pass that data down to client components where it will be presented in the UI.
Let’s look at an example of how RSC can improve our code through a before-and-after scenario comparing data fetching in a client component versus using server components.
Client Component with useEffect
In a typical client component, you might use useEffect
to fetch data when the component mounts. Here’s an example:
function ClientComponent() {
const [data, setData] = useState<Data[] | null>(null)
const [loading, setLoading] = useState<boolean>(true)
const [error, setError] = useState<string | null>(null)
useEffect(() => {
const fetchData = async () => {
try {
const response = await fetch("/api/data")
const result: Data[] = await response.json()
setData(result)
} catch (error) {
console.error("Error fetching data:", error)
setError("Failed to fetch data")
} finally {
setLoading(false)
}
}
fetchData()
}, [])
if (loading) return <div>Loading...</div>
if (error) return <div>Error: {error}</div>
return (
<div>
{data?.map((item) => (
<div key={item.id}>{item.name}</div>
))}
</div>
)
}
export default ClientComponent
Notice what we have to do when fetching data on the client:
- We need to track 3 different states:
data
,loading
, anderror
- We must use the
useEffect
hook where we need to set the loading state, catch any possible errors, store the result data in thesetData
state if everything goes well, and use an empty dependency array to tell React that this logic should run when the component mounts - There’s a lot of boilerplate code we need to write to make it work as expected
Also notice in our JSX code how we need to handle the different states depending on what state we’re currently in.
React Server Component
React Server Components allow us to fetch data on the server side, which can improve performance by reducing the amount of JavaScript sent to the client and enabling better SEO. Here’s how you might implement the same functionality:
type Data = {
id: number
name: string
}
async function fetchData(): Promise<Data[]> {
try {
const response = await fetch("https://api.example.com/data")
return response.json()
} catch (error) {
console.error("Error fetching data:", error)
return []
}
}
async function ServerComponent() {
const data = await fetchData()
return (
<div>
{/* ChildComponent will be a client component */}
<ChildComponent data={data} />
</div>
)
}
export default ServerComponent
Look at how much code we can reduce by using server components!
We don’t even have to catch errors in this component if we don’t want to. By wrapping the ServerComponent
in an error boundary, the ErrorBoundary
component will handle the error for us.
If we want to handle the loading state, we can set up Suspense and stream the HTML down to the client.
The server component will be responsible for getting the data and passing it down to the client component where it will be presented in the UI. Not only do we have less code, but we also get improvements from both a UI/UX perspective and a technical perspective. We’re improving performance since the fetch call happens on the server instead of the client.
Conclusion
I’m not here to tell you that React Server Components are 100% perfect and solve every problem we had before. But I think they deserve more positive attention, and there are many benefits we get from them. I believe it’s just a matter of time until all React developers get used to the difference between client components and server components.
I hope that all React frameworks will support RSC in the future and that all React developers will have the same mental model when working in React codebases. It shouldn’t matter if we’re working with Next.js, TanStack, or React Router.
Today, Next.js and Waku are the frameworks that fully support server components as you’re reading this blog post.