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.
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.