Server Components Vs Client Components
Sometimes in coding, it’s amazing how the smallest mistakes can cause the biggest headaches. Recently, I found myself repeatedly running into an error. Every time I tried using useState
or other hooks in a server component, I’d hit a wall — and only after some head-scratching did I realize why. You simply can’t use useState
in a server component because it doesn’t handle state or interactivity like a client component. I dove deeper into this and ended up learning something crucial about how modern web development works, and why we have the "use client"
directive.
Let’s break this down with a new analogy.
Think of building a website like assembling a stage production. There are two main teams: the backstage crew and the performers. Server components are like the backstage crew — they handle the props, lighting, and everything behind the scenes, but the audience doesn’t interact with them. Instead, they prepare everything and then push it out to the stage (the browser) before the show begins.
Before server components were introduced, most of the heavy lifting was done by client-side rendering — or in simpler terms, the performers had to not only act out their parts but also build the set, manage the lighting, and adjust the props while the audience was watching! This was inefficient, causing slow page loads and a laggy experience, especially for complex apps.
But thank goodness, the introduction of server components changed all of that. Now, the backstage crew can handle a lot of the pre-show work. Server components fetch data and render HTML on the server, sending a fully prepped stage (HTML) to the browser. This process improves performance because the heavy lifting is done before the audience (user) even arrives. Everything is ready when they show up, so the user sees content faster and interacts with it more smoothly.
What came before server components? Previously, web developers relied heavily on client-side rendering (CSR), where everything was rendered in the browser. This approach put a lot of burden on the user’s device, and while it allowed for highly interactive apps, it also resulted in slow initial loads, especially on slower connections or devices. Pages would essentially show a blank screen until all the JavaScript was downloaded and executed. This is where server-side rendering (SSR) started gaining popularity — but even SSR wasn’t a silver bullet. It required balancing performance and interactivity.
Enter server components, which blend the best of both worlds: they offer the performance of server-side rendering (fast initial page loads) while allowing client components to handle user interaction as needed. Now, the server can take care of the grunt work (fetching data, rendering static parts), while the client handles dynamic updates, improving performance and user experience.
This is where "use client"
comes in. If a component needs to be interactive — for example, handling button clicks, form submissions, or state changes — we need to tell the app: “Hey, this part of the code should be a performer on stage, not just a static backdrop.” "use client"
is the signal that makes that happen. It tells your application that the component should run on the client-side and handle interactivity, like updating state with useState
or reacting to user events.
For example, say you have a contact form on your site. The static text and layout of the form can be rendered on the server. But once the user starts typing or clicking “submit,” that interaction is handled by a client component. That’s when "use client"
comes into play — it makes sure that client-side logic, like form validation or button clicks, works smoothly in the browser.
To explain further: Server components handle all the non-interactive parts of your app — the stuff that doesn’t need to change based on user interaction. This includes fetching data from a database or an API, rendering static HTML, and ensuring everything loads fast. Once these server-side tasks are done, the client component steps in for user interactions.
Why is this better? Imagine if you had to set up the entire stage (lights, props, script) while you were performing your role. It would be messy and confusing, right? That’s what older client-side-rendered apps felt like — the browser had to handle everything at once. Now, with server components, the heavy lifting is done behind the scenes, and only the necessary parts of the app are interactive.
Because I kept making the mistake of trying to handle useState
in server components, essentially asking the backstage crew to perform live on stage — and that doesn’t make sense! Once I understood this split between server components (backstage) and client components (on stage), everything clicked.
In the end, "use client"
is your signal to the app: “This is where the interactivity happens.” Server components handle everything static and pre-rendered, while client components make sure your app responds to user input, giving you a faster, more streamlined experience — and far fewer headaches.