Next.js 15 Component Categorization Guide

This guide provides an overview of how components can be categorized in Next.js 15, including:

  1. Whether it's a Server or Client component

    In Server Components, JavaScript execution occurs on the server, and only the resulting HTML is sent to the client. For Client Components, both the HTML and JavaScript are sent to the client, where the JavaScript is executed to enable interactivity.

  2. The rendering strategy it uses
  3. The data fetching pattern it employs
  4. The route types it can be used for
  5. How to configure or switch between different types

Server Component

Rendering Strategy Data Fetching Pattern Route Type How to Configure
Static Site Generation (SSG) Use generateStaticParams to define dynamic routes; fetch data at build time Page, Layout, Loading, Error, etc. Default in the app directory. Implement generateStaticParams for dynamic routes. Source
Server-Side Rendering (SSR) Fetch data on each request using fetch with cache: 'no-store' Page, Layout, Loading, Error, etc. Use fetch with { cache: 'no-store' } or set export const dynamic = 'force-dynamic'. Source
Streaming Use asynchronous Server Components with React Suspense Page, Layout Define components or their children as async and utilize React's Suspense for progressive rendering. Source
Incremental Static Regeneration (ISR) Fetch data with fetch and specify revalidation interval using next: { revalidate } Page Use fetch with next.revalidate option to define the revalidation period. Source
Parallel Data Fetching Initiate multiple data fetches simultaneously using Promise.all Page, Layout Start data fetches in parallel within components to reduce loading times. Source
Sequential Data Fetching Fetch data in a specific order when requests depend on each other Page, Layout Structure data fetches sequentially when subsequent requests rely on previous ones. Source

Client Component

Rendering Strategy Data Fetching Pattern Route Type How to Configure
Client-Side Rendering (CSR) Fetch data using useEffect, SWR, React Query, etc. Any Add 'use client' directive at the top of the file; manage data fetching within client components. Source

This table now encompasses a broad range of rendering strategies and data fetching patterns available in Next.js 15, providing a detailed reference for implementation.

Additional Notes:

  1. Server Components:

    • Default in the App Router (`app` directory)
    • Can use async/await for data fetching
    • Cannot use hooks or browser-only APIs
  2. Client Components:

    • Must be explicitly marked with `'use client'`
    • Can use hooks and browser APIs
    • Should be used for interactivity and event listeners
  3. Rendering Strategies:

    • Static: Content generated at build time
    • Dynamic: Content generated on each request
    • Streaming: Content sent progressively as it's generated
  4. Data Fetching Patterns:

    • Server Components can fetch data directly using async/await
    • Client Components typically use hooks or libraries for data fetching
  5. Route Types:

    • Special file names in the App Router define different route types (e.g., `page.js`, `layout.js`, `loading.js`, `error.js`)
  6. Switching Between Types:

    • Server <-> Client: Add or remove `'use client'` directive
    • Static <-> Dynamic: Use or remove caching in `fetch`, or add `dynamic = 'force-dynamic'`
    • Enable Streaming: Use Suspense or make the component async

Remember, the behavior might differ between development and production environments. Always test in a production build to see the true behavior of your components.

Examples

Below are examples corresponding to each row of the table, demonstrating various rendering strategies and data fetching patterns in Next.js 15.

1. Server - Static Site Generation (SSG)

Description: Pre-renders pages at build time, suitable for content that doesn't change frequently.

Example:

// app/products/[id]/page.js

export async function generateStaticParams() {
  // Fetch product IDs to pre-render
  const res = await fetch('https://api.example.com/products');
  const products = await res.json();

  return products.map((product) => ({
    id: product.id.toString(),
  }));
}

export default async function ProductPage({ params }) {
  const res = await fetch(`https://api.example.com/products/${params.id}`);
  const product = await res.json();

  return (
    <div>
      <h1>{product.name}</h1>
      <p>{product.description}</p>
    </div>
  );
}

Configuration:

  • By default, components in the app directory are treated as static.
  • Implement generateStaticParams to define dynamic routes.

2. Server - Server-Side Rendering (SSR)

Description: Renders pages on each request, ensuring up-to-date content.

Example:

// app/users/[id]/page.js

export const dynamic = 'force-dynamic';

export default async function UserPage({ params }) {
  const res = await fetch(`https://api.example.com/users/${params.id}`, {
    cache: 'no-store',
  });
  const user = await res.json();

  return (
    <div>
      <h1>{user.name}</h1>
      <p>{user.email}</p>
    </div>
  );
}

Configuration:

  • Use fetch with { cache: 'no-store' } to fetch data on every request.
  • Set export const dynamic = 'force-dynamic' to ensure dynamic rendering.

3. Server - Streaming

Description: Streams parts of the UI to the client as they become ready, improving load times.

Example:

// app/dashboard/page.js

