FusionAuth / fusionauth-javascript-sdk

8 stars 0 forks source link

NextJS support #99

Closed JakeLo123 closed 1 day ago

JakeLo123 commented 1 month ago

NextJS support

Problem

If you are using the React SDK with NextJS, you must use the "use client" directive. This is not ideal for NextJS users.

Solution

A solution is implemented for the Vue SDK/Nuxt https://github.com/FusionAuth/fusionauth-javascript-sdk/pull/98 -- let's get the React SDK to parity.

Nextjs instructs users to use the use client directive when consuming 3rd party providers, so we should not have to make any changes due to our use of React.createContext. Although we should include Next configuration in our docs--perhaps reference this article authored by Vercel.

Alternatives/workarounds

Continue requiring Next users to use client side rendering.

Community guidelines

All issues filed in this repository must abide by the FusionAuth community guidelines.

How to vote

Please give us a thumbs up or thumbs down as a reaction to help us prioritize this feature. Feel free to comment if you have a particular need or comment on how this feature should work.

kasir-barati commented 4 weeks ago

Hey @JakeLo123,

Thanks for this issue, I love it. Actually I've been struggling with this for quite some time during the past few days. A few comments:

  1. I've asked this question in different forums and channels in hope of finding an answer:
  2. "use client" directive is used to change server component into client component but client components are still pre-rendered on server side, so window, document and local storage don't exists during pre-rendering and that's why it's working in useEffect and not outside of it since useEffect is executed only on client side. Client components = SSR + hydration + CSR.
  3. I think we could resolve the "document is not defined" issue by optional chaining; E.g. document?.cookies.

And please lemme know when this issue is going to be addressed and what would be the workaround in the meantime.

I have a reproducible repo for "document is not defined":

  1. clone the repo: https://github.com/kasir-barati/you-say
  2. npm ci
  3. npx nx build:local frontend

You can see error messages in the terminal, I am not sure but my best bet is this part since it is not able to prerender shop page even though it is a simple page like this.

Error message:

$ nx build:local frontend

> nx run frontend:"build:local"

> NODE_ENV=production nx build frontend

 NX   Running target build for project frontend and 1 task it depends on:

> nx run @shared:build

Compiling TypeScript files for project "@shared"...
Done compiling TypeScript files for project "@shared".

> nx run frontend:build

> next build

  ▲ Next.js 14.2.3
  - Experiments (use with caution):
    · outputFileTracingRoot

   Creating an optimized production build ...
 ✓ Compiled successfully
   Skipping linting
   Checking validity of types ...
   Collecting page data ...
   Generating static pages (0/6) ...
   Generating static pages (1/6) 
ReferenceError: document is not defined
    at /home/kasir/projects/you-say/apps/frontend/.next/server/chunks/657.js:142:26284
    at get at_exp (/home/kasir/projects/you-say/apps/frontend/.next/server/chunks/657.js:142:26404)
    at c.scheduleTokenExpiration (/home/kasir/projects/you-say/apps/frontend/.next/server/chunks/657.js:142:26526)
    at new c (/home/kasir/projects/you-say/apps/frontend/.next/server/chunks/657.js:142:24884)
    at /home/kasir/projects/you-say/apps/frontend/.next/server/chunks/657.js:142:26974
    at Object.nr [as useMemo] (/home/kasir/projects/you-say/node_modules/next/dist/compiled/next-server/app-page.runtime.prod.js:12:39130)
    at t.useMemo (/home/kasir/projects/you-say/node_modules/next/dist/compiled/next-server/app-page.runtime.prod.js:12:109856)
    at d (/home/kasir/projects/you-say/apps/frontend/.next/server/chunks/657.js:142:26969)
    at nj (/home/kasir/projects/you-say/node_modules/next/dist/compiled/next-server/app-page.runtime.prod.js:12:46251)
    at nM (/home/kasir/projects/you-say/node_modules/next/dist/compiled/next-server/app-page.runtime.prod.js:12:47571) {
  digest: '831143466'
}

Error occurred prerendering page "/_not-found". Read more: https://nextjs.org/docs/messages/prerender-error

