13 min read

What “use client” Really Does in React and Next.js

React’s use client directive might look like a simple annotation at the top of your file, but it marks a shift in how we structure apps. It opens a gateway between server and client environments. Dan Abramov even likened its importance to async / await or structured programming.
What “use client” Really Does in React and Next.js
Photo by Yulia Khlebnikova on Unsplash

React’s use client directive might look like a simple annotation at the top of your file, but it represents a profound shift in how we structure applications. Ever since Next.js 13 introduced the new App Router with React Server Components, developers have been grappling with this two-word directive. On the surface, 'use client' marks a component to run on the browser. Under the hood, however, it opens a gateway between the server and client environments in a way that’s both elegant and technically sophisticated. In fact, React core team member Dan Abramov argues that the invention of 'use client' (and its counterpart, 'use server') is as fundamental as the introduction of async/await or even structured programming itself​. That’s a bold claim for a little string at the top of a file. So what does 'use client' really do, and why is it so important for the future of React?

From Server to Client: Bridging Two Worlds

To understand the meaning of 'use client', it helps to consider the context in which it emerged. In Next.js 13’s App Router, components are server-first by default. This means if you write a component without any special directives, Next.js will render it on the server (producing static HTML) and send that HTML to the browser without any client-side JavaScript for that component. This is great for performance. Your page can load with minimal JS, but it poses a challenge when you do need interactivity or state. How do we tell React that a certain component (say, a counter button or a dynamic form) needs to be interactive and run in the browser? That’s exactly what 'use client' is for​.

When you add 'use client' to the top of a file (above any imports), you are declaring that this module and everything it imports should be treated as a Client Component, meaning it will execute on the client side and can use interactive features like state, effects, and browser APIs​. In essence, 'use client' draws a boundary line in your app’s module graph, on one side of that line, components run on the server; on the other side, components run on the client​. This directive flips the historical default. In traditional React apps (and in Next’s old Pages Router), every component was a client-side component by default, and you opted into server rendering. Now, with Server Components, we default to running on the server and explicitly opt into the client side for interactive parts.

Crucially, 'use client' is more than just a marker for “put this code in the browser.” It serves as a bridge between two environments. A way for the server to include client-run code in the app’s output in a controlled, declarative manner. Dan Abramov describes 'use client' as essentially a typed <script> tag. Just as a script tag in HTML tells the browser to execute some bundled JavaScript, 'use client' tells React’s tooling that “this module is UI code that the browser needs.” The server can import that module and hand off rendering to it, much like opening a door from the server world into the client world​. In other words, 'use client' allows the server to reach into the client bundle and say, “I need this component to come alive in the browser.” It’s a formal, first-class way to intertwine server-rendered content with client-side interactivity.

How Does 'use client' Work Under the Hood?

The technical mechanics of 'use client' are fascinating. When you mark a module with 'use client', you’re signaling to the build system and to React that this file (and its dependencies) belong in the client bundle. If a Server Component tries to import something from that file, the server won’t import the component’s implementation directly. Instead, it imports a stub or reference to it. Think of it like a placeholder or a token that stands in for the real component. The server-rendered output will include a pointer to that client component rather than the component’s HTML, indicating, “there’s a client component here, which will be rendered on the client side.” React’s Server Component payload (often a special JSON behind the scenes) might include an identifier for the component, such as a module path and export name​. For example, the server output could contain something like:

{ 
  "type": "/src/frontend.js#LikeButton", 
  "props": { "postId": 42, "likeCount": 8, "isLiked": true } 
}

This is not literal HTML, but a description. It says there should be a <LikeButton> there with those props, and it references the component by module (/src/frontend.js) and name (LikeButton)​. The React runtime uses this to generate actual script tags for the browser. When the response reaches the client, the framework knows it needs to load the /src/frontend.js module (the file where LikeButton is defined) as a separate JavaScript chunk. It injects a <script src="frontend.js"></script> for that file, and once loaded, it hydrates the component by calling LikeButton({...props}) on the client​. In essence, the 'use client' directive allowed the server to embed a reference to a client-side component in its output, and that reference is resolved into real interactive UI in the browser.

One important nuance is that marking a component with 'use client' does not mean it won’t be rendered on the server at all. In fact, Next.js will still pre-render the initial HTML for client components in many cases (just like it did in the old pages architecture) and then hydrate them on the client. The 'use client' directive simply ensures that the component’s JavaScript is sent to the browser and that React knows to hydrate it. This means you don’t lose the SEO or performance benefits of server-side rendering by using a Client Component, you’re just opting into sending additional JS for interactivity. A common rookie mistake is thinking that adding 'use client' makes your entire page purely client-side rendered. In reality, a Client Component in Next 13+ is usually still rendered to HTML on the server first, then made interactive on the client, which is exactly how React pages have traditionally worked​. The big difference is that now you have a choice, parts of the page with no 'use client' stay purely server-rendered (no hydration needed at all), and parts with 'use client' get that two-step treatment of SSR + hydration.

