Has anyone managed to get supertokens working with next.js v13 app directory?
d
Has anyone managed to get supertokens working with next.js v13 app directory?
n
Hi @Deleted User, The app directory setup should be similar to the pages directory setup. We dont have docs for it but if you are facing issues I can help
d
yeah that would be appreciated, been stuck on this for a while
n
What have you tried already?
d
A lot lol, gone through the Next.js integration guide, among many other things. First question is regarding `/config/frontendConfig.ts`:
Copy code
ts
import ThirdPartyEmailPasswordReact from 'supertokens-auth-react/recipe/thirdpartyemailpassword'
import SessionReact from 'supertokens-auth-react/recipe/session'
import { appInfo } from './appInfo'
import Router from 'next/router'

export const frontendConfig = () => {
  return {
    appInfo,
    recipeList: [
      ThirdPartyEmailPasswordReact.init({
        signInAndUpFeature: {
          providers: [
            ThirdPartyEmailPasswordReact.Google.init(),
            ThirdPartyEmailPasswordReact.Facebook.init(),
            ThirdPartyEmailPasswordReact.Github.init(),
            ThirdPartyEmailPasswordReact.Apple.init(),
          ],
        },
      }),
      SessionReact.init(),
    ],
    windowHandler: (oI: any) => {
      return {
        ...oI,
        location: {
          ...oI.location,
          setHref: (href: string) => {
            Router.push(href)
          },
        },
      }
    },
  }
}
SuperTokensReact.init
is meant to be called from
pages/_app.tsx
,
Copy code
tsx

if (typeof window !== 'undefined') {
  // we only want to call this init function on the frontend, so we check typeof window !== 'undefined'
  SuperTokensReact.init(frontendConfig())
}

function MyApp({ Component, pageProps }: AppProps) {
  return (
    <SuperTokensWrapper>
      <Component {...pageProps} />
    </SuperTokensWrapper>
  );
}

export default MyApp
, however next.js v13 (app directory) has a new layout system
so what is the app directory equivalent of this code?
SuperTokensReact.init()
is also meant to be called outside of a component, but because
next/router
is deprecated for the app directory, how should we call
Router.push
?
n
You could do this in the Root Layout. Other than that you would have to call it on every route's
page.js
d
Doesn't work in the root layout, either I get an
Error: No instance of Session found. Make sure to call the Session.init method. See
error (which happens if i call
.init()
inside
useEffect()
), or I'm unable to access the router because it's outside of the page component
n
Does this get thrown from the root layout itself? or from some page component?
d
that error gets thrown from the root layout itself
n
Can I see the code for your root layout?
The root layout is meant to replace
_app.js
d
correct, so the root layout i'm focusing on (i have several) is has the path `src/app/(home)/home/layout-client.tsx`:
Copy code
tsx
'use client'

// imports

export default function RootLayout(props) {
  useEffect(() => {
    SuperTokensReact.init(frontendConfig());
  }, []);

  return (
    <ChakraProvider theme={theme}>
      <SuperTokensWrapper>
        {children}
      </SuperTokensWrapper>
    </ChakraProvider>
  )
}
And the error that gets thrown:
Copy code
txt
Unhandled Runtime Error
Error: No instance of Session found. Make sure to call the Session.init method. See https://supertokens.io/docs/emailpassword/quick-setup/frontend
n
You can call init outside of a use effect
Just before the return
d
if i take it out of useEffect, and pass
const router = useRouter()
to it (e.g.
frontendConfig(router)
), I get the error
Warning: Can't perform a React state update on a component that hasn't mounted yet. This indicates that you have a side-effect in your render function that asynchronously later calls tries to update the component. Move this work to useEffect instead.
n
Hmm
Yeah looks like they force you do navigation inside a component context now
So the problem is that
SuperTokensWrapper
needs Session recipe
But that tries to run before the use effect
Can I see the frontendConfig?
d
yeah, here it is:
Copy code
ts
'use client'

import ThirdPartyReact from "supertokens-auth-react/recipe/thirdparty";
import SessionReact from "supertokens-auth-react/recipe/session";
import { appInfo } from "./appInfo";
//import Router from "next/router";

export let frontendConfig = (router: any) => {
    return {
        appInfo,
        // recipeList contains all the modules that you want to
        // use from SuperTokens. See the full list here: https://supertokens.com/docs/guides
        recipeList: [
            ThirdPartyReact.init({
                signInAndUpFeature: {
                    providers: [
                        ThirdPartyReact.Github.init(),
                        ThirdPartyReact.Apple.init(),
                    ],
                },
            }),
            SessionReact.init(),
        ],
        // this is so that the SDK uses the next router for navigation
        windowHandler: (oI) => {
            return {
                ...oI,
                location: {
                    ...oI.location,
                    setHref: (href) => {
              //Router.push(href);
                      router.push(href);
                    },
                },
            };
        },
    };
};
n
One way could be to pass a function (or a set of functions) to frontendConfig that handles navigation
d
(tried replacing
Router
from
next/router
with a router param from
useRouter()
hook in the RootLayout component, but obviously that leads to the issue previously discussed)
n
And then call that function in relevant places inside windowHandler
That way you dont reference router in the config itself but in the components that call supertokens init
d
pass it something like a state value?
n
Something like this:
Copy code
SuperTokensReact.init(frontendConfig({
  push: () => {...},
}));
And then in window handler
Copy code
export let frontendConfig = ({push}) => {
...
windowHandler: (oI) => {
            return {
                ...oI,
                location: {
                    ...oI.location,
                    setHref: (href) => {
                      push(href);
                    },
                },
            };
        },
And then the push function could use the router hooks
d
what would the contents of push look like?
ah ok...
Copy code
tsx
const push = (href) => {
  router.push(href);
}
?
n
Yeah its unfortunate but with the lack of router docs for next 13 at the moment this is the only thing I can think of
Yeah something like that
d
Can you give a full example?
n
So we are working on docs for it but they arent complete at all and we dont have a full sample to share at the moment
Unfortunately all I can do is help out with issues for now
Next 13 breaks some of the fundamental things of 12 so itll be a while till we have full docs for it
d
by full example i mean just the RootLayout code + the frontendConfig code
(i.e. the stuff I've already pasted)
n
If you open an issue in the auth react SDK we can probably provide something in a couple days (once a little bandwidth frees up in the team)
d
I'm not sure it's as easy as you think it is, passing a handler function doesn't bypass that error
n
Yeah like I said it was the only thing I could think of, theres probably other nuances to Next 13 that we need to evaluate properly
d
fair enough, i get v13 is quite a large departure from older versions, so might take some time for you guys to implement a solution
appreciate the help nonetheless, i'll check back in a few days
p
have we got this resolved, am too getting this error
d
Not that I’m aware of
n
We are still evaluating everything that would need to change/get affected with Next13 (including app directories) but we dont have any progress to report as of yet
p
okk thanks for the update
I was trying to wrap my Home component (using custom UI)in SessionAuth, but got some error while dealing with sessioncontext
87 Views