Package
@clerk/nextjs@7.7.0 (re-checked against 7.7.7 — unchanged)
What
An App Router root layout must import ClerkProvider, and the only supported specifier is the root barrel:
// app/layout.tsx — a Server Component
import { ClerkProvider } from "@clerk/nextjs";
dist/esm/index.js statically imports three "use client" modules — client-boundary/uiComponents, client-boundary/controlComponents, client-boundary/hooks — alongside the provider. A Server Component importing a "use client" barrel registers every export of that barrel as a client reference, whether or not it renders them. So an app that renders one <UserButton> ships the client wrappers for PricingTable, OrganizationProfile, OrganizationSwitcher, APIKeys, Waitlist and the rest, on every route.
The package's own exports map has no client-component subpath and no wildcard:
".", "./server", "./errors", "./internal", "./webhooks", "./experimental", "./legacy", "./types"
/internal is not an alternative — it imports client-boundary/uiComponents itself.
The provider is already clean internally
ClerkProvider resolves through the package-private #components condition (declared in dist/esm/package.json) to dist/esm/components.server.js, which imports none of the UI modules:
// dist/esm/components.server.js
import { ClerkProvider } from "./app-router/server/ClerkProvider";
import { Show } from "./app-router/server/controlComponents";
A specifier landing there would give an App Router app the provider without the UI barrel. There just isn't one — #components is package-private and the root exports map has no wildcard.
Measured
Nine cold production builds of a Next.js 16 App Router app (Turbopack, Cache Components), NEXT_SERVER_ACTIONS_ENCRYPTION_KEY pinned so builds are byte-comparable; the reverted tree rebuilt byte-identical to the baseline:
| configuration |
total client JS (gz) |
Δ |
| as shipped today |
579,569 |
— |
provider aliased to components.server.js + every Clerk component behind one client module |
574,801 |
−4,768 |
That −4,768 gz (0.82% of the app's client JS) is what a supported subpath would buy for free. Reaching it today needs a bundler alias onto dist/esm/components.server.js — a path outside your exports map, through a condition the alias bypasses — which we measured, priced and then declined to ship: a rename on your side fails our build loudly (fine), but a content change fails nothing and would land at runtime in the root layout of every page.
Ask
A supported subpath that exports ClerkProvider (and Show) without the client-component barrel — e.g. @clerk/nextjs/provider, or simply exposing what #components already resolves to under the react-server condition. Naming it in exports is most of the work; the module exists.
Related
Filed alongside a request for sideEffects: false on @clerk/react and @clerk/shared, which is the other half — with a subpath but without that flag, @clerk/react's rollup-merged dist still carries the UI implementations into the provider's chunk.
Package
@clerk/nextjs@7.7.0(re-checked against7.7.7— unchanged)What
An App Router root layout must import
ClerkProvider, and the only supported specifier is the root barrel:dist/esm/index.jsstatically imports three"use client"modules —client-boundary/uiComponents,client-boundary/controlComponents,client-boundary/hooks— alongside the provider. A Server Component importing a"use client"barrel registers every export of that barrel as a client reference, whether or not it renders them. So an app that renders one<UserButton>ships the client wrappers forPricingTable,OrganizationProfile,OrganizationSwitcher,APIKeys,Waitlistand the rest, on every route.The package's own
exportsmap has no client-component subpath and no wildcard:/internalis not an alternative — it importsclient-boundary/uiComponentsitself.The provider is already clean internally
ClerkProviderresolves through the package-private#componentscondition (declared indist/esm/package.json) todist/esm/components.server.js, which imports none of the UI modules:A specifier landing there would give an App Router app the provider without the UI barrel. There just isn't one —
#componentsis package-private and the rootexportsmap has no wildcard.Measured
Nine cold production builds of a Next.js 16 App Router app (Turbopack, Cache Components),
NEXT_SERVER_ACTIONS_ENCRYPTION_KEYpinned so builds are byte-comparable; the reverted tree rebuilt byte-identical to the baseline:components.server.js+ every Clerk component behind one client moduleThat −4,768 gz (0.82% of the app's client JS) is what a supported subpath would buy for free. Reaching it today needs a bundler alias onto
dist/esm/components.server.js— a path outside yourexportsmap, through a condition the alias bypasses — which we measured, priced and then declined to ship: a rename on your side fails our build loudly (fine), but a content change fails nothing and would land at runtime in the root layout of every page.Ask
A supported subpath that exports
ClerkProvider(andShow) without the client-component barrel — e.g.@clerk/nextjs/provider, or simply exposing what#componentsalready resolves to under thereact-servercondition. Naming it inexportsis most of the work; the module exists.Related
Filed alongside a request for
sideEffects: falseon@clerk/reactand@clerk/shared, which is the other half — with a subpath but without that flag,@clerk/react's rollup-merged dist still carries the UI implementations into the provider's chunk.