skip to content
Samuel Edwin's Website

Why I do server side rendering by default

/ 3 min read

The web development industry has been a pendulum that’s swinging back and forth in these two decades.

I remember how I learned web development using PHP in my college days.

When a request comes in, fetch the required data from MySQL and then render it in the HTML, done.

A few years later, Single Page Applications start to become popular which also results in increasing complexity.

Web apps need to show loading placeholders and do additional fetches from an API server.

Thankfully the pendulum swings back in the past couple years, making server side rendering a viable choice again.

Modern JS libraries like React, Vue and Svelte now have their full stack framework counterparts such as Next.js, Remix, Nuxt and SvelteKit which makes server side rendering possible.

This means developers have an option to do either fetch data from the client or the server when using those frameworks.

I strongly prefer to do server side rendering by default for the following reasons.

Server side rendering is simpler to implement

There are at least 2 steps needed in order to make client side data fetching work:

  1. Build an API endpoint to serve the resources
  2. Show a loading indicator on the client side and start fetching data
Client side data fetching
// /api/posts/route.ts
export async function GET(request: Request) {
// fetch the blog posts from db
}
// /app/blog/page.tsx
'use client'
export default function Page() {
// using React Query to fetch the data
const {data, isLoading} = useQuery({
queryKey: ['blog'],
queryFn: () => fetch('/api/posts')
})
if (isLoading) {
return <LoadingIndicator />
}
return (
<div>
{blogPosts.map(post => (
<div key={post.id}>
<h2>{post.title}</h2>
<div>{post.content}</div>
</div>
))}
</div>
)
}

How about server side rendering?

Server side rendering greatly simplifies the implementation code, especially with React Server Components in Next.js

export default function Page() {
const blogPosts = await getBlogPosts()
return (
<div>
{blogPosts.map(post => (
<div key={post.id}>
<h2>{post.title}</h2>
<div>{post.content}</div>
</div>
))}
</div>
)
}

Also a bonus point for not needing data fetching library such as React Query.

Performance

Users always want web pages to load as fast as possible. Let’s compare how client side data fetching vs server side rendering works and how they impact performance.

Server side rendering

server side rendering

It’s as simple as it can be. Request comes in and the server returns the HTML with required data.

One request from the browser is all that’s needed.

Client side data fetching

client side data fetching

As you can see, there are more steps involved in client side data fetching before the content is delivered to the user.

The data fetching activity only starts after the JS files have been loaded.

There’s a lot of round trip connections made from the browser to the server.

A slightly slow internet connection can significantly make the page load feels much slower.

Does that mean client side data fetching is useless now?

Not really, there are some cases where client side data fetching is still required.

One of such example is infinite scrolling.

When the user scrolls to the bottom, more content is loaded from server.

This is not possible to achieve by server side rendering alone, and a fetch request is required to load the next page.

In essence, I use client side data fetching only when server side rendering is not possible.