MENU
Composition Tips
A Next.js developer must decide on how to mix server components and client components in an application. This section provides a few guiding tips on this aspect.
Tip 1: To share data between components, use cached fetch().
Rather than relying on Redux / React Context (which isn't accessible on the server) or passing data down through props, you can leverage fetch() or React's cache() to retrieve data directly in the components that need it. React enhances fetch() by automatically caching requests, and when fetch isn't suitable, you can use cache() to achieve similar results.
Tip 2: To keep code out of client components, use the 'server-only' Node package.
Code that was intended to run exclusively on the server can sometimes unintentionally end up being executed on the client.
npm install server-onlyimport 'server-only'
export async function getData() {
const res = await fetch('https://external-service.com/data', {
headers: {
authorization: process.env.API_KEY,
},
})
return res.json()
}Now, any Client Component that tries to import getData() will trigger a build-time error, indicating that this module is restricted to server-side use only.
Tip 3: To use third-party packages on client components, wrap the packages in client components.
Currently, many components from npm packages that rely on client-side features still do not have the 'use client' directive.
carousel.tsx:
'use client'
import { Carousel } from 'acme-carousel'
export default Carouselpage.tsx:
import Carousel from './carousel'
export default function Page() {
return (
<div>
<p>View pictures</p>
{/* Works, since Carousel is a Client Component */}
<Carousel />
</div>
)
}theme-provider.tsx:
'use client'
import { createContext } from 'react'
export const ThemeContext = createContext({})
export default function ThemeProvider({
children,
}: {
children: React.ReactNode
}) {
return <ThemeContext.Provider value="dark">{children}</ThemeContext.Provider>
}layout.tsx:
import ThemeProvider from './theme-provider'
export default function RootLayout({
children,
}: {
children: React.ReactNode
}) {
return (
<html>
<body>
<ThemeProvider>{children}</ThemeProvider>
</body>
</html>
)
}Tip 4: To reduce the size of the transmitted bundle, move client components down the component tree.
Instead of turning the entire layout into a Client Component, move the interactive functionality to a separate Client Component and keep the layout as a Server Component. This way, you avoid sending the full layout's JavaScript to the client, reducing the client-side load.
Tip 5: To pass props from server to client components, serialize prop data, fetch on client, or use a route handler.
When fetching data in a server component, you might need to pass that data as props to client components. Any props passed from server to client components must be serializable by React. If your client components rely on data that can't be serialized, you can either fetch the data on the client using a third-party library or on the server using a route Handler.
Tip 6: To use server components in client components, pass server components to client components as props.
Within client subtrees, you can still nest Server Components or invoke Server Actions, but there are a few key points to consider: During a request-response cycle, the execution moves from the server to the client. If you need server-side data or resources while on the client, this will result in a new request to the server—there’s no switching back and forth between client and server. When a new request is made to the server, all Server Components are rendered first, even those nested inside Client Components. The rendered output (RSC Payload) contains references to the Client Components' locations. React then uses this payload to merge Server and Client Components into a unified component tree on the client. Since Server Components render before Client Components, you can't import a Server Component directly into a Client Component module (as that would require another request to the server). Instead, you can pass a Server Component as a prop to a Client Component.
client-component.js:
'use client'
import { useState } from 'react'
export default function ClientComponent({
children,
}: {
children: React.ReactNode
}) {
const [count, setCount] = useState(0)
return (
<>
<button onClick={() => setCount(count + 1)}>{count}</button>
{children}
</>
)
}page.js:
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>
)
}'use client'
// You cannot import a Server Component into a Client Component.
import ServerComponent from './Server-Component'
export default function ClientComponent({
children,
}: {
children: React.ReactNode
}) {
const [count, setCount] = useState(0)
return (
<>
<button onClick={() => setCount(count + 1)}>{count}</button>
<ServerComponent />
</>
)
}