Add Stytch authentication to your Next.js application

In this guide, you'll learn how to add Stytch-powered authentication to your Next.js application using our frontend JavaScript SDK and our backend Node SDK, along with our pre-built login UI component.

We'll cover a similar implementation to that used in this Next.js example app, which we'll reference throughout this guide.

There are many different ways to implement Stytch, and the specific strategy you choose will depend on your use case and preferences. This guide is intended to demonstrate one way of implementing Stytch in order to get you up and running as quickly as possible.

Before you start

Create a Stytch Consumer project via the Stytch Dashboard if you don't have one already. To do so, click on your existing project name in top left corner of the Dashboard, click Create a new project, and then select Consumer authentication.

Copy your project_id, secret, and public_token from the Test environment tab in the API keys section of the Stytch Dashboard. You'll need to include these values when instantiating Stytch SDKs.

Finally, navigate to the Frontend SDKs tab in the Stytch Dashboard and enable the authentication products that you're interested in adding to your application.

Step 1: Install Stytch SDKs and configure your API keys

Add our frontend JavaScript SDK and our backend Node SDK packages to your Next.js application:

npm install @stytch/nextjs @stytch/vanilla-js stytch --save

Add your Stytch project's API keys to your application as environment variables, as we do here.

STYTCH_PROJECT_ENV=test
STYTCH_PROJECT_ID="YOUR_STYTCH_PROJECT_ID"
STYTCH_PUBLIC_TOKEN="YOUR_STYTCH_PUBLIC_TOKEN"
STYTCH_SECRET="YOUR_STYTCH_SECRET"

Step 2: Wrap your application in a StytchProvider

First, initialize the Stytch client and add a StytchProvider component to the root of your application so that the frontend JavaScript SDK is available to child components, like we do here.

const stytch = createStytchUIClient(
  process.env.STYTCH_PUBLIC_TOKEN
);

export default function App({ Component, pageProps }) {
  return (
    <>
      <Head>
        // Truncated
      </Head>
      <StytchProvider stytch={stytch}>
        <Component {...pageProps} />
      </StytchProvider>
    </>
  );
}

Step 3: Add a StytchLogin component

Create a StytchLogin component, like we do here. You can configure which authentication methods you'd like to offer by modifying the products array in your config object. Here's an example that includes our Email Magic Links and OAuth products:

const Login = () => {
  const REDIRECT_URL = "SEE_STEP_5";

  const config = {
    products: [Products.emailMagicLinks, Products.oauth],
    emailMagicLinksOptions: {
      loginRedirectURL: REDIRECT_URL,
      loginExpirationMinutes: 60,
      signupRedirectURL: REDIRECT_URL,
      signupExpirationMinutes: 60,
    },
    oauthOptions: {
      providers: [{ type: "google" }],
      loginRedirectURL: REDIRECT_URL,
      signupRedirectURL: REDIRECT_URL,
    },
  };

  return <StytchLogin config={config} styles={styles} />;
};

Next, add the StytchLogin component to your application's login page, like we do here. You'll notice that we check for a logged-in user before displaying the StytchLogin component, which we'll cover in more detail in Step 5.

export default function LoginPage() {
  const { user, isInitialized } = useStytchUser();
  const router = useRouter();

  useEffect(() => {
    if (isInitialized && user) {
      router.replace("/profile");
    }
  }, [user, isInitialized, router]);

  return <Login />;
}

We also offer headless JavaScript methods that you can call directly if you'd prefer to build your own login UI, though we'll focus on the StytchLogin component in this guide.

Step 4: Handle redirects and complete the authentication flow

The authentication flow will begin when your user interacts with the StytchLogin component. There are two general types of authentication flows: those where the user is redirected to or must navigate to a different page (like OAuth or Email Magic Links), and those where the user remains on the same page in your application (like OTP or Passwords).

For flows where the user is redirected to or must navigate to a different page, you'll need to handle the redirect back to your application. First, specify the URL that users should be redirected back to by setting the REDIRECT_URL parameter used in your StytchLogin component config from Step 3. Be sure to also add that URL to the Redirect URLs tab in the Stytch Dashboard.

Next, implement logic on the page that users are redirected to that retrieves the token value that Stytch adds to the redirect URL query parameters. Pass that token to the appropriate Stytch authenticate method, as we do here.

const stytch_token_type = router?.query?.stytch_token_type?.toString();
const token = router?.query?.token?.toString();

if (token && stytch_token_type === OAUTH_TOKEN) {
    stytch.oauth.authenticate(token, {
        session_duration_minutes: 60,
    });
} else if (token && stytch_token_type === MAGIC_LINKS_TOKEN) {
    stytch.magicLinks.authenticate(token, {
        session_duration_minutes: 60,
    });
}

For flows where the user remains on the same page in your application, the authenticate method will be called automatically by the StytchLogin component.

Step 5: Grant access to protected frontend content

Upon successful authentication, our frontend JavaScript SDK will automatically populate the session and user objects. You can listen for changes in the session and user objects using our useStytchSession and useStytchUser hooks, as we do here.

const { user, isInitialized } = useStytchUser();
const { session, isInitialized } = useStytchSession();

Once your application detects a non-null user object, you can redirect to a page that displays logged-in content, as we do here.

useEffect(() => {
    if (!isInitialized) {
      return;
    }
    if (user) {
      router.replace("/profile");
    }
  }, [router, user, isInitialized]);

Each page that displays logged-in content should also check to make sure a user is present, as we do here.

export default function ProfilePage() {
  const { user, isInitialized } = useStytchUser();
  const router = useRouter();

  useEffect(() => {
    if (isInitialized && !user) {
      router.replace("/");
    }
  }, [user, isInitialized, router]);

  return <Profile />;
}

Step 6: Grant access to protected server-side routes

You can add authentication to your protected server-side routes by loading our backend Node SDK, retrieving the Stytch session_token or session_jwt from the request cookies (automatically set by our frontend JavaScript SDK and included in requests by the browser), and then calling our sessions.authenticate or sessions.authenticateJwt methods, as we do here.

// Load our backend Node SDK
const loadStytch = () => {
  if (!client) {
    client = new stytch.Client({
      project_id: process.env.STYTCH_PROJECT_ID || "",
      secret: process.env.STYTCH_SECRET || "",
      env:
        process.env.STYTCH_PROJECT_ENV === "live"
          ? stytch.envs.live
          : stytch.envs.test,
    });
  }

  return client;
};

export async function getServerSideProps({ req }) {
  const redirectRes = {
    redirect: {
      destination: "/",
      permanent: false,
    },
  };
  // Retrieve the Stytch JWT from the cookies included in the request
  const sessionJWT = req.cookies["stytch_session_jwt"];

  if (!sessionJWT) {
    return redirectRes;
  }

  const stytchClient = loadStytch();

  try {
    // Authenticate the Stytch JWT
    await stytchClient.sessions.authenticateJwt(sessionJWT);
    // The session is now authenticated; proceed with returning protected data 
    return { props: {} };
  } catch (e) {
    return redirectRes;
  }
}

What's next

At this point, your application will have a Stytch-powered login flow and logic to determine when to display protected content. Now you can get back to focusing on your own application's functionality, and we'll handle the rest!

See also

If you're using the Next.js 13 App Router functionality, check out our Next.js 13 example app.

We'd also recommend checking out our Stytch demo app, which is built using Next.js and demonstrates a variety of different Stytch products.