Skip to content

Next does not tree-shake Zod correctly #88643

@DoroGi

Description

@DoroGi

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

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
  1. Importing zod instead of zod/mini in the zodder component seems to work. Locales are not present anymore.
  2. 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.

  1. Removing the line export { z }; in zod/v4/mini/index.js seems to work. Notice that the import line when using the standard import * as z from 'zod' (which is just zod/index.js -> ./v4/classic/external.js) has a similar line in zod/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

colinhacks/zod#4433
colinhacks/zod#4572
colinhacks/zod#4637
colinhacks/zod#4798
colinhacks/zod#5206
colinhacks/zod#5561

I also created an identical issue in Zod:
colinhacks/zod#5641

Metadata

Metadata

Assignees

No one assigned

    Labels

    Module ResolutionModule resolution (CJS / ESM, module resolving).TurbopackRelated to Turbopack with Next.js.

    Type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions