clerk / clerk/javascript

@clerk/react and @clerk/shared do not declare sideEffects: false, which blocks tree-shaking through ClerkProvider

Open
#9,475 1 comment 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

Stale
Dominant language
TypeScript
Stars
1.8k
Forks
472
Avg merge
2d 11h
Merged PRs (30d)
189

Description

Package

@clerk/react@6.13.1, @clerk/shared@4.27.1 (reached via @clerk/nextjs@7.7.0)

What

@clerk/nextjs declares "sideEffects": false. Neither of the two packages it stands on does — @clerk/react and @clerk/shared both omit the field entirely, so a bundler must assume every module in them has side effects and cannot drop unreachable exports.

$ node -e "for (const p of ['@clerk/react','@clerk/shared','@clerk/nextjs'])
  console.log(p, require(p+'/package.json').sideEffects)"
@clerk/react   undefined
@clerk/shared  undefined
@clerk/nextjs  false
Why it matters downstream

Measured on a Next.js 16 App Router app (Turbopack, Cache Components, @clerk/nextjs@7.7.0), across nine cold production builds:

  • @clerk/nextjs puts 66,806 bytes gzipped into first load, on every route, because ClerkProvider lives in the root layout.
  • With every Clerk UI component deleted from the app — no <UserButton>, no <SignIn>, no <SignUp> — and the provider resolved past the root barrel, 51,910 gz still remains. That is the floor: ClerkProvider alone.

Two things put it there, and the sideEffects gap is one:

  1. No sideEffects: false on @clerk/react / @clerk/shared, so nothing unreachable can be dropped.
  2. @clerk/react's dist is rollup-mergeddist/ClerkProvider-CNl0Jmp6.mjs is a single 59,673-byte file holding the provider and the UI component implementations. Even with sideEffects: false, importing the provider pulls that whole file, because the merge happened at publish time rather than at the consumer's bundler.

So (1) alone is necessary but may not be sufficient — (2) would need the provider split out of the shared chunk for the flag to actually pay. Both together are what would let an app that renders one avatar stop shipping the code for PricingTable, OrganizationProfile, OrganizationSwitcher and the rest.

Ask

Declare "sideEffects": false in @clerk/react and @clerk/shared if it holds (both look side-effect-free from the outside — they are component/hook/util modules), and, if you're open to it, split ClerkProvider out of the merged dist chunk so the flag has something to act on.

Happy to re-run the measurement against a canary if that's useful.

Related

Filed alongside a request for a client-component subpath export on @clerk/nextjs, which is the other half of the same problem for App Router apps.

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start by inspecting the published package manifests for @clerk/react and @clerk/shared, then trace how @clerk/react produces dist/ClerkProvider-CNl0Jmp6.mjs. Verify whether the packages are side-effect-free and how the provider is merged with UI components. Done means the metadata is correct and, if pursued, the provider no longer pulls unrelated UI code; rerun the reported production bundle measurement.

Written by the indexing model from the issue text.

Assessment

Tech stack
nextjs, react, typescript
Domain
build-system, frontend, performance
Issue type
Feature
Difficulty
4/5
Estimated time
3-5 days
Activity status
Active
Clarity
Mostly clear
Newbie friendliness
48/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.