ReferenceError: document is not defined
    at /home/kasir/projects/you-say/apps/frontend/.next/server/chunks/657.js:142:26284
    at get at_exp (/home/kasir/projects/you-say/apps/frontend/.next/server/chunks/657.js:142:26404)
    at c.scheduleTokenExpiration (/home/kasir/projects/you-say/apps/frontend/.next/server/chunks/657.js:142:26526)
    at new c (/home/kasir/projects/you-say/apps/frontend/.next/server/chunks/657.js:142:24884)
    at /home/kasir/projects/you-say/apps/frontend/.next/server/chunks/657.js:142:26974
    at Object.nr [as useMemo] (/home/kasir/projects/you-say/node_modules/next/dist/compiled/next-server/app-page.runtime.prod.js:12:39130)
    at t.useMemo (/home/kasir/projects/you-say/node_modules/next/dist/compiled/next-server/app-page.runtime.prod.js:12:109856)
    at d (/home/kasir/projects/you-say/apps/frontend/.next/server/chunks/657.js:142:26969)
    at nj (/home/kasir/projects/you-say/node_modules/next/dist/compiled/next-server/app-page.runtime.prod.js:12:46251)
    at nM (/home/kasir/projects/you-say/node_modules/next/dist/compiled/next-server/app-page.runtime.prod.js:12:47571)
ReferenceError: document is not defined
    at /home/kasir/projects/you-say/apps/frontend/.next/server/chunks/657.js:142:26284
    at get at_exp (/home/kasir/projects/you-say/apps/frontend/.next/server/chunks/657.js:142:26404)
    at c.scheduleTokenExpiration (/home/kasir/projects/you-say/apps/frontend/.next/server/chunks/657.js:142:26526)
    at new c (/home/kasir/projects/you-say/apps/frontend/.next/server/chunks/657.js:142:24884)
    at /home/kasir/projects/you-say/apps/frontend/.next/server/chunks/657.js:142:26974
    at Object.nr [as useMemo] (/home/kasir/projects/you-say/node_modules/next/dist/compiled/next-server/app-page.runtime.prod.js:12:39130)
    at t.useMemo (/home/kasir/projects/you-say/node_modules/next/dist/compiled/next-server/app-page.runtime.prod.js:12:109856)
    at d (/home/kasir/projects/you-say/apps/frontend/.next/server/chunks/657.js:142:26969)
    at nj (/home/kasir/projects/you-say/node_modules/next/dist/compiled/next-server/app-page.runtime.prod.js:12:46251)
    at nM (/home/kasir/projects/you-say/node_modules/next/dist/compiled/next-server/app-page.runtime.prod.js:12:47571) {
  digest: '831143466'
}

Error occurred prerendering page "/shop". Read more: https://nextjs.org/docs/messages/prerender-error

ReferenceError: document is not defined
    at /home/kasir/projects/you-say/apps/frontend/.next/server/chunks/657.js:142:26284
    at get at_exp (/home/kasir/projects/you-say/apps/frontend/.next/server/chunks/657.js:142:26404)
    at c.scheduleTokenExpiration (/home/kasir/projects/you-say/apps/frontend/.next/server/chunks/657.js:142:26526)
    at new c (/home/kasir/projects/you-say/apps/frontend/.next/server/chunks/657.js:142:24884)
    at /home/kasir/projects/you-say/apps/frontend/.next/server/chunks/657.js:142:26974
    at Object.nr [as useMemo] (/home/kasir/projects/you-say/node_modules/next/dist/compiled/next-server/app-page.runtime.prod.js:12:39130)
    at t.useMemo (/home/kasir/projects/you-say/node_modules/next/dist/compiled/next-server/app-page.runtime.prod.js:12:109856)
    at d (/home/kasir/projects/you-say/apps/frontend/.next/server/chunks/657.js:142:26969)
    at nj (/home/kasir/projects/you-say/node_modules/next/dist/compiled/next-server/app-page.runtime.prod.js:12:46251)
    at nM (/home/kasir/projects/you-say/node_modules/next/dist/compiled/next-server/app-page.runtime.prod.js:12:47571)
