next.js icon indicating copy to clipboard operation
next.js copied to clipboard

"You're importing a component that needs next/headers." for unreachable code

Open jeengbe opened this issue 2 years ago • 14 comments

Verify canary release

  • [X] I verified that the issue exists in the latest Next.js canary release

Provide environment information

Operating System:
      Platform: linux
      Arch: x64
      Version: #1 SMP Fri Jan 27 02:56:13 UTC 2023
    Binaries:
      Node: 18.13.0
      npm: 8.19.3
      Yarn: 1.22.18
      pnpm: 7.32.3
    Relevant packages:
      next: 13.4.3-canary.0
      eslint-config-next: 13.4.2
      react: 18.2.0
      react-dom: 18.2.0
      typescript: 5.0.4

Which area(s) of Next.js are affected? (leave empty if unsure)

App directory (appDir: true), SWC transpilation

Link to the code that reproduces this issue

https://github.com/jeengbe/nextjs-next-headers-issue

To Reproduce

  1. Start Next.js with pnpm dev
  2. Go to http://localhost
  3. Observe error
  4. git checkout HEAD~1
  5. Repeat step 1-2
  6. Observe no error

Describe the Bug

image

Expected Behavior

Because of how the Environment component works (./environment/amphibious.tsx), it is only possible to reach the file that includes next/headers on the server:

export const Environment = () => {
  if (typeof window === "undefined") {
    return require("./server").getEnvironment();
  }

  return getClientEnvironment();
};

This is also understood by webpack, so the server code never makes it into the client bundle:

__webpack_require__.r(__webpack_exports__);
/* harmony export */ __webpack_require__.d(__webpack_exports__, {
  /* harmony export */ Environment: function () {
    return /* binding */ Environment;
  },
  /* harmony export */
});
/* harmony import */ var _client__WEBPACK_IMPORTED_MODULE_0__ =
  __webpack_require__(/*! ./client */ "(app-client)/./environment/client.ts");

const Environment = () => {
  if (false) {
  }
  return (0, _client__WEBPACK_IMPORTED_MODULE_0__.getEnvironment)();
};
_c = Environment;
// ...

Note that if(typeof window === "undefined") { ... } was replaced with if (false) { }.


Since webpack's dead branch elimination is smart enough to eliminate this code, it should be legal to dynamically require modules that import next/headers from Client Components as long as we know that the code is only reachable though branches webpack considers dead. I can't exactly find how webpack does this, but the greater dead branch parity SWC gets with webpack, the better.

Which browser are you using? (if relevant)

No response

How are you deploying your application? (if relevant)

No response

jeengbe avatar May 13 '23 15:05 jeengbe

I expiriencing similar issue with supabase - https://github.com/orgs/supabase/discussions/18086 Its more to supabase related - not to next js

nicitaacom avatar Oct 16 '23 18:10 nicitaacom

any updates on how to solve this? I have to use require('next/headers').headers() as a replacement for now, but im not sure if by doing that im sending the entire next/headers bundle to the client or not

lightrow avatar Jan 16 '24 10:01 lightrow

any updates on how to solve this? I have to use require('next/headers').headers() as a replacement for now, but im not sure if by doing that im sending the entire next/headers bundle to the client or not

Please try to research first (I mean updates alrady exist) https://github.com/vercel/next.js/issues/56630#issuecomment-1755473286

nicitaacom avatar Jan 16 '24 10:01 nicitaacom

This is super annoying. Basically I need to have two API handlers because I want to get cookies on the server, and on the client.

I did the same workaround using require('next/headers').cookies() but it just seems so janky. Please fix this!

marioatbahasa avatar Jan 17 '24 04:01 marioatbahasa

Adding an additional scenario. This happened to me when using the cookies function outside of the app/ directory.

You're importing a component that needs next/headers. That only works in a Server Component which is not supported in the pages/ directory. Read more: https://nextjs.org/docs/getting-started/

I am using the app directory as opposed to pages, but this call was in a directory features/. So I tried the "require" above, worked fine.

It was really odd that it was telling me it's not supported in the pages/ directory, when I wasn't using pages/. I did a minor refactor, calling the same exact code inside of the app/ directory and it worked perfectly fine.

I assume there is some sort of check to see if you are using it in the actual app/ directory.

christopher-caldwell avatar Jan 26 '24 06:01 christopher-caldwell

Adding an additional scenario. This happened to me when using the cookies function outside of the app/ directory.

You're importing a component that needs next/headers. That only works in a Server Component which is not supported in the pages/ directory. Read more: https://nextjs.org/docs/getting-started/

I am using the app directory as opposed to pages, but this call was in a directory features/. So I tried the "require" above, worked fine.

It was really odd that it was telling me it's not supported in the pages/ directory, when I wasn't using pages/. I did a minor refactor, calling the same exact code inside of the app/ directory and it worked perfectly fine.

