Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Using next/link.js in an ESM app causes a hard reload / full ESM support via subpath exports for next #77200

Open
amannn opened this issue Mar 17, 2025 · 3 comments
Labels
Linking and Navigating Related to Next.js linking (e.g., <Link>) and navigation.

Comments

@amannn
Copy link
Contributor

amannn commented Mar 17, 2025

Link to the code that reproduces this issue

amannn/nextjs-bug-repro-linkjs@eedf0a9

To Reproduce

  1. pnpm i
  2. pnpm run dev
  3. Open homepage
  4. Click on link to /test

Current vs. Expected behavior

Currently, the target page is opened with a hard reload. I'd expect it to use soft navigation—same as when using next/link without the explicit file extension.

Provide environment information

Operating System:
  Platform: darwin
  Arch: x64
  Version: Darwin Kernel Version 23.6.0: Mon Jul 29 21:13:00 PDT 2024; root:xnu-10063.141.2~1/RELEASE_X86_64
  Available memory (MB): 16384
  Available CPU cores: 12
Binaries:
  Node: 20.11.1
  npm: 10.2.4
  Yarn: 1.22.22
  pnpm: 9.15.4
Relevant Packages:
  next: 15.3.0-canary.11 // Latest available version is detected (15.3.0-canary.11).
  eslint-config-next: N/A
  react: 19.0.0
  react-dom: 19.0.0
  typescript: 5.8.2
Next.js Config:
  output: N/A

Which area(s) are affected? (Select all that apply)

Linking and Navigating

Which stage(s) are affected? (Select all that apply)

next dev (local)

Additional context

amannn/next-intl#1796 (comment)

https://next-intl.dev/docs/environments/testing#vitest

@github-actions github-actions bot added the Linking and Navigating Related to Next.js linking (e.g., <Link>) and navigation. label Mar 17, 2025
@samcx
Copy link
Member

samcx commented Mar 17, 2025

@amannn Thank you for submitting an issue!

without the explicit file extension.

Can you clarify why you need to add an explicit file extension when importing Link?

@amannn
Copy link
Contributor Author

amannn commented Mar 18, 2025

@samcx Thank you for the quick reply!

So the issue is that the ESM spec requires mandatory file extensions, e.g.:

// ❌ Invalid
import Link from 'next/link';

// ✅ Valid
import Link from 'next/link.js';

To make authoring code easier, bundlers like Turbopack, webpack and to some extend Vite(st) typically allow to leave out these file extensions by (pre-)bundling source code.

However, under some circumstances, this will still cause issues. In the example of Vitest, only first-party user code is pre-bundled and not any 3rd party dependencies. Due to this, if you have a package that imports from Next.js, this will cause errors when used:

Error: Cannot find module '/(...)/node_modules/next/link' imported from /(...)/node_modules/nextjs-bug-repro-link-package/index.js
Did you mean to import nextjs-bug-repro-linkjs/node_modules/(...)/next/link.js?

I've updated the reproduction in cd3145e to use nextjs-bug-repro-link-package as an example for a 3rd party dependency that has an import to next/link. Currently, running pnpm test in that repo throws the above error.

So as it stands currently, a 3rd party dependency can either:

  1. Import without a file extension, but this will break in strict ESM environments like the Vitest example above
  2. Import with a file extension, with Next.js causing hard reloads for Link (original issue posted here; I'm also not sure if it has other unwanted side effects)

I think this could be addressed in two ways on the Next.js side:

  1. Make entry points like next/link.js work equally to alternatives without a file extension (are there some bundler assumptions that imports from next/* don't use a file extension?)
  2. Add subpath exports to make imports like next/link valid in strict ESM environments
{
  // ...
  "exports": {
    "./link": "./link.js",
    "./navigation": "./navigation.js",
    // ...
  }
} 

(2) might be preferable since it would work well for the imports that Next.js has popularized in its docs, but it'd be a breaking change since it would make the public API of next very explicit, disallowing consumers to import private APIs like:

// Would break when you add `exports`
import { ResponseCookies } from 'next/dist/compiled/@edge-runtime/cookies';

I guess Next.js wants to discourage importing private APIs anyway, but practically some users might use such imports today.


The above tries to outline the issue from a minimal example. Practically, this currently affects libraries like next-intl which are ESM-only and import from next/link & friends (ref).

/cc @franky47 Maybe you've also encountered similar errors with users of nuqs since you're importing from Next.js into an ESM package?

/cc @karlhorky If I'm not mistaken, I saw your name once in a discussion about full ESM support in Next.js, but I can't find the relevant issue right now. Maybe you have other insights?

@amannn amannn changed the title Using next/link.js in an ESM app causes a hard reload Using next/link.js in an ESM app causes a hard reload / full ESM support via subpath exports for next Mar 18, 2025
@franky47
Copy link
Contributor

franky47 commented Mar 18, 2025

Thanks for the ping @amannn, I just had this issue come up again in 47ng/nuqs#869, but with next/navigation.

nuqs is indeed an ESM package, and we provide adapters for:

  • The pages router (importing from next/compat/router.js)
  • The app router (importing from next/navigation)
  • An agnostic adapter that imports code from both of the above (and uses whichever one is appropriate), and caused issues in some cases due to the pages router bundler not finding next/navigation, but being OK with next/navigation.js (see fix: Import from next/navigation.js 47ng/nuqs#939).

I'll try and reproduce your hard reload issue to see if it impacts next/navigation too.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Linking and Navigating Related to Next.js linking (e.g., <Link>) and navigation.
Projects
None yet
Development

No branches or pull requests

3 participants