skip to Main Content

When we try to use a ThemeProvider(as shown below) in NextJS, we need to declare this Provider as a client component. But in NextJS docs it is said that if we declare a component as a client component all other modules imported into it, including child components, are considered part of the client bundle – and will be rendered by React on the client.

Since our whole application component live inside the provider, does it mean all of the child component would behave as client comps. If that’s the case, doesn’t it completely eliminate SSR using NextJS? I’m curious about this interaction and how it aligns with the SSR and CSR principles that Next.js is built upon.

//layout.tsx
import { Provider } from './provider'
import '@/styles//globals.css'
import type { Metadata } from 'next'
import { Inter } from 'next/font/google'

const inter = Inter({ subsets: ['latin'] })

export const metadata: Metadata = {
  title: 'Create Next App',
  description: 'Generated by create next app',
}

export default function RootLayout({
  children,
}: {
  children: React.ReactNode
}) {
  return (
    <html lang="en">
      <body className={inter.className}>
        <Provider attribute="class" defaultTheme="dark">
          {children}
        </Provider>
      </body>
    </html>
  )
}

//provider.tsx
'use client'

import { ThemeProvider as NextThemesProvider } from "next-themes"
import { type ThemeProviderProps } from "next-themes/dist/types"
import { NextUIProvider } from "@nextui-org/react"
 
export function Provider({ children, ...props }: ThemeProviderProps) {
  return (
    <NextUIProvider>
      <NextThemesProvider {...props}>
        {children}
      </NextThemesProvider>
    </NextUIProvider>
  )
}

I am asking this question as a newbie in NextJS, so please excuse any technical inaccuracies or lack of experience in my query.

2

Answers


  1. Chosen as BEST ANSWER

    In the updated doc itself it has been clarified later on in the composition pattern

    In summary:

    If you need to nest Server Components within Client Components, you can pass Server Components as props to Client Components. A common pattern is to use the React children prop to create a "slot" in your Client Component.

    When passing a child component or prop to the client component, the Client Component has no knowledge of what the child is. Its responsibility is solely to determine where the child component will be placed. As a result, the client component can effectively wrap both client & server component or accept them as a prop.

    // This pattern works:
    // You can pass a Server Component as a child or prop of a
    // Client Component.
    import ClientComponent from './client-component'
    import ServerComponent from './server-component'
    
    // Pages in Next.js are Server Components by default
    export default function Page() {
      return (
        <ClientComponent>
          <ServerComponent />
        </ClientComponent>
      )
    }
    

    Only restriction while using both Client and Server component is that we cannot **import** the server component inside of client component, so this code will not work

    'use client'
    
    // You cannot import a Server Component into a Client Component.
    import ServerComponent from './Server-Component'
    
    export default function ClientComponent({ children }) {
      const [count, setCount] = useState(0)
    
      return (
        <>
          <button onClick={() => setCount(count + 1)}>{count}</button>
    
          <ServerComponent />
        </>
      )
    }
    

  2. Your assumption that we need to use client component for creating a context is correct.

    So to create a Provider, we need to make it a client component like:

    "use client"
    
    import React, {createContext} from "react";
    
    const ThemeContext = createContext({})
    
    export default function ThemeProvider({children}){
      return (
        <ThemeContext.Provider value="dark">
          {children}
        </ThemeContext.Provider>
      )
    }
    

    But you can import this client component inside a server component without any issue, like:

    import ThemeProvider from './providers';
    
    export default function RootLayout({
      children,
    }: {
      children: React.ReactNode;
    }) {
      return (
        <html>
          <body>
            <ThemeProvider>{children}</ThemeProvider>
          </body>
        </html>
      );
    }
    

    Without the use client, this component is treated as a server component.


    On a side note, server components are new. So if you are trying to import a provider from a third party library, chances are, they are not using "use client" yet.

    If that’s the case, if you try to import that provider into your server component, that shall throw error: Error: "createContext" can't be used in Server Components.

    To solve this, you need to wrap the third-party provider inside a client component, before using it in a server component.

    "use client"
    
    import React from "react";
    import { ThemeProvider } from 'theme-package';
    import { ThirdPartyProvider } from 'third-party-package';
    
    export default function MyAppProvider({children}){
      return (
        <ThemeProvider>
          <ThirdPartyProvider>
            {children}
          </ThirdPartyProvider>
        </ThemeProvider>
      )
    }
    

    This MyAppProvider will be rendered only on the client (meaning, only in client bundle), even thought it has ThirdPartyProvider (which is a server component).

    Once "use client" is defined in a file, all other modules imported into it, including child components, are considered part of the client bundle.

    https://nextjs.org/docs/getting-started/react-essentials#the-use-client-directive

    If you import a server component into a client component, that server component will be rendered as a client component.

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search