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

When using useAuthenticator build fails because of server-side rendering. #5778

Closed
4 tasks done
ramon-san opened this issue Sep 10, 2024 · 3 comments
Closed
4 tasks done
Labels
Authenticator An issue or a feature-request for an Authenticator UI Component question General question React An issue or a feature-request for React platform

Comments

@ramon-san
Copy link

Before creating a new issue, please confirm:

On which framework/platform are you having an issue?

React

Which UI component?

Authenticator

How is your app built?

Next.js

What browsers are you seeing the problem on?

No response

Which region are you seeing the problem in?

No response

Please describe your bug.

I know a solution to the problem and this is not per se a bug, but I would really appreciate some feedback to get this implemented as best as possible.

I am building a Next.js project and my _app.tsx file looks like this:

import '@/styles/globals.css';
import '@/styles/fonts.css';
import '@aws-amplify/ui-react/styles.css';
import { AlertProvider } from '@/src/contexts/AlertContext';
import AlertBanner from '@/src/components/basics/AlertBanner';
import AccessManager from '@/src/components/auth/AccessManager';
import { configureAmplify, amplifyTheme } from '@/src/utils/amplify-config';
import { ThemeProvider, Authenticator } from '@aws-amplify/ui-react';

configureAmplify();

  return (
    <>
      <AlertProvider>
        <Authenticator.Provider>
          <ThemeProvider theme={amplifyTheme}>
            <main className="bg-[#EEEEEE]">
              <AccessManager>
                <Component {...pageProps} />
              </AccessManager>
            </main>
          </ThemeProvider>
        </Authenticator.Provider>
        <AlertBanner />
      </AlertProvider>
    </>
  );
}

In this structure, I use the <AccessManager> component to handle user status in the whole project instead of doing this granularly per page. The code for this component is the following:

import { ReactNode } from 'react';
import { useRouter } from 'next/router';
import { useAuthenticator } from '@aws-amplify/ui-react';

interface AccessManagerProps {
  children: ReactNode;
}

export default function AccessManager({ children }: AccessManagerProps) {
  const { authStatus } = useAuthenticator(context => [context.authStatus]);
  const router = useRouter();

  if (authStatus !== 'authenticated' && router.pathname !== '/app' && router.pathname !== '/privacy-policy' && router.pathname !== '/terms-of-service') {
    router.push(`/app`);
  } else {
    return <>{children}</>;
  }
}

The code works well when I run things locally, but the problem appears when I try to build the project (npm run build). I used to handle this logic without the @aws-amplify/ui-react library and things worked; I used to have a separate context file that basically handles what useAuthenticator does. The build command fails because the component is being server-side rendered during the build.

The problem with doing SSR with this component is that I use the next/router library which only works on the client-side. The error message I get is the following and it appears for multiple pages: Error: No router instance found. you should only use "next/router" inside the client side of your app. https://nextjs.org/docs/messages/no-router-instance.

I already confirmed the build works if I remove the useAuthenticator from this file or if I remove the next/router and keep the useAuthenticator, however, both provide important functionality that I need to maintain. Any recommendations on what would be the best way to handle this scenario? Is what I'm doing with the <AccessManager> component a good practice and if so how can I improve this to avoid the current error?

What's the expected behaviour?

I was not expecting useAuthenticator to cause SSR issues.

Help us reproduce the bug!

It should be reproducible if you try to run npm run build in a project that uses useAuthenticator inside a component that at the same time uses next/router. The component should be used in the _app.tsx file.

Code Snippet

With the help of Claude I already made it work using a useEffect() that avoids doing SSR on the whole code. However, I am not a big fan of this solution cause I think handling the client state can cause some jitter in the application when it loads for the first time. What would be the best way to handle what I am trying to do?

import { ReactNode, useEffect, useState } from 'react';
import { useRouter } from 'next/router';
import { useAuthenticator } from '@aws-amplify/ui-react';

interface AccessManagerProps {
  children: ReactNode;
}

export default function AccessManager({ children }: AccessManagerProps) {
  const { authStatus } = useAuthenticator(context => [context.authStatus]);
  const [isClient, setIsClient] = useState(false);
  const router = useRouter();

  // Only run on the client side.
  useEffect(() => {
    setIsClient(true);
  }, []);

  if (!isClient) {
    return null;
  }


  if (authStatus !== 'authenticated' && !['/app', '/privacy-policy', '/terms-of-service'].includes(router.pathname)) {
    router.push('/app');
  } else {
    return <>{children}</>;
  }
}

Console log output

No response

Additional information and screenshots

No response

@github-actions github-actions bot added the pending-triage Issue is pending triage label Sep 10, 2024
@reesscot
Copy link
Contributor

reesscot commented Sep 10, 2024

@ramon-san Are you using Next.js's App or Pages router? If you are using the latest version of Next.js the App Router will be default. Amplify UI components are client components, so you need to put 'use client' at the top of any pages where you are using Amplify UI.

see: https://ui.docs.amplify.aws/react/getting-started/usage/nextjs#app-router

@reesscot reesscot added question General question React An issue or a feature-request for React platform Authenticator An issue or a feature-request for an Authenticator UI Component and removed pending-triage Issue is pending triage labels Sep 10, 2024
@ramon-san
Copy link
Author

ramon-san commented Sep 11, 2024

Thanks for the heads up @reesscot! If the default is App Router then I am using this. However, I am managing the folder structure as a Pages Router. In the end, I solved the problem by checking authStatus === "configuring".

import { ReactNode } from 'react';
import { useRouter } from 'next/router';
import { useAuthenticator } from '@aws-amplify/ui-react';
import Spinner from '@/src/components/basics/Spinner';

interface AccessManagerProps {
  children: ReactNode;
}

export default function AccessManager({ children }: AccessManagerProps) {
  const { authStatus } = useAuthenticator(context => [context.authStatus]);
  const router = useRouter();

  if (authStatus === 'configuring') {
    return <Spinner />
  }

  if (authStatus !== 'authenticated' && !['/app', '/privacy-policy', '/terms-of-service'].includes(router.pathname)) {
    router.push('/app');
  } else {
    return <>{children}</>;
  }
}

Not having this check (authStatus === 'configuring'), the component loaded too fast and it caused the router to not be initialized by the time the code reached the router.push() section.

@reesscot
Copy link
Contributor

Glad you found a solution! I'll close this out for now, please feel free to open another issue if you have any more questions!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Authenticator An issue or a feature-request for an Authenticator UI Component question General question React An issue or a feature-request for React platform
Projects
None yet
Development

No branches or pull requests

2 participants