SyntaxStudy
Sign Up
Next.js Context, Hooks, and Sharing Data Across the Server
Next.js Beginner 1 min read

Context, Hooks, and Sharing Data Across the Server

React Context is not supported in Server Components because context relies on the React tree being available in the browser. If you need to share data across Server Components in the same request, use native JavaScript patterns instead. The React cache() function from 'react' memoises a function call across a single render pass, meaning multiple Server Components calling the same cached function will share the result without duplicating the work or the network request. For authentication and per-request data sharing in server-side code, Next.js recommends using the next/headers module (headers() and cookies() functions) along with the React cache() pattern. You create a cached function that reads the session from the cookie, calls your auth library, and returns the user object. Any Server Component that needs the current user imports and calls this function — React's deduplication guarantees it runs only once per request. Client-side state management with tools like Zustand, Jotai, or Redux Toolkit works normally within Client Components. The key insight is that these stores live in Client Component trees and should not be used to manage server-fetched data — that is better handled by the RSC cache and server fetching patterns. Use client state for UI state (open/closed, selected tab, form inputs) and server fetching for data from external sources.
Example
// lib/auth.ts — cached auth check shared across Server Components
import { cache } from 'react';
import { cookies } from 'next/headers';
import { verifySession } from '@/lib/session';

// cache() ensures this runs at most once per request
// even if called from multiple Server Components
export const getUser = cache(async () => {
  const cookieStore = cookies();
  const sessionToken = cookieStore.get('session')?.value;

  if (!sessionToken) return null;

  const session = await verifySession(sessionToken);
  return session?.user ?? null;
});

// app/dashboard/page.tsx
import { getUser } from '@/lib/auth';
import { redirect } from 'next/navigation';

export default async function DashboardPage() {
  const user = await getUser(); // cached — no duplicate DB query
  if (!user) redirect('/login');

  return <h1>Welcome, {user.name}</h1>;
}

// app/dashboard/layout.tsx
import { getUser } from '@/lib/auth';

export default async function DashboardLayout({
  children,
}: {
  children: React.ReactNode;
}) {
  const user = await getUser(); // same cached call — no extra query
  return (
    <div>
      <nav>Logged in as {user?.email}</nav>
      {children}
    </div>
  );
}