I assume there is some sort of check to see if you are using it in the actual app/ directory.

Hi @christopher-caldwell, I'm having the exact same situation as you. Hopefully someone from the team reaches out soon.

ezeamin avatar Mar 12 '24 14:03 ezeamin

@ezeamin Here is my work around for now:

// A file at some path outside of the `app/` dir
const SomeComponent = async (props) => {
  const { cookies } = await import('next/headers')
  const cookieManager = cookies()

  return (
    <div>
      {cookieManager.get('cookieName')?.value ?? 'Nope'}
    </div>
  )
}

christopher-caldwell avatar Mar 12 '24 14:03 christopher-caldwell

Guys I don't understand what's the problem to use

This solution in client components to get cookies

export function getCookie(name: string) {
  if (typeof document === "undefined") return

  const value = "; " + document.cookie
  const decodedValue = decodeURIComponent(value)
  const parts = decodedValue.split("; " + name + "=")

  if (parts.length === 2) {
    return parts.pop()?.split(";").shift()
  }
}

And this solution in server components to get cookies

import { cookies } from "next/headers"

cookie.get('someCookieName').value

nicitaacom avatar Mar 13 '24 11:03 nicitaacom

Guys I don't understand what's the problem to use

This solution in client components to get cookies


export function getCookie(name: string) {

  if (typeof document === "undefined") return



  const value = "; " + document.cookie

  const decodedValue = decodeURIComponent(value)

  const parts = decodedValue.split("; " + name + "=")



  if (parts.length === 2) {

    return parts.pop()?.split(";").shift()

  }

}

And this solution in server components to get cookies


import { cookies } from "next/headers"



cookie.get('someCookieName').value

For me, the issue is not how to access cookies. It's that doing so outside of the actual app/ directory (while still using the app router) throws an error.

christopher-caldwell avatar Mar 15 '24 04:03 christopher-caldwell

Guys I don't understand what's the problem to use This solution in client components to get cookies

export function getCookie(name: string) {

  if (typeof document === "undefined") return



  const value = "; " + document.cookie

  const decodedValue = decodeURIComponent(value)

  const parts = decodedValue.split("; " + name + "=")



  if (parts.length === 2) {

    return parts.pop()?.split(";").shift()

  }

}

And this solution in server components to get cookies

import { cookies } from "next/headers"



cookie.get('someCookieName').value

For me, the issue is not how to access cookies. It's that doing so outside of the actual app/ directory (while still using the app router) throws an error.

Of course because you try to get cookies outside of app You need to set cookies to some component related to some route or component in app folder Otherwise you may pass it trhough props into some component

I don't understand use case of using cookie.get() from next/headers outside app directory

nicitaacom avatar Mar 15 '24 08:03 nicitaacom

Guys I don't understand what's the problem to use

This solution in client components to get cookies

export function getCookie(name: string) {

if (typeof document === "undefined") return

const value = "; " + document.cookie

const decodedValue = decodeURIComponent(value)

const parts = decodedValue.split("; " + name + "=")

if (parts.length === 2) {

return parts.pop()?.split(";").shift()

}

}

And this solution in server components to get cookies

import { cookies } from "next/headers"

cookie.get('someCookieName').value

For me, the issue is not how to access cookies. It's that doing so outside of the actual app/ directory (while still using the app router) throws an error.

Of course because you try to get cookies outside of app

You need to set cookies to some component related to some route or component in app folder

Otherwise you may pass it trhough props into some component

I don't understand use case of using cookie.get() from next/headers outside app directory

Using app router, and having files outside of the actual app directory are 2 different things. You can use the cookies function anywhere, but there is (I believe) incorrect check on where you're accessing them. My basis for this is because the error message says that it's not supported in the pages router. Also it works if you require it, or import it in the component.

Your feature code does not need to be in the app directory to use the app router.

christopher-caldwell avatar Mar 19 '24 04:03 christopher-caldwell

'use client';

I tracked and fouund having to use use client in my layout.tsx file.

// import { usePathname } from 'next/navigation';
'use client';

import DashboardHeader from "@/components/layout/header";
import Sidebar from "@/components/layout/sidebar";
import type { Metadata } from "next";
import { SidebarOne } from '@/components/admin-panel/sidebar';

import { redirect } from 'next/navigation';
import { getAuth } from '@/app/features/auth/queries/get-auth'; // Lucia*

So, I went for files around the feature and removed things like useRouter for Link tags where necessary. Got fixed.

Qodestackr avatar Jun 01 '24 01:06 Qodestackr

'use client';

I tracked and fouund having to use use client in my layout.tsx file.

// import { usePathname } from 'next/navigation';
'use client';

import DashboardHeader from "@/components/layout/header";
import Sidebar from "@/components/layout/sidebar";
import type { Metadata } from "next";
import { SidebarOne } from '@/components/admin-panel/sidebar';

