DEV Community

AILI Fida Aliotti Christino
AILI Fida Aliotti Christino

Posted on • Edited on

Client or Server component ?

Recently, I had a project that needed to be rewritten from React to Next.js. I worked with individuals who weren't familiar with server components and how they work, so they kept asking me constantly:

💬 How do i know if this component is a server one or a client one?

My first answer (a silly one you may say 😕) was:

💬 Just check at the top of the file if it contains 'use client' or not" 😇

Just for those who didn't made the jump to NextJS yet, all components in NextJS are server by default so if you needed a client one, you have to add "use client" at the top of your file.

Later, they came to me again with another question:

Ok, we got it, when there is "use client" at the top, it is a client one but how comes this other component that doesn't have "use client" inside it claims to be one?

I was suggested to rename every server file into something explicit like ServerTagComponent 🤧

But then i remembered a video of Jack Herrington about state management in Next.JS with this kind of segmentation in his components:

segmented_component

Wouldn't it be cool to have something like this in your components 🤩?

So the general idea is to implement a higher order component that adds a label for each component that uses it.

The final result should be something like this:

expected_result

And every component that needs it should have a withComponentIdentifier in order to display these boxes.

Our HOC component

A Higher Order Component (HOC) is a function in React that takes a component and returns a new component with additional features or behaviors.

It's a way to reuse component logic and share functionalities among different parts of your application.

Let's start by creating our HOC file:

// components/ComponentIdentifier
import type { ComponentType, FC } from 'react';

export function withComponentIdentifier<P extends object>(
  WrappedComponent: ComponentType<P>
): ComponentType<P> {
  const WithComponentIdentifier: FC<P> = (props) => {
    const isClient = typeof window !== 'undefined';
    const display = isClient ? 'client' : 'server';

    return process.env.NODE_ENV === 'development' ? (
      <div>
        <p className={`hoc-text ${display}`}>This is a {display} component</p>
        <div className={`hoc-content ${display}`}>
          <WrappedComponent {...props} />
        </div>
      </div>
    ) : (
      <WrappedComponent {...props} />
    );
  };

  return WithComponentIdentifier;
}
Enter fullscreen mode Exit fullscreen mode

This is a simple HOC component written in TypeScript, but the trick here is on this line:

  const isClient = typeof window !== 'undefined';
Enter fullscreen mode Exit fullscreen mode

Because a server component is rendered by the server then it shouldn't have a window property.

💡 By adding the line process.env.NODE_ENV === 'development' we make sure that the HOC only displays on development mode. Read more about that here

Usage

Now that we have our HOC component, let's use it. Create any tsx file that you want inside our app folder. I chose to name mine client.tsx then create a simple client component:

// app/client.tsx
'use client';

const ClientComponent = () => {
  return <p>This is a simple client component</p>;
};

export default ClientComponent;
Enter fullscreen mode Exit fullscreen mode

Then create another component called server.tsx:

const ServerComponent = () => {
  return <p>This is a server component</p>;
};

export default ServerComponent;
Enter fullscreen mode Exit fullscreen mode

Finally, create a component that we will identify:

// app/unknown.tsx
const UnknownComponent = () => {
  return <p>This is an unknown component</p>;
};

export default UnknownComponent;
Enter fullscreen mode Exit fullscreen mode

We will use this component inside each file created previously to determine either it's a client or a server component.

Your final page.tsx should be like this:

import ClientComponent from './client';
import ServerComponent from './server';

export default async function HomePage() {
  return (
    <>
      <ClientComponent />
      <ServerComponent />
    </>
  );
}
Enter fullscreen mode Exit fullscreen mode

client_server_result

Now, let's use our HOC inside these components:

// app/client.tsx
'use client';

import { withComponentIdentifier } from '@/components/ClientOrServer';

const ClientComponent = () => {
  return <p>This is a simple client component</p>;
};

export default withComponentIdentifier(ClientComponent);
Enter fullscreen mode Exit fullscreen mode

And the server.tsx:

import { withComponentIdentifier } from '@/components/ClientOrServer';

const ServerComponent = () => {
  return <p>This is a server component</p>;
};

export default withComponentIdentifier(ServerComponent);
Enter fullscreen mode Exit fullscreen mode

Just like that, we now have these cool boxes:

boxes_result

Use case

Although these components are clearly explicit, there is a scenario where you might not know if a component is nested inside a client or server component. This is where our Higher Order Component (HOC) proves to be truly useful. Let's consider a situation where we call our UnknownComponent within our ClientComponent (and remember to include our HOC in our UnknownComponent).

// app/client.tsx
'use client';
import UnknownComponent from './unknown';

const ClientComponent = () => {
  return (
    <>
      <p>This is a simple client component</p>
      <UnknownComponent />
    </>
  );
};

export default ClientComponent;
Enter fullscreen mode Exit fullscreen mode

Notice that i removed the HOC from the ClientComponent resulting to identify just the unknown component.

unknown_component

Now, let's just move our UnknownComponent inside our ServerComponent:

// app/server.tsx
import UnknownComponent from './unknown';

const ServerComponent = () => {
  return (
    <>
      <p>This is a server component</p>
      <UnknownComponent />
    </>
  );
};

export default ServerComponent;
Enter fullscreen mode Exit fullscreen mode

Here is the result:

final_result

Conclusion

With the usage of that HOC, everyone can now wrap components to determine if its a client one or a server one without asking all the time! 😄😇

Thanks for reading!

Buy Me A Coffee

Top comments (0)