Server-Only Context for React Server Components
`server-only-context` is a lightweight utility designed to provide request-scoped context specifically for React Server Components (RSC). It helps mitigate the problem of prop drilling in server-side rendering environments by leveraging React's experimental `cache` function to store and retrieve values associated with the current request. This ensures that context values are isolated between different concurrent server requests, which is crucial for multi-user applications. The current stable version, 0.1.0, indicates it's an early-stage project that is actively developed but may still be subject to changes. Its core differentiator is its exclusive design for the server-side, contrasting with traditional React Context which is primarily client-side or hydrates from server-rendered content. It offers a straightforward API to create getter/setter tuples for managing context values.
Common errors
-
TypeError: Cannot read properties of undefined (reading 'getCacheForType')
cause Attempting to use `server-only-context` functions within a client component or outside of the React Server Component environment.fixVerify that the component where `get*` or `set*` functions are called is a Server Component. Ensure no `"use client";` directive is present in the file or any parent component that directly uses this context. -
ReferenceError: getLocale is not defined
cause Incorrectly importing the getter/setter functions. They are named exports from your custom context file, not directly from `server-only-context`.fixEnsure you are importing `getLocale` and `setLocale` from the specific file where you defined and exported them (e.g., `import { getLocale } from '@/context/locale';`). -
MyComponent unexpectedly receives 'undefined' for context values after client navigation.
cause The context was not reset or correctly set in both the layout and page components during a client-side navigation, leading to stale or absent values.fixImplement `setContextValue(value)` calls in both your server-rendered layout and server-rendered page components to ensure the context is correctly initialized for all request paths, including those initiated by client navigation.
Warnings
- gotcha When navigating on the client side, layouts are not re-rendered by default in React. Therefore, you must explicitly set the context in both the page component and the layout component to ensure consistent values across client-side navigation.
- breaking This library relies on React's experimental `cache` function and requires `react@next` as a peer dependency. Future updates to React or changes in the `cache` API could introduce breaking changes to `server-only-context`.
- gotcha This library is strictly for server components. Attempting to use `get` or `set` functions within a client component will result in a runtime error, as `cache` is a server-only API.
- gotcha As a version 0.1.0 package, `server-only-context` is in an early development stage. While functional, it may not be fully battle-tested, and its API or internal implementation might evolve rapidly, potentially leading to breaking changes in minor versions.
Install
-
npm install server-only-context -
yarn add server-only-context -
pnpm add server-only-context
Imports
- serverContext
const serverContext = require('server-only-context');import serverContext from 'server-only-context';
- getters/setters
import { getLocale, setLocale } from '@/context/locale';
Quickstart
/* src/context/locale.ts */
import serverContext from 'server-only-context';
export const [getLocale, setLocale] = serverContext<string>('en');
/* src/context/user.ts */
export const [getUserId, setUserId] = serverContext<string>('');
/* src/app/layout.tsx */
// Example of a root layout that might set initial context
// import { setLocale, setUserId } from '@/context'; // In a real app, combine contexts into one file or use aliases
// export default function RootLayout({ children }: { children: React.ReactNode }) {
// // In a real scenario, you'd extract locale/userId from headers or cookies here
// setLocale('en-US');
// setUserId('guest');
// return <html><body>{children}</body></html>;
// }
/* src/app/[locale]/[userId]/page.tsx */
// This represents a server component page where context is set based on URL params
import { setLocale, setUserId } from '@/context/locale'; // Assume these are from a combined context file for brevity
import { getLocale, getUserId } from '@/context/user'; // Assume these are from a combined context file for brevity
interface UserPageProps {
params: {
locale: string;
userId: string;
};
}
async function MyComponent() {
// In a real app, this might be a child component deep in the tree
const locale = getLocale();
const userId = getUserId();
return (
<div>
<p>Hello {userId || 'Guest'}!</p>
<p>Current Locale is: {locale || 'Default'}</p>
<p>This data was fetched using server-only context.</p>
</div>
);
}
export default async function UserPage({ params: { locale, userId } }: UserPageProps) {
setLocale(locale);
setUserId(userId);
// Demonstrate subsequent access within the same request scope
const currentLocaleCheck = getLocale();
const currentUserIdCheck = getUserId();
console.log(`Context set to: locale=${currentLocaleCheck}, userId=${currentUserIdCheck}`);
return <MyComponent />;
}