20

Next13 was released a week ago, and I am trying to migrate a next12 app to a next13. I want to use server-side components as much as possible, but I can't seem to use

import { createContext } from 'react';

in any server component.

I am getting this error:

Server Error
Error: 

You're importing a component that needs createContext. It only works in a Client Component but none of its parents are marked with "use client", so they're Server Components by default.

   ,----
 1 | import { createContext } from 'react';
   :          ^^^^^^^^^^^^^
   `----


Maybe one of these should be marked as a client entry with "use client":

Is there an alternative here or do I have to resort to prop drilling to get server-side rendering?

juliomalves
  • 42,130
  • 20
  • 150
  • 146
Tomer Almog
  • 3,604
  • 3
  • 30
  • 36
  • See [this older question](https://stackoverflow.com/questions/66106408/retrieve-data-server-side-and-save-in-context-with-next-js/75533592#75533592), I've provided an updated answer. In short: use React 18 "cache" function. This usage is currently undocumented (Next.js only shows how to use cache for data fetching) however it is confirmed that this is currently the right way to store data in the request-scoped server-side context. You can also render a client context from an RSC is that's what you need instead. – Eric Burel Feb 22 '23 at 16:00

4 Answers4

20

This is a new feature from React's SSR to recognize whether a component is client-side or server-side. In your case, createContext is only available on the client side.

If you only use this component for client-side, you can define 'use client'; on top of the component.

'use client';

import { createContext } from 'react';

You can check this Next.js document and this React RFC for the details

Nick Vu
  • 14,512
  • 4
  • 21
  • 31
  • 7
    Thanks, yes I know... the point of the question is how to pass context in server components, without using `use client` – Tomer Almog Nov 05 '22 at 00:04
9

It seems like I can use createServerContext

import { createServerContext } from 'react';

If you're using Typescript and React 18, you'll also need to add "types": ["react/next"] to your tsconfig.json compiler options, since this is a not-yet-stable function.

Zack
  • 941
  • 9
  • 14
Tomer Almog
  • 3,604
  • 3
  • 30
  • 36
  • 4
    Which React version are you using? I'm getting a `'"react"' has no exported member named 'createServerContext'. Did you mean 'createContext'?` – Rijk Nov 22 '22 at 13:38
  • 1
    I am also unable to import it. How did you did it? – Niklas Nov 23 '22 at 20:15
  • This was removed in the next version of React. I am just drilling props like it is 2016 again... – Tomer Almog Dec 21 '22 at 17:58
  • 3
    @Rijk `@types/react` classified this function as a "next" function, so to access it in Typescript, you need to add `"types": ["react/next"]` to your `compilerOptions` in `tsconfig.json` – Zack Jan 03 '23 at 00:34
  • 1
    This is out of date, you should instead use React 18 `cache` function, which handles a server-side, request scoped context for you. – Eric Burel Feb 22 '23 at 16:00
  • *Where is the official documentation of `createServerContext`?* – Mir-Ismaili Jun 05 '23 at 13:33
8

I've made a tiny package to handle context in server components, works with latest next.js, it's called server-only-context:

https://www.npmjs.com/package/server-only-context

Usage:

import serverContext from 'server-only-context';

export const [getLocale, setLocale] = serverContext('en')
export const [getUserId, setUserId] = serverContext('')
import { setLocale, setUserId } from '@/context'

export default function UserPage({ params: { locale, userId } }) {
  setLocale(locale)
  setUserId(userId)
  return <MyComponent/>
}
import { getLocale, getUserId } from '@/context'

export default function MyComponent() {
  const locale = getLocale()
  const userId = getUserId()

  return (
    <div>
      Hello {userId}! Locale is {locale}.
    </div>
  )
}

This is the code for it, it's really simple:

import 'server-only'
import { cache } from 'react'

export default <T>(defaultValue: T): [() => T, (v: T) => void] => {
  const getRef = cache(() => ({ current: defaultValue }))

  const getValue = (): T => getRef().current
  
  const setValue = (value: T) => {
    getRef().current = value
  }

  return [getValue, setValue]
}
2

According to Next.js 13 beta documentation, you cannot use context in Server Components:

In Next.js 13, context is fully supported within Client Components, but it cannot be created or consumed directly within Server Components. This is because Server Components have no React state (since they're not interactive), and context is primarily used for rerendering interactive components deep in the tree after some React state has been updated

However, there are alternative ways to handle data in the new approach, depending on your case. F.e. if you fetched the data from the server in a parent component and then passed it down the tree through Context, you can now fetch the data directly in all the components that depend on this data. React 18 will dedupe (de-duplicate) the fetches, so there are no unnecessary requests.

There are more alternatives in the documentation.

Filip Kowal
  • 322
  • 1
  • 3
  • Thank you! that part of the documentation wasn't there two weeks ago! – Tomer Almog Nov 18 '22 at 17:28
  • Note that at the time of writing, the documentation only covers data fetching use cases (which are already relevant for many situations), not caching generic values (say a GraphQL client), however it works the same. [This part of the doc about per-request caching](https://beta.nextjs.org/docs/data-fetching/caching#per-request-caching) is relevant. – Eric Burel Feb 22 '23 at 16:03