Because of how the boundaries work, you typically only need to put 'use client' at the top of entry points for interactive islands of your application. Once you’ve marked a component as a Client Component, all of its children and imports automatically become part of the client bundle as well​. You do not need to sprinkle 'use client' on every file that contains a hook or browser API call. For example, if you create a Counter.tsx component with 'use client' (so it can use useState and handle clicks) and then import it into a parent server-rendered page, that Counter and anything it imports will be bundled for the client. If that Counter itself renders other components (passed in as children or imported within it), those can actually be server components if they don’t need interactivity. React will seamlessly render those on the server and slot their HTML into the client component’s output before hydration​. This flexibility can be mind-bending. You can have a Server Component inside a Client Component, which is inside a Server Component, and so on. The framework’s job is to sort out which parts run where. As developers, our job is just to label the boundaries correctly. And thanks to 'use client', those boundaries are explicit and easy to reason about.

Why use client Matters (More Than You Might Think)

The introduction of 'use client' has significant implications for how we architect React applications, especially in frameworks like Next.js. First and foremost, it enables fine-grained performance optimization. By defaulting everything to server-rendered and then opting specific pieces into client-side hydration, we send far less JavaScript to the browser than a traditional SPA would. A page that might have previously bundled the logic of every component now can ship only the code for truly interactive parts. This “eat your cake and have it too” approach, full server rendering for most of the UI, and rich interactivity where needed, is essentially an implementation of the elusive ideal of progressive hydration or the so-called “islands architecture.” You can think of each 'use client' component as an island of interactivity amid a sea of purely server-rendered HTML. If a part of your UI doesn’t need interactivity, simply leave out the directive and it remains an island of static content (no hydration overhead)​. This leads to better loading performance and less JavaScript bloat on the client. Next.js 13+’s architecture actively encourages this. It makes you consciously add 'use client' only where necessary, nudging you into keeping most of your UI logic on the server by default.

Second, 'use client' improves the developer experience and code maintainability in a full-stack React app. In the past, to make a client-side interactive widget that also fetched or updated data on the server, you had to write a lot of boilerplate. Define an API route or endpoint, call fetch from the client, handle state for loading or errors, and so on. Now consider the new world with Server and Client Components. The server can render a component and pass it data directly as props, and the client component can, in turn, directly call back to server functions (using 'use server', which goes hand-in-hand with 'use client'). In Dan Abramov’s Like button example, instead of manually writing API endpoints for “like” and “unlike” and then writing client code to fetch those, you can simply write a server function likePost and import it into your client component with 'use server'. React will handle turning that into an API call for you. On the flip side, you write a LikeButton component with 'use client' and import it into your server-rendered UI; React will handle sending that component’s code to the browser and hydrating it​. The connection is expressed through the module system (via import/export), not through ad-hoc API contracts. This means your editor and type system can understand the relationship. You can navigate to definitions, get type checking across the boundary, and treat the client–server interaction as a function call rather than a network call. As Abramov puts it, the 'use client' import “expresses a direct connection within the module system” between the part of the program that sends the <script> (server) and the part that lives inside that script (client), making it fully visible to tools and type-checkers​. In practical terms, this can reduce bugs and make code more discoverable compared to the old way of stringly-typed API endpoints.

Using 'use client' also forces a clearer separation of concerns between your purely presentational/server-driven components and your interactive ones. In a large codebase, this can be a healthy discipline. You might designate most of a page (navigation bars, content sections, data displays) as server-rendered and free of client-side logic, and only sprinkle a few 'use client' components for things like forms, modals, or widgets that truly need it. Those client components can still leverage server-side data by receiving props or calling server actions, but they won’t inadvertently drag the entire page’s code into the client bundle. Many developers, upon first migrating to Next 13+, felt it was annoying to add 'use client' everywhere they used hooks. But this “annoyance” is intentional. It makes you stop and consider “Does this code really need to run on the client?”. If not, perhaps it could be refactored to a server component, leaving just a tiny client component for the interactive bit. In time, teams find that this leads to smaller, more purpose-driven client modules and a more robust rendering strategy. It’s a new mental model, but one that aligns with the performance needs of modern apps.

One caution, because 'use client' scopes an entire module to the client, you do have to be mindful about what you import inside a client module. Anything you import into a 'use client' file becomes part of the client-side bundle (unless it’s a purely type import or something that gets compiled away). This means you wouldn’t want to import a Node-only library or a huge server-only module inside a client component. It either won’t work (if it relies on Node APIs) or it will bloat your bundle. Next’s compiler will usually warn or error if you try to import server-only code into a client module. In short, keep client components focused and lean. Use them for UI and interactivity, not heavy data fetching or processing (those belong on the server side). Fortunately, the system makes this natural: heavy data fetching is easier to do in Server Components, and they can feed the results into Client Components as props. The end result is an app that is modularized by environment, server logic and rendering over here, client logic and interaction over there, both living in the same codebase but clearly delineated.

The Future of 'use client' and the React Ecosystem

