Back to Rules
React96% popularity

React Server Components Data Fetching Patterns

Master async/await patterns in Server Components for efficient data loading and caching.

React TeamUpdated Feb 10, 2024

Overview

React Server Components introduce a new mental model for data fetching where components themselves are async functions that can await data directly. This represents a fundamental shift from the useEffect + API call pattern to co-located data fetching where the component that needs data is responsible for fetching it. The async/await pattern in Server Components eliminates the waterfall problem where nested components each fetch their own data sequentially. By using Promise.all or simply awaiting multiple independent data sources in parallel at the component level, you can fetch all required data in a single round trip time. This pattern naturally encourages optimizing data fetching at the component boundary rather than in effects. Caching strategies become critical with Server Components. The fetch Web API is extended in Next.js to support automatic request deduplication and multiple cache options. Using fetch with cache: 'force-cache' (default) caches responses indefinitely, while cache: 'no-store' bypasses all caching for fresh data. For database queries without fetch, Next.js provides unstable_cache for caching arbitrary function results. Streaming with Suspense enables progressive loading where parts of the UI render as their data becomes available rather than waiting for all data. This dramatically improves Time to First Byte and allows showing skeleton loaders or fallback content immediately while slower data sources complete. The combination of streaming and selective hydration ensures the application feels responsive even when some data is slow.

Code Example

.reactrules
import { db } from '@/lib/db';
import { cache } from 'next/cache';
import { Suspense } from 'react';
import { Skeleton } from '@/components/ui/skeleton';

// Cached data function
const getProjects = cache(async () => {
  return db.project.findMany({
    orderBy: { updatedAt: 'desc' },
    take: 20,
    include: { owner: true, _count: { select: { tasks: true } } },
  });
});

const getCategories = cache(async () => {
  return db.category.findMany({
    include: { _count: { select: { projects: true } } },
  });
});

// Parallel data fetching
async function DashboardData() {
  const [projects, categories] = await Promise.all([
    getProjects(),
    getCategories(),
  ]);

  return (
    <div className="grid grid-cols-1 md:grid-cols-2 gap-6">
      <ProjectList projects={projects} />
      <CategoryList categories={categories} />
    </div>
  );
}

// Streaming with Suspense
export default function DashboardPage() {
  return (
    <main className="container py-8">
      <h1 className="text-3xl font-bold mb-8">Dashboard</h1>

      <Suspense fallback={<DashboardSkeleton />}>
        <DashboardData />
      </Suspense>
    </main>
  );
}

function DashboardSkeleton() {
  return (
    <div className="grid grid-cols-1 md:grid-cols-2 gap-6">
      <Skeleton className="h-64" />
      <Skeleton className="h-64" />
    </div>
  );
}

// Individual data fetching components
async function ProjectList({ projects }: { projects: any[] }) {
  return (
    <section>
      <h2 className="text-xl font-semibold mb-4">Recent Projects</h2>
      <ul className="space-y-3">
        {projects.map((project) => (
          <li key={project.id} className="border rounded-lg p-4">
            <h3 className="font-medium">{project.name}</h3>
            <p className="text-sm text-muted">
              {project._count.tasks} tasks • Updated {project.updatedAt}
            </p>
          </li>
        ))}
      </ul>
    </section>
  );
}

More React Rules

REACT
94%

React Component Testing with Testing Library

Test behavior over implementation. Use Testing Library queries to interact with components as users would.

testingreacttesting-library
import { render, screen, fireEvent, waitFor } from '@testing-library/react';
import userEvent from '@testing-library/user-event';
import { LoginForm }...
Jan 28, 2024by Kent C. Dodds
View Rule
REACT
95%

React Performance Optimization Patterns

Use React.memo, useMemo, and useCallback correctly to prevent unnecessary re-renders.

reactperformanceoptimization
import React, { useState, useMemo, useCallback, memo, useEffect } from 'react';

// Expensive calculation helper
function calculateExpensiveValue(item...
Jan 25, 2024by Dan Abramov
View Rule
REACT
96%

React Form Handling with React Hook Form

Build performant forms with React Hook Form using uncontrolled components and validation.

reactformsreact-hook-form
import { useForm, Controller } from 'react-hook-form';
import { zodResolver } from '@hookform/resolvers/zod';
import { z } from 'zod';
import DatePick...
Mar 22, 2024by Beier(Bill) Luo
View Rule