Server vs Client Side in Nextjs

In Next.js 14 App Router, you have the flexibility to use both server-side and client-side components. Understanding when to use each is crucial for building a fast, SEO-friendly, and interactive application. In this tutorial, we'll explore when to use server-side vs client-side components, how to mix them, and the SEO and performance considerations that come with each.

Folder Structure Overview

Hereโ€™s a basic folder structure of a Recipe App where both server and client components are used:

src/
โ””โ”€โ”€ app/
    โ”œโ”€โ”€ layout.tsx           # Server-side layout
    โ”œโ”€โ”€ recipes/
    โ”‚   โ”œโ”€โ”€ page.tsx         # Server-side component for recipe listing
    โ”‚   โ””โ”€โ”€ [id]/
    โ”‚       โ””โ”€โ”€ page.tsx     # Client-side component for recipe detail
    โ””โ”€โ”€ recipes-interactive/
        โ””โ”€โ”€ page.tsx         # Client-side component with interactivity

When to Use Server-Side Components

Server-side components are ideal when you need to fetch data or perform heavy computations before rendering the page. They can improve performance and SEO because the HTML is generated on the server and sent to the browser, making the page content available to search engines.

Use Case: Fetching data for a list of recipes from the database.

Hereโ€™s how you can implement a server-side component to fetch and display a list of recipes:

// src/app/recipes/page.tsx
import { fetch } from 'next/navigation';

export default async function RecipesPage() {
  const recipes = await fetch('https://api.example.com/recipes').then(res => res.json());

  return (
    <section>
      <h2>Recipe Listings</h2>
      <ul>
        {recipes.map(recipe => (
          <li key={recipe.id}>{recipe.name}</li>
        ))}
      </ul>
    </section>
  );
}

Why Server-Side?:

  • Data fetching is done server-side, ensuring that the recipe list is ready before the page is served.
  • SEO benefits: Search engines can index the content because itโ€™s delivered as part of the initial HTML.

Final HTML output for /recipes:

<html lang="en">
  <body>
    <section>
      <h2>Recipe Listings</h2>
      <ul>
        <li>Chocolate Cake</li>
        <li>Pasta Alfredo</li>
        <li>Grilled Chicken</li>
      </ul>
    </section>
  </body>
</html>

When to Use Client-Side Components

Client-side components are useful when you need interactivity or when youโ€™re dealing with state that only exists in the browser. Unlike server-side components, client-side components execute JavaScript in the browser after the page loads, which can delay content rendering but is essential for dynamic interactions.

Use Case: Handling interactive recipe forms where users can add or update a recipe.

Hereโ€™s how you can use a client-side component:

// src/app/recipes/[id]/page.tsx
'use client';

import { useState } from 'react';

export default function RecipeDetail({ params }: { params: { id: string } }) {
  const [recipeName, setRecipeName] = useState('');

  const handleSubmit = (e: React.FormEvent) => {
    e.preventDefault();
    console.log(`Recipe updated: ${recipeName}`);
  };

  return (
    <section>
      <h2>Update Recipe: {params.id}</h2>
      <form onSubmit={handleSubmit}>
        <label htmlFor="recipe-name">Recipe Name:</label>
        <input
          type="text"
          id="recipe-name"
          value={recipeName}
          onChange={(e) => setRecipeName(e.target.value)}
        />
        <button type="submit">Update</button>
      </form>
    </section>
  );
}

Why Client-Side?:

  • Interactivity: Client-side components allow users to interact with forms and update data dynamically.
  • State management: Components handle browser-specific state (like form input) that doesnโ€™t need to be processed by the server.

Final HTML output for /recipes/[id] (after the JavaScript runs):

<html lang="en">
  <body>
    <section>
      <h2>Update Recipe: chocolate-cake</h2>
      <form>
        <label for="recipe-name">Recipe Name:</label>
        <input type="text" id="recipe-name" value="Chocolate Cake" />
        <button type="submit">Update</button>
      </form>
    </section>
  </body>
</html>

Mixing Server and Client Components

Next.js allows you to mix server and client components, creating pages that leverage the best of both worlds. For example, you can fetch static data on the server and handle client-side interactivity on the same page.

Use Case: Displaying a static recipe while allowing users to favorite it interactively.

// src/app/recipes-interactive/page.tsx
'use client';

import { useState } from 'react';

export default async function InteractiveRecipePage() {
  const recipe = await fetch('https://api.example.com/recipes/1').then(res => res.json());
  const [favorited, setFavorited] = useState(false);

  const toggleFavorite = () => {
    setFavorited(!favorited);
  };

  return (
    <section>
      <h2>{recipe.name}</h2>
      <p>{recipe.description}</p>
      <button onClick={toggleFavorite}>
        {favorited ? 'Unfavorite' : 'Favorite'}
      </button>
    </section>
  );
}

Why Mix?:

  • Server-side for static content: The recipe data is fetched on the server, ensuring fast loading and SEO benefits.
  • Client-side for interactivity: The favorite button is interactive, updating the UI without needing a page reload.

Final HTML output for /recipes-interactive:

<html lang="en">
  <body>
    <section>
      <h2>Chocolate Cake</h2>
      <p>This is a delicious chocolate cake recipe.</p>
      <button>Favorite</button>
    </section>
  </body>
</html>

Performance Considerations

When deciding between server-side and client-side components, performance plays a key role:

  • Server-side components generate content on the server, which reduces the amount of JavaScript needed and improves initial load times.
  • Client-side components can add interactivity but can slow down the page if too much logic or rendering happens client-side.

Best Practice: Use server-side components for static or heavy data-fetching tasks, and reserve client-side components for interactivity or tasks that require browser state (like form submissions or button clicks).


SEO Considerations

  • Server-Side Components: Since the content is rendered on the server, search engines can crawl and index the page easily. This makes server-side components ideal for pages that need to rank well in search engines (like blog posts or recipe listings).

    Example: The recipe listings page is perfect for server-side rendering because it contains static data that benefits from SEO.

  • Client-Side Components: Client-rendered content might not be indexed properly by search engines because it relies on JavaScript to load. For example, pages that rely heavily on dynamic content or interactive features may have limited SEO value.

    Example: Interactive forms or real-time features (like chat) are best handled client-side, but they wonโ€™t contribute much to SEO.


SEO and Meta Tags

When using server-side components, you can include SEO meta tags directly within your page.

// src/app/recipes/page.tsx
import { Metadata } from 'next';

export const metadata: Metadata = {
  title: 'Recipes - Recipe App',
  description: 'Find and share the best recipes on Recipe App.',
};

export default async function RecipesPage() {
  // Fetch and render recipes
}

This ensures that meta tags are included in the initial HTML response, improving SEO performance.


Summary

In Next.js 14 App Router, deciding between server-side and client-side components depends on your app's requirements:

  • Server-side components are great for SEO, static data, and initial page rendering.
  • Client-side components excel at handling interactivity and browser-specific tasks like form inputs.
  • You can also mix server and client components to combine static content with dynamic functionality, making your app both fast and interactive.

By choosing the right component for the task, you can optimize your app for both performance and user experience.

Next.js
Clap here if you liked the blog