It’s early days for React Server Components and the 'use client' directive, but the impact is already being felt. As of Next.js 13+ (and the evolving React 18+ ecosystem), we’re seeing a re-thinking of how UI and backend logic intermingle. The success of these directives could influence other frameworks and the broader web platform in interesting ways. Dan Abramov suggests that the ideas behind 'use client'/'use server' are not limited to React, they are a generic approach to distributed applications, essentially a form of RPC (remote procedure call) built into the module system​. Imagine a future where your codebase seamlessly spans multiple runtimes (web browser, server, maybe even mobile or worker contexts), with the boundaries declared in the code and handled by compilers and bundlers. The React team expects these patterns to “survive past React and become common sense” in web development​. It’s a bold vision, a world where sending code to the client or calling into the server is as straightforward as calling a function, with tools taking care of the messy details of networking and serialization.

In practical terms, the ecosystem is already adapting. Libraries that provide React components are starting to consider how they’ll work in a Server Components world. For example, a date picker or charting library might mark its components with 'use client' so that if you use them in a Next 13+ app, the library’s code is correctly included on the client side. Tooling is also improving. Since these directives are just string literals, they rely on build tooling to do the right thing. We might see better ESLint rules or even language support to catch mistakes like forgetting to add 'use client' when needed, or conversely adding it unnecessarily. There’s active discussion in the community about how to make the developer experience smoother. Could future React versions infer 'use client' automatically for certain components based on usage of hooks? Possibly, though the React team seems to prefer explicit boundaries for now, as automation might be error-prone. What’s more likely is continued guidance and patterns for structuring apps. Over time, using 'use client' may feel as natural as using useState, just another part of React’s vocabulary.

We should also watch for how other frameworks respond. The idea of partial hydration and islands of interactivity isn’t unique to React. Frameworks like Astro, Qwik, and Marko have been exploring similar territory, each with their own spin. React’s approach with 'use client' and 'use server' is distinctive in that it integrates deeply with JavaScript modules and bundlers, rather than introducing a completely new DSL. This means it could be adopted beyond React if standardized, for instance, a future build tool could allow any JavaScript project to designate certain modules for the client or server environment using similar directives. It’s not hard to imagine the concept spreading: the benefits of clarity, performance, and type safety at the boundary are not something any full-stack developer would want to pass up. On the other hand, React’s solution is opinionated: it assumes a single unified project that produces both server and client artifacts, which fits frameworks like Next.js perfectly. Not every project will have that shape, so there will continue to be alternatives and variations.

In the shorter term, we can expect the React community to establish best practices around 'use client'. Already, the recommendation is to use it sparingly and purposefully. The ideal React Server Components app uses 'use client' only for components that truly need it, and sometimes that means writing a small wrapper component just to hold some client state or effect while the rest stays on the server. This granularity might feel like extra work, but it pays off in load performance and gives you a clearer understanding of your app’s runtime behavior. There’s also an educational aspect: understanding 'use client' inevitably means understanding how the client–server continuum works in a React app, which makes one a better full-stack developer. It forces you to confront where state lives, where data comes from, and what code runs where. Those who embrace this mindset are likely to build apps that scale better and are easier to debug across environments.

The Yin-Yang of modern React architecture. Many have begun to view their React apps as a yin-yang symbol of server and client, two complementary halves of a single whole. The 'use client' and 'use server' directives are the two gates that let data and code flow between these halves in a controlled way, each gate opening in one direction​. 'use server' lets the client safely invoke server-side functions (essentially turning an import into a network call), while 'use client' lets the server include interactive client-side UI (turning an import into a script reference). Together, they allow “seamless composition across the network”​, meaning you can build features that feel like one cohesive program even though under the hood they involve browser code talking to server code. It’s a powerful illusion that improves practicality rather than just abstracting it away: you still know which parts run where, but you no longer have to hand-stitch the plumbing every time you cross the boundary.

In conclusion, 'use client' is far more than a mere hint to “do this in the browser.” It is a cornerstone of React’s new architecture, enabling a new level of integration between server and client logic while preserving performance and clarity. Its importance will only grow as more of the React ecosystem adopts Server Components. Yes, it requires learning a new way of thinking about React, one where you occasionally have to pop open a different mental toolbox for client versus server concerns, but the payoff is an application that can be both highly performant and richly interactive. For busy developers working on complex apps, 'use client' offers a way to write code that is simultaneously efficient and expressive, bridging worlds that used to be separate. As we continue to refine these patterns, it’s likely that in a few years using 'use client' (and 'use server') will feel as natural as writing an async function. It’s a small change with big implications, and it’s pointing the way toward a future in which the line between front-end and back-end code is blurred by design, not by accident. In that future, the phrase “full-stack developer” might take on a more literal meaning, and 'use client' will have been one of the keys that opened the door.


Enjoyed this piece?

If this piece was helpful or resonated with you, you can support my work by buying me a Coffee!

Click the image to visit Alvis’s Buy Me a Coffee page.
Subscribe to our newsletter.

Become a subscriber receive the latest updates in your inbox.