How the barrel file ruins Server Components

A typical library bundles everything into one entry file. If any component in that bundle needs interactivity, the directive goes at the top of the bundle, because a directive applies to the whole file. Import a purely presentational `Badge` from that package and you have crossed the client boundary — along with everything you rendered around it.

The symptom is subtle: nothing errors, the page works, and your JavaScript payload is quietly far larger than it should be. You only notice when you look at what actually shipped and find server-safe markup being hydrated for no reason.

Unbundled output is the fix

VivekUI's build is deliberately unbundled — one output file per source file. That is usually considered a downside, since bundling produces fewer files. Here it is the entire point: when each component is its own file, each file carries its own `'use client'` or does not, and importing a `Badge` gives you exactly a `Badge`.

The result is that 49 of the 91 components render directly in React Server Components. Only genuinely interactive ones declare a client boundary, and they declare it at their own file rather than on your behalf.

Why this is asserted in CI

Directives are fragile in a way types are not. A bundler upgrade, a rollup config change or a minifier that strips 'unused' string literals can silently remove `'use client'` from the output, and nothing fails — you just get a runtime hydration error in someone else's app.

So it is checked mechanically: CI asserts on every build that all client files still carry their directive in both the ESM and the CJS output. If you are publishing a library that claims Server Component support, this check is not optional. A claim nobody verifies stops being true at some point without anyone noticing.

How to structure your own pages

Keep the page a Server Component and push interactivity down into the smallest possible island. If your page has a static hero, a static feature grid and one interactive pricing toggle, only the toggle should be a client component — not the page, and not the section containing it.

The practical move is extracting the interactive part into its own file with its own directive, then importing it into the server page. This is exactly the pattern the library uses internally, which is why it composes correctly: server-safe components can be rendered inside your server page without forcing a boundary.

Passing data across the boundary

Props crossing from a Server Component into a client component must be serialisable. Plain objects, arrays, strings and numbers are fine; functions, class instances and Dates need care. This is a React rule, not a library rule, but it is where most Server Component confusion actually originates.

Where the library helps is that server-safe components accept `ReactNode` children, so you can render server content inside them without pulling anything client-side. The `asChild` pattern on link-rendering components lets your router's `Link` slot in without the library depending on a router at all.

Verifying it yourself

Do not take the 49-of-91 figure on trust. In a Next.js app, build and inspect which components ended up in client chunks. Or read the published files directly — `'use client'` is either at the top of a component's file or it is not, and the package ships unminified enough to check.

This is the general principle worth carrying to any library: server-component support is a claim you can verify in about five minutes, and libraries that bundle into a single entry usually cannot pass it regardless of what the README says.

Key takeaways

  • A directive applies to a whole file, so a bundled library forces the client boundary on every component it exports
  • Unbundled per-file output lets each component keep its own directive — the mechanism behind 49 of 91 being server-safe
  • Directives can be silently stripped by build tooling, so CI asserts them in both ESM and CJS output
  • Keep pages as Server Components and extract interactivity into the smallest possible island
  • Props crossing into a client component must be serialisable — a React rule, and the usual source of confusion
  • Verify any library's RSC claim yourself by checking which components land in client chunks

Conclusion

Server Component support is an architectural property, not a feature you add. It comes from shipping unbundled output so directives stay where they belong, and it stays true only because something checks it on every build.

Enjoyed this article?

Vivek Kumar Singh

Vivek Kumar Singh

Technical Expert · Full Stack Cloud Engineer · Tokyo, Japan