Using React.Context with Nextjs13 server-side components [duplicate]
Asked Answered
K

4

30

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?

Kristiankristiansand answered 4/11, 2022 at 1:56 Comment(1)
See this older question, 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.Thimbleweed
K
15

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.

Kristiankristiansand answered 4/11, 2022 at 2:30 Comment(8)
Which React version are you using? I'm getting a '"react"' has no exported member named 'createServerContext'. Did you mean 'createContext'?Telepathy
I am also unable to import it. How did you did it?Kink
This was removed in the next version of React. I am just drilling props like it is 2016 again...Kristiankristiansand
@Telepathy @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.jsonInternationale
This is out of date, you should instead use React 18 cache function, which handles a server-side, request scoped context for you.Thimbleweed
Where is the official documentation of createServerContext?Matlock
⚠️ ServerContexts are deprecated! ⚠️Alimentary
I don't see anywhere where it is depreciated. You can import it in the latest version of NextJS, however, there is no context consumer, so it doesn't work. Here is only doc on twitter - x.com/sebmarkbage/status/1587615489605369861?s=20Urmia
P
29

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

Polis answered 4/11, 2022 at 2:45 Comment(1)
Thanks, yes I know... the point of the question is how to pass context in server components, without using use clientKristiankristiansand
K
15

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.

Kristiankristiansand answered 4/11, 2022 at 2:30 Comment(8)
Which React version are you using? I'm getting a '"react"' has no exported member named 'createServerContext'. Did you mean 'createContext'?Telepathy
I am also unable to import it. How did you did it?Kink
This was removed in the next version of React. I am just drilling props like it is 2016 again...Kristiankristiansand
@Telepathy @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.jsonInternationale
This is out of date, you should instead use React 18 cache function, which handles a server-side, request scoped context for you.Thimbleweed
Where is the official documentation of createServerContext?Matlock
⚠️ ServerContexts are deprecated! ⚠️Alimentary
I don't see anywhere where it is depreciated. You can import it in the latest version of NextJS, however, there is no context consumer, so it doesn't work. Here is only doc on twitter - x.com/sebmarkbage/status/1587615489605369861?s=20Urmia
E
12

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]
}
Effect answered 14/2, 2023 at 22:7 Comment(2)
This is sweet, but how does it work? I've never seen the React cache export before.Tobias
cache uses context under the hood. dev.to/jdgamble555/…Urmia
C
4

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.

Carrel answered 18/11, 2022 at 16:46 Comment(2)
Thank you! that part of the documentation wasn't there two weeks ago!Kristiankristiansand
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 is relevant.Thimbleweed

© 2022 - 2024 — McMap. All rights reserved.