How to write client-only code in Next.js Server Components

·3 min read

Next.js Server Components in the new app directory leverage React Server components to render these special components on the server.

Next.js allows us to leverage Server Components for writing layouts and pages, but what about client-only code? In this tutorial, we'll learn how to write client-only code in Next.js Server Components.

Sometimes, we need to write code that executes only in the client. For example, we might want to use a library that only works in the browser or some code that needs the browser's API to work.

In my case, I wanted to run a side effect such as setting the page's theme based on the user's system preferences. Since this needs to run in the client, I can't do it in the server, and I was struggling to add the required code to make a server component run some code only when running in the client.

Other examples can include:

  • reading or writing to the browser's local storage
  • using some browser's API such as the Geolocation API or Intersection Observer API
  • using a library that only works in the browser (for example, some charts libraries)

In my case, I wanted to add code that executed in every page, but only when running in the client. To do so, I added the code to the root layout, eg. the layout.tsx file within the app directory.

Below is the simplest root layout that I could write:

export default async function RootLayout({ children, }: { children: React.ReactNode; }) { return ( <html> <head /> <body>{children}</body> </html> ); }

Now, let's assume we want to add some code that runs only in the client, but that we define once for all pages.

We can do so by creating a further client component, which I name ThemeSetter: this component runs when server-rendered and on the client, but is not a Server Component.

'use client'; import { useEffect } from 'react'; function ThemeSetter() { useEffect(() => { if (typeof window !== undefined) { // execute your logic here } }, []); return null; }

The component above does not render anything: in my case, I simply want to read or set a value, but of course, you can render something if you want.

Now, we can safely add this component to the root layout:

export default async function RootLayout({ children, }: { children: React.ReactNode; }) { return ( <html> <head /> <body>{children}</body> <ThemeSetter /> </html> ); }

Et voilà! We can now execute some code only in the client, but that is defined once for all pages, using the new Next.js Server Components.

If you have any questions, feel free to reach out!