DEV Community

kay-adamof
kay-adamof

Posted on • Updated on

3 things you should care about Client Component 【Next.js app/】

1 - How to use 'use client'

There are two ways to make a component client-side.

No.1: Add 'use client' at the beginning of the file

By adding 'use client' at the beginning of the file, it becomes a Client Component. I think this is the most basic configuration method.

// app/ClientComponent.tsx
'use client'

export default function ClientComponent(){
    return (
        <button onClick={()=>{
            console.log('Hello, Client')
        }}>BUTTON</button>
    )
}

// app/page.tsx
import ClientComponent from './ClientComponent.tsx'

export default function Page(){
    return(
        <div>
            <ClientComponent />
        </div>
    )
}
Enter fullscreen mode Exit fullscreen mode

No.2: Pass through the file with 'use client'

Even if the component file itself does not have 'use client', you can make it a Client Component by importing it into another file with 'use client'.

// app/ClientComponent.tsx
export default function ClientComponent(){
    return (
        <button onClick={()=>{
            console.log('Hello, Client')
        }}>BUTTON</button>
    )
}

// app/client.ts
'use client'
export { default } from './ClientComponent.tsx'


// app/page.tsx
import ClientComponent from './client.ts'

export default function Page(){
    return(
        <div>
            <ClientComponent />
        </div>
    )
}
Enter fullscreen mode Exit fullscreen mode

Thanks to this specification, even if an external library component does not support Client Component, you can treat it as a Client Component by adding 'use client' to an arbitrary file and importing/exporting it.

However, please note that I explain more in the following section.

2 - Do not directly nest Server Components

If you import Server Component directly into the file of a Client Component and nest it, the Server Component will be treated as a Client Component (due to the specification shown in No.2 earlier).

To prevent this, set {children} on the Client Component side and wrap it with the opening tag of the Client Component.

// ❌

// app/ServerComponent.tsx
export default function ServerComponent(){
    return(
        <div>Hello, Server</div>
    )
}

// app/ClientComponent.tsx
'use client'
import ServerComponent from './ServerComponent.tsx'

export default function ClientComponent(){
    return (
        <button onClick={()=>{console.log("Hello, Client")}}>
            <ServerComponent />
        </button>
    )
}

// app/page.tsx
import ClientComponent from './ClientComponent.tsx'

export default function Page(){
    return(
        <div>
            <ClientComponent />
        </div>
    )
}
Enter fullscreen mode Exit fullscreen mode
// ⭕️

// app/ServerComponent.tsx
export default function ServerComponent(){
    return(
        <div>Hello, Server</div>
    )
}

// app/ClientComponent.tsx
'use client'

export default function ClientComponent(){
    return (
        <button onClick={()=>{console.log("Hello, Client")}}>
            { children }
        </button>
    )
}

// app/page.tsx
import ClientComponent from './ClientComponent.tsx'
import ServerComponent from './ServerComponent.tsx'

export default function Page(){
    return(
        <div>
            <ClientComponent>
                <ServerComponent />
            </ClientComponent>
        </div>
    )
}
Enter fullscreen mode Exit fullscreen mode

It is not necessary to always wrap it with the opening tag. However, you don't want to bring what can be generated on the server side to the client side, right?

3 - Do not import modules from index files (cannot)


CAUTION

This section was incorrect.

See the revised version


You cannot import from an index.ts/js file into a Client Component.

// CAN NOT DO THIS

// lib/index.ts
export const sayHello = () => {console.log('Hello')

// app/ClientComponent.tsx
'use client'
import { sayHello } from '../lib'

export default function ClientComponent(){
    return (
        <button onClick={()=>{
            sayHello()
        }}>
            You can't do this with ERROR
        </button>
    )
}

Enter fullscreen mode Exit fullscreen mode

If you try to do this, you will get the following error.

image.png

4 - Pre-Rendering

Client components are first rendered on the server side.

The official documentation mentions the following.

image.png

For example, let's say you accidentally use a function inside the component that relies on the document object.


// getDataTheme.ts
export const getDataTheme = () =>
  document.documentElement.getAttribute('data-theme')

// Button.tsx
'use client'
import {getDataTheme} from './getDataTheme'

export default function Button(){
    return (
        <div>{getDataTheme()==='dark'?'Moon':'Sun'}</div>
    )
}
Enter fullscreen mode Exit fullscreen mode

In this case, you will likely see the following warning:

ReferenceError: document is not defined

This is likely caused by using a client-side API on the server side. Find a way to avoid this issue.


I have summarized the knowledge when creating Client Components. I hope it will be helpful to you.

That's all.

Top comments (0)