import { Suspense } from 'react';
import LoadingSpinner from './LoadingSpinner';
import UserStats from './UserStats';

export default function DashboardPage() {
  return (
    <div>
      <h1>Dashboard</h1>
      <Suspense fallback={<LoadingSpinner />}>
        <UserStats />
      </Suspense>
    </div>
  );
}

// app/dashboard/UserStats.js

export default async function UserStats() {
  const res = await fetch('https://api.example.com/user-stats');
  const stats = await res.json();

  return (
    <div>
      <h2>Stats</h2>
      <p>Posts: {stats.posts}</p>
      <p>Followers: {stats.followers}</p>
    </div>
  );
}

Configuration:

  • Define components or their children as async.
  • Utilize React's Suspense for progressive rendering.

4. Server - Incremental Static Regeneration (ISR)

Description: Allows static pages to be regenerated in the background at specified intervals.

Example:

// app/blog/[id]/page.js

export default async function BlogPostPage({ params }) {
  const res = await fetch(`https://api.example.com/blog/${params.id}`, {
    next: { revalidate: 60 },
  });
  const post = await res.json();

  return (
    <div>
      <h1>{post.title}</h1>
      <p>{post.content}</p>
    </div>
  );
}

Configuration:

  • Use fetch with next.revalidate option to define the revalidation period (in seconds).

5. Server - Parallel Data Fetching

Description: Initiates multiple data fetches simultaneously to reduce loading times.

Example:

// app/profile/page.js

export default async function ProfilePage() {
  const [userRes, postsRes] = await Promise.all([
    fetch('https://api.example.com/user/1'),
    fetch('https://api.example.com/user/1/posts'),
  ]);

  const [user, posts] = await Promise.all([userRes.json(), postsRes.json()]);

  return (
    <div>
      <h1>{user.name}</h1>
      <h2>Posts</h2>
      <ul>
        {posts.map((post) => (
          <li key={post.id}>{post.title}</li>
        ))}
      </ul>
    </div>
  );
}

Configuration:

  • Start data fetches in parallel within components using Promise.all().

6. Server - Sequential Data Fetching

Description: Fetches data in a specific order when requests depend on each other.

Example:

// app/orders/[id]/page.js

export default async function OrderPage({ params }) {
  const orderRes = await fetch(`https://api.example.com/orders/${params.id}`);
  const order = await orderRes.json();

  const productRes = await fetch(`https://api.example.com/products/${order.productId}`);
  const product = await productRes.json();

  return (
    <div>
      <h1>Order #{order.id}</h1>
      <p>Product: {product.name}</p>
      <p>Quantity: {order.quantity}</p>
    </div>
  );
}

Configuration:

  • Structure data fetches sequentially when subsequent requests rely on previous ones.

7. Client - Client-Side Rendering (CSR)

Description: Fetches data on the client side after the initial page load, suitable for user-specific data or frequent updates.

Example:

// app/search/page.js
'use client';

import { useState, useEffect } from 'react';

export default function SearchPage() {
  const [query, setQuery] = useState('');
  const [results, setResults] = useState([]);

  useEffect(() => {
    if (query.length > 2) {
      fetch(`/api/search?q=${query}`)
        .then((res) => res.json())
        .then((data) => setResults(data));
    }
  }, [query]);

  return (
    <div>
      <input
        type="text"
        value={query}
        onChange={(e) => setQuery(e.target.value)}
        placeholder="Search..."
      />
      <ul>
        {results.map((result) => (
          <li key={result.id}>{result.title}</li>
        ))}
      </ul>
    </div>
  );
}

Configuration:

  • Add 'use client' directive at the top of the file to designate it as a Client Component.
  • Manage data fetching within client components using hooks like useEffect.

In this example, as the user types into the search input, the query state updates. When the query length exceeds two characters, the useEffect hook triggers a fetch request to the /api/search endpoint with the query parameter. The results are then displayed in a list below the input field.

This approach is beneficial for scenarios where data needs to be fetched based on user interactions or when dealing with frequently changing data that doesn't require SEO indexing.

What does "Rendering" mean?

