Most component libraries are client libraries, and most catalogue components are marked "use client" whether they need it or not. In an application built on server components that is not a compatibility problem, it is a cost problem: every client component and everything it imports ships to the browser.
Here is how to tell which components genuinely need it, and what React 19 changed.
The rule
A component needs "use client" if it uses state, an effect, a ref, a browser API or an event handler. That is the whole list.
Which means a surprising number of catalogue components do not need it: a card, a badge, a table row, a static hero, a footer, a pricing block. They are marked client because the author added the directive at the top of every file, or because one child in the tree needed it and it propagated upward.
The practical exercise: open the component, look for the four things above, and if none are there, delete the directive and see whether it still builds.
Pushing the boundary down
The pattern that matters more than any individual component. A section that is 95% static markup with one interactive control does not need to be a client component; the control does.
Split it: keep the section as a server component and extract the interactive part into its own client component. That way the markup, the copy and everything they import stay on the server, and only the small piece ships.
The reverse mistake is the common one: a "use client" at the top of a page, which makes the entire tree beneath it client-side, including components that never needed to be.
Passing things across the boundary
Two rules, both of which produce confusing errors when broken.
Props must be serialisable. A server component can pass strings, numbers, plain objects and arrays to a client component. It cannot pass a function, a class instance or a Date in the way people expect. Functions are the common trip: an onSelect handler defined in a server component cannot be handed to a client child.
Children can cross. A client component can render server-rendered children passed as children, which is what makes a client-side layout wrapper around server content possible. This is the escape hatch for a provider or an animation wrapper: make the wrapper the client component and let its children stay on the server.
What React 19 changed
Three things worth knowing for component work.
ref is a normal prop. Function components take ref directly, so the forwardRef wrapper that every primitive used is no longer required. Existing code still works; new components can drop the ceremony.
Form actions and the pending state. A form can take an action directly, and the hooks that report submission state replace the manual boolean. The forms guide covers what that changes in practice, including the fact that a real form works before hydration.
Metadata and stylesheets hoist. Rendering a title or a link tag inside a component now does what you expect rather than needing a separate mechanism.
Checking what you actually ship
Two habits catch almost everything.
Search your components for "use client" and ask, file by file, which of the four triggers applies. In most codebases a third of the directives are inherited rather than needed.
Then look at the bundle. A client component that imports a date library, an icon set or a chart package pulls all of it to the browser, and that is where the surprises are - not in the component's own code, but in what it imported.
Where else to look
The honest list, because the answer is not always us:
| Source | Best for | Trade-off |
|---|---|---|
| React docs on server components | The canonical boundary rules | Framework-agnostic, so light on practice |
| Your framework's docs | How the boundary actually works in your router | Version-specific |
| shadcn/ui | Primitives that mark client only where needed | You still place the boundary |
| 21st | Components with their source visible before you install | Many are marked client by default; check before assuming |
Taking one
Every component page has a live preview and the code. Installing goes through the shadcn CLI against our registry:
That key comes from your 21st account, and installs require a membership. Set API_KEY_21ST once in your shell and the command works for anything in the catalogue.
Frequently asked
- When does a component need "use client"?
- When it uses state, an effect, a ref, a browser API or an event handler. That is the whole list, and it means a card, a badge, a static hero and a footer usually do not, even though many catalogue components are marked client because the author added the directive to every file.
- How do you keep a mostly static section on the server?
- Push the boundary down. Keep the section as a server component and extract only the interactive control into its own client component. The reverse, a use client at the top of the page, drags the entire tree and everything it imports into the browser bundle.
- What can a server component pass to a client component?
- Serialisable values: strings, numbers, plain objects and arrays. Not functions, class instances or anything expecting to survive as a live reference. Children are the exception and the escape hatch: a client wrapper can render server-rendered children passed to it.
- What did React 19 change for component libraries?
- ref is a normal prop, so the forwardRef ceremony is no longer required. Forms take an action directly, with hooks reporting the pending state instead of a manual boolean. And metadata and stylesheets rendered inside a component hoist as expected.



