Next does not tree-shake Zod correctly
Nobody has claimed this yet.
- Dominant language
- JavaScript
- Stars
- 142k
- Forks
- 32.4k
- Avg merge
- 2d 14h
- Merged PRs (30d)
- 351
Description
Link to the code that reproduces this issue
https://github.com/DoroGi/nextjs-zod-tree-shaking-issues/tree/main
I created this repo running: npx create-next-app@latest my-app --yes as suggested by NextJS "getting started".
Then, in a second commit, I installed Zod and created a simple client component called Zodder. This component simply imports Zod using import * as z from zod/mini and parses a string.
To Reproduce
- npm i
- npm run build
- npx next experimental-analyze
- navigate to http://localhost:4000/
Current vs. Expected behavior
You will see that the bundle contains all Zod locales, which are unused and very heavy;
I expect they should be tree-shaken away.
I suspect this tree-shaking issue applies to many other packages where the impact is less obvious.
Provide environment information
Operating System:
Platform: darwin
Arch: arm64
Version: Darwin Kernel Version 25.2.0: Tue Nov 18 21:09:40 PST 2025; root:xnu-12377.61.12~1/RELEASE_ARM64_T6000
Available memory (MB): 32768
Available CPU cores: 10
Binaries:
Node: 24.11.1
npm: 11.6.2
Yarn: N/A
pnpm: 10.15.0
Relevant Packages:
next: 16.1.2 // Latest available version is detected (16.1.2).
eslint-config-next: N/A
react: 19.2.3
react-dom: 19.2.3
typescript: 5.9.3
Next.js Config:
output: N/A
Which area(s) are affected? (Select all that apply)
Module Resolution
Which stage(s) are affected? (Select all that apply)
next build (local)
Additional context
A few workarounds to make it work:
To try this methods, remember to run each time the line below, so you update the .next and you can check whether the locales are present in the bundle or not:
npm run build && npx next experimental-analyze
- Importing zod instead of zod/mini in the zodder component seems to work. Locales are not present anymore.
- Going into node_modules/zod/package.json and editing the ./mini exports configuration to this seems to work:
"./mini": {
"@zod/source": "./src/mini/index.ts",
"types": "./mini/index.d.cts",
"import": "./v4/mini/external.js", <-- only line changed
"require": "./mini/index.cjs"
},
I did this because ./mini in the package.json links to zod/mini/index.js, which just imports zod/v4/mini/index.js, which just imports zod/v4/mini/externals.js.
- Removing the line
export { z };inzod/v4/mini/index.jsseems to work. Notice that the import line when using the standardimport * as z from 'zod'(which is justzod/index.js->./v4/classic/external.js) has a similar line inzod/index.js, but it's not causing any issue apparently.
Related issues
This seems to have been talked a lot in the zod issues. Everybody seems to have found their solution, the only remaining ones are the issues related to NextJS
https://github.com/colinhacks/zod/issues/4433
https://github.com/colinhacks/zod/issues/4572
https://github.com/colinhacks/zod/issues/4637
https://github.com/colinhacks/zod/issues/4798
https://github.com/colinhacks/zod/issues/5206
https://github.com/colinhacks/zod/issues/5561
I also created an identical issue in Zod:
https://github.com/colinhacks/zod/issues/5641
Contributor guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Research direction
Start with the linked reproduction repository and run npm i, npm run build, then npx next experimental-analyze. Inspect the bundle for Zod locales and compare the zod/mini export path in node_modules/zod/package.json with zod/v4/mini/index.js; done means unused locales are absent when importing zod/mini without editing dependencies.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- javascript, next.js, react
- Domain
- build-system, performance
- Issue type
- Bug
- Difficulty
- 4/5
- Estimated time
- 3-5 days
- Activity status
- Quiet
- Clarity
- Mostly clear
- Newbie friendliness
- 50/100