ReferenceError: document is not defined
    at /home/kasir/projects/you-say/apps/frontend/.next/server/chunks/657.js:142:26284
    at get at_exp (/home/kasir/projects/you-say/apps/frontend/.next/server/chunks/657.js:142:26404)
    at c.scheduleTokenExpiration (/home/kasir/projects/you-say/apps/frontend/.next/server/chunks/657.js:142:26526)
    at new c (/home/kasir/projects/you-say/apps/frontend/.next/server/chunks/657.js:142:24884)
    at /home/kasir/projects/you-say/apps/frontend/.next/server/chunks/657.js:142:26974
    at Object.nr [as useMemo] (/home/kasir/projects/you-say/node_modules/next/dist/compiled/next-server/app-page.runtime.prod.js:12:39130)
    at t.useMemo (/home/kasir/projects/you-say/node_modules/next/dist/compiled/next-server/app-page.runtime.prod.js:12:109856)
    at d (/home/kasir/projects/you-say/apps/frontend/.next/server/chunks/657.js:142:26969)
    at nj (/home/kasir/projects/you-say/node_modules/next/dist/compiled/next-server/app-page.runtime.prod.js:12:46251)
    at nM (/home/kasir/projects/you-say/node_modules/next/dist/compiled/next-server/app-page.runtime.prod.js:12:47571) {
  digest: '831143466'
}

Error occurred prerendering page "/". Read more: https://nextjs.org/docs/messages/prerender-error

ReferenceError: document is not defined
    at /home/kasir/projects/you-say/apps/frontend/.next/server/chunks/657.js:142:26284
    at get at_exp (/home/kasir/projects/you-say/apps/frontend/.next/server/chunks/657.js:142:26404)
    at c.scheduleTokenExpiration (/home/kasir/projects/you-say/apps/frontend/.next/server/chunks/657.js:142:26526)
    at new c (/home/kasir/projects/you-say/apps/frontend/.next/server/chunks/657.js:142:24884)
    at /home/kasir/projects/you-say/apps/frontend/.next/server/chunks/657.js:142:26974
    at Object.nr [as useMemo] (/home/kasir/projects/you-say/node_modules/next/dist/compiled/next-server/app-page.runtime.prod.js:12:39130)
    at t.useMemo (/home/kasir/projects/you-say/node_modules/next/dist/compiled/next-server/app-page.runtime.prod.js:12:109856)
    at d (/home/kasir/projects/you-say/apps/frontend/.next/server/chunks/657.js:142:26969)
    at nj (/home/kasir/projects/you-say/node_modules/next/dist/compiled/next-server/app-page.runtime.prod.js:12:46251)
    at nM (/home/kasir/projects/you-say/node_modules/next/dist/compiled/next-server/app-page.runtime.prod.js:12:47571)
   Generating static pages (2/6) 
   Generating static pages (4/6) 
ReferenceError: document is not defined
    at /home/kasir/projects/you-say/apps/frontend/.next/server/chunks/657.js:142:26284
    at get at_exp (/home/kasir/projects/you-say/apps/frontend/.next/server/chunks/657.js:142:26404)
    at c.scheduleTokenExpiration (/home/kasir/projects/you-say/apps/frontend/.next/server/chunks/657.js:142:26526)
    at new c (/home/kasir/projects/you-say/apps/frontend/.next/server/chunks/657.js:142:24884)
    at /home/kasir/projects/you-say/apps/frontend/.next/server/chunks/657.js:142:26974
    at Object.nr [as useMemo] (/home/kasir/projects/you-say/node_modules/next/dist/compiled/next-server/app-page.runtime.prod.js:12:39130)
    at t.useMemo (/home/kasir/projects/you-say/node_modules/next/dist/compiled/next-server/app-page.runtime.prod.js:12:109856)
    at d (/home/kasir/projects/you-say/apps/frontend/.next/server/chunks/657.js:142:26969)
    at nj (/home/kasir/projects/you-say/node_modules/next/dist/compiled/next-server/app-page.runtime.prod.js:12:46251)
    at nM (/home/kasir/projects/you-say/node_modules/next/dist/compiled/next-server/app-page.runtime.prod.js:12:47571) {
  digest: '831143466'
}

