Client Components

Here is a quick overview of the difference between client and server components.

Server Components

In a Next.js, any time you make a component, it is a Server Component by default. So everything you've done so far has been using server components.

Server components contain JavaScript/TypeScript and JSX that is run on a server. This code is guaranteed to never run in the browser, it only runs on the server. The browser never sees the code.

When the browser requests a page from our next.js app, the server uses the JSX to generate JSON and HTML representations of the components. There's no logic in HTML and JSON, it's just the final state, the output, of the components.

So Logic only runs on the server, the client just gets HTML and JSON.

This means that we can do things like connect to a database from our components. It's all server side code. But since all the JavaScript in these components runs on the server, how do we write JavaScript that runs in the browser? Well, we can't, at least not in a server component.

Client Components

So let's say we want to add client side JavaScript to our app. Let's go with the following example that gets used by a lot of frameworks to demonstrate reactivity. The Counter:

0

This is a very simple example where you increment or decrement a number when you click a button. Now you don't need client side JavaScript to do this, but it's a much better user experience if you do.

Try to imagine how this code would look in a server component, maybe something like this:

let count = 0;

export default function Counter() {
return (
<div>
<div>{count}</div>
<button onClick={() => count -= 1 }>-</button>
<button onClick={() => count += 1}>+</button>
</div>
);
}

let count = 0;

export default function Counter() {
return (
<div>
<div>{count}</div>
<button onClick={() => count -= 1 }>-</button>
<button onClick={() => count += 1}>+</button>
</div>
);
}

let count = 0;

export default function Counter() {
return (
<div>
<div>{count}</div>
<button onClick={() => count -= 1 }>-</button>
<button onClick={() => count += 1}>+</button>
</div>
);
}

let count = 0;

export default function Counter() {
return (
<div>
<div>{count}</div>
<button onClick={() => count -= 1 }>-</button>
<button onClick={() => count += 1}>+</button>
</div>
);
}

But this won't work because all of that JavaScript runs only on the server. The count variable only exists on the server, never the browser. So we need to use a Client Component.

Here's the same code, but as a client component:

"use client";
let count = 0;

export default function Counter() {
return (
<div>
<div>{count}</div>
<button onClick={() => count -= 1 }>-</button>
<button onClick={() => count += 1}>+</button>
</div>
);
}
"use client";
let count = 0;

export default function Counter() {
return (
<div>
<div>{count}</div>
<button onClick={() => count -= 1 }>-</button>
<button onClick={() => count += 1}>+</button>
</div>
);
}
"use client";
let count = 0;

export default function Counter() {
return (
<div>
<div>{count}</div>
<button onClick={() => count -= 1 }>-</button>
<button onClick={() => count += 1}>+</button>
</div>
);
}
"use client";
let count = 0;

export default function Counter() {
return (
<div>
<div>{count}</div>
<button onClick={() => count -= 1 }>-</button>
<button onClick={() => count += 1}>+</button>
</div>
);
}

That's it, all you need to do to create a client component is to add "use client" to the top of the file. This is now a client component. That means that all the logic in this file will run in the browser. The count variable will exist in the browser, and the browser will be able to respond to button clicks.

It still won't work as expected thought, because react is complex and managing state is complex so all our code is going to get more complex.

The next section "Client React" will explain go into much more detail about client components and how to use them, so I won't go into too much detail here. But here's the code you need to make the counter work:

counter.tsx
"use client";
import { useState } from "react";

export default function Counter() {
const [count, setCount] = useState(0);
return (
<div>
<div>{count}</div>
<button onClick={() => setCount(count - 1)}>-</button>
<button onClick={() => setCount(count + 1)}>+</button>
</div>
);
}
counter.tsx
"use client";
import { useState } from "react";

export default function Counter() {
const [count, setCount] = useState(0);
return (
<div>
<div>{count}</div>
<button onClick={() => setCount(count - 1)}>-</button>
<button onClick={() => setCount(count + 1)}>+</button>
</div>
);
}
counter.tsx
"use client";
import { useState } from "react";

export default function Counter() {
const [count, setCount] = useState(0);
return (
<div>
<div>{count}</div>
<button onClick={() => setCount(count - 1)}>-</button>
<button onClick={() => setCount(count + 1)}>+</button>
</div>
);
}
counter.tsx
"use client";
import { useState } from "react";

export default function Counter() {
const [count, setCount] = useState(0);
return (
<div>
<div>{count}</div>
<button onClick={() => setCount(count - 1)}>-</button>
<button onClick={() => setCount(count + 1)}>+</button>
</div>
);
}

Using Client Components

To use a client component, you just import it into any other component, including server components. So to use this counter button on a page, we just need to import it into the page:

page.tsx
import Counter from "./counter";

export default function Page() {
return (
<div>
<h1>Counter</h1>
<Counter />
</div>
);
}
page.tsx
import Counter from "./counter";

export default function Page() {
return (
<div>
<h1>Counter</h1>
<Counter />
</div>
);
}
page.tsx
import Counter from "./counter";

export default function Page() {
return (
<div>
<h1>Counter</h1>
<Counter />
</div>
);
}
page.tsx
import Counter from "./counter";

export default function Page() {
return (
<div>
<h1>Counter</h1>
<Counter />
</div>
);
}

The page is still a server component, it can be async and make database calls, but it can also import client components. We can even pass props down to client components.

The main thing we can't do, is import a server component into a client component, that is not allowed. The moment you import another component into a client component, the imported component becomes a client component too.

Next.js Uses Client Side JS

Even if we never use a client component in our code, Next.js will still run JavaScript in the browser. This is because Next want's to create a good user experience and reloading an entire page every time you click a link is not a good user experience. This means you don't need to use client components as often, as long as you're following Next.js best practice.

For example, if you're using <Link> instead <a>, you're getting the benefits of client side JavaScript, without having to create client components.

Don't avoid using client components, but only use them when you need to run some JavaScript in the browser.