Jagadhiswaran Devaraj

Feb 25, 2025 • 4 min read

Client-Side vs. Server-Side Requests in Next.js (App Router)

How to Choose Between use client and use server for Optimal Performance, SEO, and Hydration Management

Introduction

When building web apps with Next.js using the App Router, you've probably faced the choice between fetching data on the client or server side. It’s one of those decisions that can make or break your app’s performance, SEO, and user experience. In this article, we’ll break down the difference between client-side and server-side requests, explore when to use use client and use server in the App Router, and dive deep into what’s really happening under the hood, including rendering techniques, hydration, strategies, and best practices.


Personal Experience: When I Moved from Server-Side to Client-Side Requests

During a recent project, I initially leaned heavily into server-side requests using use server in the App Router to handle data fetching, processing, and rendering. My goal was to maximize SEO benefits and deliver fully rendered HTML to users. However, as the project scaled and new features were added, I started noticing that too much workload on server actions led to performance bottlenecks.

Every new feature demanded more server-side processing, and our server response times began to increase. In production, this could have led to degraded user experiences, especially during peak traffic times. To prevent potential issues, I reevaluated our data fetching strategy and shifted non-essential requests to the client side.

By using use client for specific components that didn’t require pre-rendered data, we offloaded some of the server load, improved our app's responsiveness, and maintained a balance between performance and SEO. This change allowed our server to focus on critical tasks while keeping the UI snappy and dynamic.

This experience was a perfect example of why it’s important to balance server and client-side requests based on the needs of your application.


Client-Side Requests

What Are Client-Side Requests?

Client-side requests happen directly in the browser. When a page loads, your frontend code (typically with React) makes API calls to fetch data, then updates the DOM dynamically. It’s like telling the browser, “Hey, get this data and render it whenever you’re ready.”

How to Use Client-Side Requests in the App Router

In Next.js, with the App Router, client-side requests are handled with useEffectfetch, or data-fetching libraries like SWR or React Query. When using use client, the component is fully rendered on the client side, meaning the server only sends a lightweight HTML shell and the rest of the rendering and data fetching happens in the browser.

'use client';
import { useEffect, useState } from 'react';

export default function ClientComponent() {
  const [data, setData] = useState(null);

  useEffect(() => {
    fetch('/api/data')
      .then((res) => res.json())
      .then((data) => setData(data));
  }, []);

  if (!data) return <p>Loading...</p>;

  return <div>{data.message}</div>;
}

When to Use Client-Side Requests

  • Dynamic Interactions: Ideal when you need data based on user actions.

  • User-Specific Data: Perfect for personalized content (e.g., dashboards, profiles).

  • Reducing Server Load: When your server needs to focus on critical backend processes.


Server-Side Requests

What Are Server-Side Requests?

When you make server-side requests in the App Router, the server handles data fetching and rendering before sending the HTML to the browser. This approach is powerful for SEO and ensuring the user sees fully populated content immediately.

How to Use Server-Side Requests in the App Router

With use server, you can handle server-side data fetching in Next.js with enhanced control over caching and revalidation.

'use server';

export async function fetchData() {
  const res = await fetch('https://api.example.com/data', { cache: 'no-store' });
  return res.json();
}

When to Use Server-Side Requests

  • SEO-Driven Pages: Marketing pages, blogs, or anything SEO-heavy.

  • Initial Data Fetching: When you need data ready before the page loads.

  • Secure Data Handling: Ideal for fetching authenticated or sensitive data.


Understanding Hydration in Next.js

What is Hydration?

Hydration is the process where React attaches event listeners and reactivates a server-rendered HTML page to make it fully interactive. During hydration, React reconciles the static HTML with the client-side JavaScript components.

Why Avoid Excessive Hydration?

Excessive hydration can lead to performance issues because the browser has to reprocess the entire page to attach event listeners and make it dynamic. This can cause delays in interaction and layout shifts.

How to Avoid Hydration in Next.js

  1. Use Static Rendering with SSG: When pages are pre-rendered as static HTML, they require minimal or no hydration.

  2. Progressive Hydration: Hydrate components only when they become visible or needed.

  3. Selective Hydration: Split components and hydrate only necessary parts using frameworks like React Server Components.

  4. Avoid Client-Side Rendering When Not Needed: Keeping more components server-rendered can minimize the hydration process.

  5. Use Edge Functions and Streaming: Send HTML as a stream and progressively hydrate parts of the page.


Strategies and Best Practices

Key Strategies

  • Leverage use server for Critical Data: Use server actions for authentication and SEO-heavy content.

  • Use use client for Dynamic UI: Client-side requests are ideal for interactions that don't affect SEO.

  • Implement Caching and ISR: Improve performance by using cache and revalidate options in fetch requests.

  • Avoid Over-Hydration: Split large components and only hydrate what's necessary.

  • Utilize Edge Functions: Reduce latency by executing server-side code closer to the user.

Best Practices

  1. Choose the Right Data Fetching Strategy: Evaluate whether a request should be server or client-side based on SEO, performance, and data sensitivity.

  2. Cache Strategically: Use Next.js built-in caching with fetch options like cache: 'force-cache' or no-store for dynamic data.

  3. Optimize Hydration: Avoid unnecessary client-side rendering to improve load times.

  4. Test in Production-Like Environments: Load test server actions and assess client-side performance under realistic conditions.

  5. Monitor and Optimize: Use tools like Lighthouse and Vercel Analytics to continuously monitor performance.


Conclusion

Mastering client-side and server-side requests in Next.js, especially with the App Router, requires a deep understanding of how rendering, hydration, and data fetching work under the hood. By balancing the use of use client and use server, leveraging caching strategies, and following best practices, you can build fast, scalable, and SEO-friendly applications. The key is to experiment, monitor, and refine your approach as your application evolves, ensuring you provide the best possible experience for your users.

- Jagadhiswaran devaraj

Join Jagadhiswaran on Peerlist!

Join amazing folks like Jagadhiswaran and thousands of other people in tech.

Create Profile

Join with Jagadhiswaran’s personal invite link.

0

6

0