Error occurred prerendering page "/sign-up". Read more: https://nextjs.org/docs/messages/prerender-error

ReferenceError: document is not defined
    at /home/kasir/projects/you-say/apps/frontend/.next/server/chunks/657.js:142:26284
    at get at_exp (/home/kasir/projects/you-say/apps/frontend/.next/server/chunks/657.js:142:26404)
    at c.scheduleTokenExpiration (/home/kasir/projects/you-say/apps/frontend/.next/server/chunks/657.js:142:26526)
    at new c (/home/kasir/projects/you-say/apps/frontend/.next/server/chunks/657.js:142:24884)
    at /home/kasir/projects/you-say/apps/frontend/.next/server/chunks/657.js:142:26974
    at Object.nr [as useMemo] (/home/kasir/projects/you-say/node_modules/next/dist/compiled/next-server/app-page.runtime.prod.js:12:39130)
    at t.useMemo (/home/kasir/projects/you-say/node_modules/next/dist/compiled/next-server/app-page.runtime.prod.js:12:109856)
    at d (/home/kasir/projects/you-say/apps/frontend/.next/server/chunks/657.js:142:26969)
    at nj (/home/kasir/projects/you-say/node_modules/next/dist/compiled/next-server/app-page.runtime.prod.js:12:46251)
    at nM (/home/kasir/projects/you-say/node_modules/next/dist/compiled/next-server/app-page.runtime.prod.js:12:47571)
 ✓ Generating static pages (6/6)

> Export encountered errors on following paths:
        /_not-found/page: /_not-found
        /page: /
        /shop/page: /shop
        /sign-up/page: /sign-up
Warning: command "next build" exited with non-zero status code

 NX   Running target build for project frontend and 1 task it depends on failed

Failed tasks:

- frontend:build

Hint: run the command with --verbose for more details.

Warning: command "NODE_ENV=production nx build frontend" exited with non-zero status code
——————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————

 NX   Ran target build:local for project frontend (18s)

   ✖  1/1 failed
   ✔  0/1 succeeded [0 read from cache]
kasir-barati commented 4 weeks ago

I downgraded my @fusionauth/react-sdk. Not the best option but at least I can get going and if you managed to address this issue please lemme know so I can upgrade to latest version.

-"@fusionauth/react-sdk": "^2.1.1",
+"@fusionauth/react-sdk": "^1.0.6"
JakeLo123 commented 4 weeks ago

This issue board will reflect the status of the issue. We're working on a fix for this one 👍

JakeLo123 commented 2 days ago

These are libraries we considered using to add this feature, but decided not to. PR #119

cookies-next -- will not work for this SDK because it only works within getServerSideProps. The SDK must be able to get cookies outside of that context. nextjs cookies -- using this will automatically opt routes into dynamic rendering. Ideally, we should avoid doing things that silently affect the rendering method of apps using the SDK.

kasir-barati commented 1 day ago

Question @JakeLo123, is not it easier for you and us who will use this SDK to have two hooks that work slightly different; what I mean is something like this:

  1. useFusionAuth hook will be client side.
  2. useServerSideFusionAuth hook will be a server side hook.

Now we can work with cookies based on the context and the context is clear since we will use different hooks. Just thinking out loud, might not be the brightest idea out there. Please feel free to point out the down sides if you see any.

JakeLo123 commented 1 day ago

Question @JakeLo123, is not it easier for you and us who will use this SDK to have two hooks that work slightly different; what I mean is something like this:

  1. useFusionAuth hook will be client side.
  2. useServerSideFusionAuth hook will be a server side hook.

Now we can work with cookies based on the context and the context is clear since we will use different hooks. Just thinking out loud, might not be the brightest idea out there. Please feel free to point out the down sides if you see any.

We certainly do want to support NextJS devs by providing a hook that works in both client and server context. The main downside with the above suggestion--in my opinion--would be creating 2 hooks that presumably do the same thing. The next release will have NextJS support where useFusionAuth works in client and server context--it's pending merge The documentation will describe how to configure the SDK in NextJS.

The purpose of my comment where I linked the 2 libraries was to document other options we considered and why we didn't use them.