The term "rendering" can be ambiguous. It stands for javascript execution to generate the html markup. Here's a breakdown of the different rendering strategies in Next.js:

  1. Static Site Generation (SSG):

    • Description: Next.js generates the HTML for each page at build time. This pre-rendered HTML is then served to clients on request.
    • Use Cases: Ideal for pages with content that doesn't change frequently, such as blogs, marketing pages, or documentation.
    • Benefits: Fast load times and improved SEO due to pre-rendered content.
  2. Server-Side Rendering (SSR):

    • Description: Next.js generates the HTML for a page on each request. When a user requests a page, the server fetches necessary data, renders the page, and sends the HTML to the client.
    • Use Cases: Suitable for pages that require up-to-date data on each request, like user dashboards or news feeds.
    • Benefits: Ensures users receive the most current content.
  3. Incremental Static Regeneration (ISR):

    • Description: Allows Next.js to update static pages after the site has been built. You can specify a revalidation interval, and Next.js will regenerate the page in the background as needed.
    • Use Cases: Useful for large sites where some content updates periodically, like e-commerce product pages or blogs with frequent updates.
    • Benefits: Combines the performance benefits of SSG with the ability to update content without a full rebuild.
  4. Client-Side Rendering (CSR):

    • Description: The initial HTML is sent from the server with minimal content, and JavaScript on the client fetches data and renders the full content.
    • Use Cases: Suitable for highly interactive pages where SEO is not a primary concern, such as dashboards or applications requiring real-time updates.
    • Benefits: Enables rich interactivity and dynamic content updates on the client side.
  5. Streaming:

    • Description: Next.js can stream parts of the page to the client as they become ready, allowing for faster content display.
    • Use Cases: Beneficial for pages where certain components may take longer to load, enabling users to see and interact with parts of the page while other parts are still loading.
    • Benefits: Improves user experience by reducing perceived load times.

By understanding these rendering strategies, you can choose the most appropriate approach for each page or component in your Next.js application, balancing performance, SEO, and user experience.

loading.tsx

The loading.tsx file in Next.js 15 is a special file that creates a loading UI to be shown while page content is loading. Its rendering behavior can indeed vary depending on how it's used and the context in which it appears. Let's break this down:

  1. Basic Usage: When you create a loading.tsx file, it automatically becomes a React Suspense boundary for the page or layout it's associated with .
// app/dashboard/loading.tsx
export default function Loading() {
  return <p>Loading dashboard...</p>
}

In this basic case, the Loading component will be rendered on the server and sent immediately while the Dashboard page content is being generated.

  1. Instant Loading States: For static routes, the loading file might never be shown in production because the content is pre-rendered. However, it will still be used during development for a consistent experience .
  2. Streaming with Suspense: When used with streaming and Suspense, the loading state can be more granular:
// app/dashboard/page.tsx
import { Suspense } from 'react'
import { PostFeed, Weather } from './Components'

export default function Dashboard() {
  return (
    <section>
      <Suspense fallback={<p>Loading feed...</p>}>
        <PostFeed />
      </Suspense>
      <Suspense fallback={<p>Loading weather...</p>}>
        <Weather />
      </Suspense>
    </section>
  )
}

In this case, each Suspense boundary can show its own loading state, allowing parts of the page to load independently.

  1. Nested Routes: The rendering of loading.tsx can also vary in nested routes:
app/
├── dashboard/
│   ├── page.tsx
│   ├── loading.tsx
│   └── settings/
│       ├── page.tsx
│       └── loading.tsx

Here, when navigating from /dashboard to /dashboard/settings:

  • The Dashboard's loading.tsx will be shown instantly.
  • The Settings page's loading.tsx will replace the Dashboard's loading UI once it's ready.
  1. With Server Components vs Client Components:

  2. With Server Components: The loading UI is rendered on the server and streamed to the client, providing an instant loading state.

  3. With Client Components: The loading UI is rendered on the client, which might result in a slight delay before it appears.

Here's an example illustrating the difference:

// Server Component
// app/dashboard/page.tsx
import { Suspense } from 'react'
import Loading from './loading'
import DashboardContent from './DashboardContent'

export default function Dashboard() {
  return (
    <Suspense fallback={<Loading />}>
      <DashboardContent />
    </Suspense>
  )
}

// Client Component
// app/dashboard/client-page.tsx
'use client'

import { Suspense } from 'react'
import Loading from './loading'
import DashboardContent from './DashboardContent'

export default function Dashboard() {
  return (
    <Suspense fallback={<Loading />}>
      <DashboardContent />
    </Suspense>
  )
}

In the Server Component example, the Loading component will be rendered on the server and sent immediately. In the Client Component example, the Loading component will only be rendered after the JavaScript has loaded on the client side.

  1. With or Without Streaming:

  2. With Streaming: The loading state can be more granular, showing loading UI for specific parts of the page as they load.

  3. Without Streaming: The entire loading UI is shown until the full page content is ready.

To enable streaming for a dynamic route:

// app/dashboard/layout.tsx
export const dynamic = 'force-dynamic'

export default function DashboardLayout({ children }) {
  return <section>{children}</section>
}

In summary, the rendering of loading.tsx can vary based on:

  • Whether it's used in a static or dynamic route
  • Its position in the route hierarchy
  • Whether it's used with Server or Client components
  • Whether streaming is enabled
  • How it's used with Suspense boundaries

The flexibility of loading.tsx in Next.js 15 allows for creating sophisticated loading UIs that can adapt to different rendering scenarios, providing a smooth user experience across various parts of your application .