import { redirect } from 'next/navigation';
import { getAuth } from '@/app/features/auth/queries/get-auth'; // Lucia*

So, I went for files around the fe ature and removed things like useRouter for Link tags where necessary. Got fixed.

Nice fix by killing SSR feature of Next.js

like

nicitaacom avatar Jun 01 '24 04:06 nicitaacom

In my case, I'm using Nx monorepo, and all cookie-related code lives under the same library. The issue is that this library exports a single 'barrel' file, which exports both client and server code. One of these files imports the next/headers, and even if it's an unreachable code on client components I get the error too.

Right now, I'm also using the const { cookies } = await import('next/headers') workaround. But it's not ideal.

tiagobnobrega avatar Jul 03 '24 01:07 tiagobnobrega

Same as tiago here, using a turbo monorepo and splitting auth into its own package, gonna follow the same approach everyone else is using until it's fixed 🤲🤲

foodornt avatar Jul 30 '24 18:07 foodornt

I'm experiencing the same issue. I have a request.ts file that is used by both the client and the server. I only need to read next/headers on the server, but if I import next/headers at the top level of the module, it throws an error, even though I only call it when typeof window === 'undefined'.

ArianHamdi avatar Aug 09 '24 01:08 ArianHamdi

I've encountered this issue when using cookies inside of a middleware - as it's required that middleware be placed outside of the app directory, it's currently impossible to use anything from next/headers inside of one.

jm-alan avatar Aug 16 '24 08:08 jm-alan

@ezeamin Here is my work around for now:

// A file at some path outside of the `app/` dir
const SomeComponent = async (props) => {
  const { cookies } = await import('next/headers')
  const cookieManager = cookies()

  return (
    <div>
      {cookieManager.get('cookieName')?.value ?? 'Nope'}
    </div>
  )
}

this gives error in production if used outside pages

   Generating static pages (62/63)  [    ]Error: redacted
    at /path/.next/server/chunks/8356.js:19:12770
    at process.processTicksAndRejections (node:internal/process/task_queues:95:5) {
  digest: '2976706782'

FrancMihani avatar Aug 19 '24 14:08 FrancMihani

In my case I had a util outside of app directory doing some cookie parsing. I don't understand why it has to be within app directory. The error message is also outdated, the reasoning behind it might be outdated also.

mobeigi avatar Sep 07 '24 16:09 mobeigi

Now with next15 the require('next/headers') approach will not always work because we need to add an await. For example in my code below:

onst isMobileDevice = (): boolean => {
  const userAgent =
    typeof window !== 'undefined' && window
      ? window.navigator.userAgent
      : // eslint-disable-next-line @typescript-eslint/no-require-imports, @typescript-eslint/no-var-requires
        require('next/headers').headers().get('user-agent')

  return /Android|BlackBerry|iPhone|iPad|iPod|Opera Mini|IEMobile|WPDesktop/i.test(
    userAgent
  )
}

Does anyone have a way to get around this next/header import problem, but still maintain asynchronicity?

dfiedlerx avatar Oct 28 '24 19:10 dfiedlerx

add "use server" on top of your actions with next/headers imports

kitchanismo avatar Nov 11 '24 21:11 kitchanismo

@mobeigi did you find a solution, i do have a utils file inside src/server/lib/action.ts and it complains about it not being used inside app

ChoaibMouhrach avatar Nov 29 '24 21:11 ChoaibMouhrach

Adding an additional scenario. This happened to me when using the cookies function outside of the app/ directory.

You're importing a component that needs next/headers. That only works in a Server Component which is not supported in the pages/ directory. Read more: https://nextjs.org/docs/getting-started/

I am using the app directory as opposed to pages, but this call was in a directory features/. So I tried the "require" above, worked fine.

It was really odd that it was telling me it's not supported in the pages/ directory, when I wasn't using pages/. I did a minor refactor, calling the same exact code inside of the app/ directory and it worked perfectly fine.

I assume there is some sort of check to see if you are using it in the actual app/ directory.

Had the same issue, unexpectedly putting "use server"; at the top in component worked for me, which is weird because by default components should be server side

KuzniaProgramistowCom avatar Jan 09 '25 23:01 KuzniaProgramistowCom

The next/headers is only allowed in RSC environment, even you add a window typecheck condition it still could be in SSR runtime which might be bundled to client. The original reproduction is not accessible and it's being passed for 2 major versions. Now if you need to do sth like that you could use Server Actions or pass the RSC function through a React context to client.

huozhi avatar Jan 14 '25 15:01 huozhi

This closed issue has been automatically locked because it had no new activity for 2 weeks. If you are running into a similar issue, please create a new issue with the steps to reproduce. Thank you.

github-actions[bot] avatar Jan 29 '25 00:01 github-actions[bot]