Mastering SSR & CSR in Next.js with GraphQL – With and Without Apollo

Muhammad Hamza - Feb 22 - - Dev Community

When working with Next.js (13+) and GraphQL, choosing between Server-Side Rendering (SSR) and Client-Side Rendering (CSR) has a major impact on performance, SEO, and user experience.

So, what’s the right approach when fetching GraphQL data from a NestJS backend? Let’s break it down!

1️⃣ SSR vs. CSR – Key Differences in Next.js 13+

SSR (Server-Side Rendering)

  • Data is fetched on the server before rendering.
  • Ideal for SEO-heavy pages (e.g., blogs, product pages).
  • Implemented using Server Components or fetch() inside async functions in React Server Components.

CSR (Client-Side Rendering)

  • The page loads first, then fetches data on the client.
  • Suitable for dynamic dashboards and interactive UIs.
  • Implemented using useEffect() with fetch() or Apollo Client.

2️⃣ Using GraphQL in Next.js 13+ – Apollo vs. Fetch?

🔹 With Apollo Client (CSR & Hybrid SSR/CSR)

  • Ideal for apps needing real-time updates & caching.
  • Works well in Client Components, but requires extra setup for SSR.
  • Use @apollo/client with useQuery() in Client Components.

🔹 Without Apollo (Using fetch/GraphQL-request)

  • Simpler and better for SSR.
  • Use fetch() inside Server Components to get GraphQL data directly.
  • Reduces bundle size and avoids unnecessary dependencies.

3️⃣ How to Implement GraphQL Fetching in Next.js 13+?

✔️ For SSR (Recommended for SEO & Fast Performance)


// Using Server Component (App Router)
async function BlogPost({ params }: { params: { id: string } }) {
  const res = await fetch("https://api.example.com/graphql", {
    method: "POST",
    headers: { "Content-Type": "application/json" },
    body: JSON.stringify({ query: `{ post(id: "${params.id}") { title, content } }` }),
    cache: "no-store", // Avoids caching for fresh data
  });

  const { data } = await res.json();
  return <h1>{data.post.title}</h1>;
}

export default BlogPost;
Enter fullscreen mode Exit fullscreen mode

✔️ For CSR (Recommended for Interactive Dashboards)

"use client";
import { useQuery, gql } from "@apollo/client";

const GET_POST = gql`
  query GetPost($id: ID!) {
    post(id: $id) {
      title
      content
    }
  }
`;

function BlogPost({ id }: { id: string }) {
  const { data, loading, error } = useQuery(GET_POST, { variables: { id } });

  if (loading) return <p>Loading...</p>;
  if (error) return <p>Error fetching post</p>;

  return <h1>{data.post.title}</h1>;
}

export default BlogPost;
Enter fullscreen mode Exit fullscreen mode

4️⃣ When to Use What?

✔️ SEO-Focused Pages? → Use SSR (Server Components + fetch()).

✔️ Highly Interactive Dashboards? → Use CSR (Apollo Client + useQuery).

✔️ Hybrid Approach? → Preload critical data with SSR, hydrate client-side with Apollo.

🔹 Pro Tip: For frequently changing data, ISR (Incremental Static Regeneration) helps balance performance & freshness.

💡 Final Thoughts

GraphQL + Next.js (13+) is powerful, but choosing the right rendering strategy is crucial.

👉 What’s your go-to approach for using GraphQL in Next.js 13+? Let’s discuss in the comments! 🚀

. . . . . . . . . . . . . . . . . . . .