Retrieve data server side and save in context with Next.js

Next.js 13+ answer

Next.js experimental app folder now covers this use case perfectly well.

Passing values from RSC to client components via React context

  1. Fetch your data in a React Server Component (RSC) layout
  2. In this layout, render a React context with this value
  3. Consume this context in your application as you wish

You can of course set this context in an RSC further down the tree, for example if you need it only on a certain page or component of a page.

In this real-life example, I fetch a survey definition in a page RSC and pass it down to client-code via context. My context provider exposes a typed reusable hook.

Sample code for the React Server Component (here a page):

// /app/referer/page.tsx (Server Component)
import { headers } from 'next/headers'
export default async function Page() {
  const headersList = headers()
  const referer = headersList.get('referer')
  return (
    // we set a CLIENT context,
    // based on the SERVER context that we got via headers()
    <RefererProvider value={referer}>
      <InteractiveReferer />
    </RefererProvider>
  )
}

Sample code for the client context (don’t forget the "use client" directive):

// /components/InteractiveReferer.tsx (Client Component)
// use "client"
export const InteractiveReferer = () => {
  // this context has been initialized by a parent RSC
  // but we can access it as expected in a Client Component
  const referer = useRefererContext()
  const [clickedReferer, setClickedReferer] = useState(false)
  return (
    <button
      onCLick={() => {
        setClickedReferer(true)
      }}
    >
      Referer: {referer}
    </button>
  )
}

Passing values from RSC to other RSCs via caching

Another novelty of Next.js app folder, is that now, server-side code is not limited to page-level getServerSideProps anymore. Nested component can also be Server Components and trigger server calls. Therefore, you might sometimes not only want to setup a client-side context, but also a kind of server-side context, scoped to the current request.

I describe this use case extensively in this article. To sum it up, you can use React 18 cache function (undocumented at the time of writing) to achieve this goal.

I’ve crafted an open source demonstration of this pattern, which can be summarized by this code sample:

import { cache } from "react";

export const getServerContext = cache(() => ({
    // a dummy context for the demonstration
    createdAt: Date.now().toString(),
    calledByLayout: false,
    calledByPage: false,
    calledByNested: false
}))

You can mutate the returned value directly to store new information in this context.

A note on naming: I’ve be using the term “server context” to designate data stored in cache that acts as a context. It’s because it’s the server-side equivalent of the “client” context.
However, “Request cache” is perhaps more suited, as “server context” may be used for an other purpose in future versions of Next.js and React (sharing data between RSC and client components).

Leave a Comment

tech