# FusionAuth QuickStart for Next.js
Set up authentication in a Next.js app using FusionAuth. Follow this QuickStart guide to integrate secure login, user management, and authentication in your Next.js project.
> For the index of this section of the site, see [llms.txt](https://4002.18-224-161-104.sslip.io/docs/llms.txt)

In this QuickStart, you are going to build an application with Next.js and integrate it with FusionAuth. You'll be building it for [ChangeBank](https://www.youtube.com/watch?v=CXDxNCzUspM), a global leader in converting dollars into coins. It'll have areas reserved for users who have logged in as well as public facing sections.
The Docker Compose file and source code for a complete application are available at [https://github.com/FusionAuth/fusionauth-quickstart-javascript-nextjs-web](https://github.com/FusionAuth/fusionauth-quickstart-javascript-nextjs-web).
## Prerequisites
* [Node LTS](https://nodejs.org/en/download)
* [Docker](https://www.docker.com): The quickest way to stand up FusionAuth. (There are [other ways](https://4002.18-224-161-104.sslip.io/docs/get-started/download-and-install.md)).
## General Architecture
Here's a typical application login flow before FusionAuth:
```mermaid
sequenceDiagram
participant User
participant App as Application
User ->> App : View Homepage
User ->> App : Click Login Link
App ->> User : Show Login Form
User ->> App : Fill Out and Submit Login Form
App ->> App : Authenticates User
App ->> User : Display User's Account or Other Info
```
Request flow during login before FusionAuth
And here's the same application login flow after introducing FusionAuth:
```mermaid
sequenceDiagram
participant User
participant App as Application
participant FusionAuth
User ->> App : View Homepage
User ->> App : Click Login Link (to FusionAuth)
User ->> FusionAuth : View Login Form
FusionAuth ->> User : Show Login Form
User ->> FusionAuth : Fill Out and Submit Login Form
FusionAuth ->> FusionAuth : Authenticates User
FusionAuth ->> User: Go to Redirect URI
User ->> App: Request the Redirect URI
App ->> FusionAuth : Is User Authenticated?
FusionAuth ->> App : User is Authenticated
App ->> User : Display User's Account or Other Info
```
Request flow during login after FusionAuth
In general, you are introducing FusionAuth in order to normalize and consolidate user data. This helps make sure it is consistent and up-to-date as well as offloading your login security and functionality to FusionAuth.
## Getting Started
In this section, you'll get FusionAuth up and running, and configured with the ChangeBank application.
### Clone The Code
First off, grab the code from the repository and change into that directory.
```plaintext
git clone https://github.com/FusionAuth/fusionauth-quickstart-javascript-nextjs-web.git
cd fusionauth-quickstart-javascript-nextjs-web
```
### Run FusionAuth Via Docker
You'll find a Docker Compose file (`docker-compose.yml`) and an environment variables configuration file (`.env`) in the root directory of the repo.
Assuming you have Docker installed, you can stand up FusionAuth on your machine with the following.
```shell-session
$ docker compose up -d
```
Here you are using a bootstrapping feature of FusionAuth called [Kickstart](https://4002.18-224-161-104.sslip.io/docs/get-started/download-and-install/development/kickstart.md). When FusionAuth comes up for the first time, it will look at the `kickstart/kickstart.json` file and configure FusionAuth to your specified state.
Note
If you ever want to reset the FusionAuth application, you need to delete the volumes created by Docker Compose by executing `docker compose down -v`, then re-run `docker compose up -d`.
FusionAuth will be initially configured with these settings:
* Your client Id is `e9fdb985-9173-4e01-9d73-ac2d60d1dc8e`.
* Your client secret is `super-secret-secret-that-should-be-regenerated-for-production`.
* Your example username is `richard@example.com` and the password is `password`.
* Your admin username is `admin@example.com` and the password is `password`.
* The base URL of FusionAuth is `http://localhost:9011/`.
You can log in to the [FusionAuth admin UI](http://localhost:9011/admin) and look around if you want to, but with Docker and Kickstart, everything will already be configured correctly.
Note
If you want to see where the FusionAuth values came from, they can be found in the [FusionAuth app](http://localhost:9011/admin). The tenant Id is found on the Tenants page. To see the Client Id and Client Secret, go to the Applications page and click the `View` icon under the actions for the ChangeBank application. You'll find the Client Id and Client Secret values in the `OAuth configuration` section.
Caution
The `.env` file contains passwords. In a real application, always add this file to your `.gitignore` file and never commit secrets to version control.
### Create Next.js Application
In this section, you'll set up a basic Next.js application with two pages.
1. Homepage
2. Account - protected
Create a new application using the `npx`.
```shell-session
$ npx create-next-app@latest changebank --ts --eslint --no-tailwind --src-dir --app --import-alias "@/*"
```
Make sure you are in your new directory `changebank`.
```shell-session
$ cd changebank
```
Install [NextAuth.js](https://next-auth.js.org/getting-started/example), which simplifies integrating with FusionAuth and creating a secure web application.
```shell-session
$ npm install next-auth
```
Copy environment variables from our complete application example.
```shell-session
$ cp ../complete-application/.env.example .env.local
```
Also copy an image file into a new directory within `public` called `img`.
```shell-session
$ mkdir ./public/img && cp ../complete-application/public/img/money.jpg ./public/img/money.jpg
```
As you will be recreating all of the files in our app directory, please delete all files within `/src/app`.
```shell-session
$ rm -rf ./src/app && \
mkdir -p './src/app/api/auth/[...nextauth]' ./src/app/account ./src/app/makechange ./src/components
```
## Authentication
Next.js 13.2 introduced [Route Handlers](https://nextjs.org/docs/app/api-reference/file-conventions/route), which are the preferred way to handle REST-like requests. In the `Changebank` application you can configure NextAuth.js FusionAuth's provider in a new route handler by creating a file within `src/app/api/auth/[...nextauth]/route.ts`.
On first load of Next.js this file will make sure that you have all of the correct environment variables. The variables are then exported in an object called `authOptions` which can be imported on the server when you need to get our session using `getServerSession`.
The `FusionAuthProvider` is then provided to `NextAuth` as a provider for any `GET` or `POST` commands that are sent to the `/api/auth/*` route.
Create a new file named `src/app/api/auth/[...nextauth]/route.ts` and copy the following code for the ChangeBank application.
```ts
import NextAuth from "next-auth"
import FusionAuthProvider from "next-auth/providers/fusionauth"
const fusionAuthIssuer = process.env.FUSIONAUTH_ISSUER;
const fusionAuthClientId = process.env.FUSIONAUTH_CLIENT_ID;
const fusionAuthClientSecret = process.env.FUSIONAUTH_CLIENT_SECRET;
const fusionAuthUrl = process.env.FUSIONAUTH_URL;
const fusionAuthTenantId = process.env.FUSIONAUTH_TENANT_ID;
const missingError = 'missing in environment variables.';
if (!fusionAuthIssuer) {
throw Error('FUSIONAUTH_ISSUER' + missingError)
}
if (!fusionAuthClientId) {
throw Error('FUSIONAUTH_CLIENT_ID' + missingError)
}
if (!fusionAuthClientSecret) {
throw Error('FUSIONAUTH_CLIENT_SECRET' + missingError)
}
if (!fusionAuthUrl) {
throw Error('FUSIONAUTH_URL' + missingError)
}
if (!fusionAuthTenantId) {
throw Error('FUSIONAUTH_TENANT_ID' + missingError)
}
export const authOptions =
{
providers: [
FusionAuthProvider({
issuer: fusionAuthIssuer,
clientId: fusionAuthClientId,
clientSecret: fusionAuthClientSecret,
wellKnown: `${fusionAuthUrl}/.well-known/openid-configuration/${fusionAuthTenantId}`,
tenantId: fusionAuthTenantId, // Only required if you're using multi-tenancy
authorization:{
params:{
scope: 'openid offline_access email profile'
}
}
}),
],
}
const handler = NextAuth(authOptions)
export { handler as GET, handler as POST }
```
## App Customization
### Styles
Create a new file named `src/app/globals.css` and copy the below CSS for the ChangeBank application.
```css
h1 {
color: #096324;
}
h3 {
color: #096324;
margin-top: 20px;
margin-bottom: 40px;
}
a {
color: #096324;
}
p {
font-size: 18px;
}
.header-email {
color: #096324;
margin-right: 20px;
}
.fine-print {
font-size: 16px;
}
body {
font-family: sans-serif;
padding: 0px;
margin: 0px;
}
.h-row {
display: flex;
align-items: center;
}
#page-container {
display: flex;
flex-direction: column;
width: 100%;
height: 100%;
}
#page-header {
flex: 0;
display: flex;
flex-direction: column;
}
#logo-header {
display: flex;
justify-content: space-between;
align-items: center;
padding: 10px;
}
.menu-bar {
display: flex;
flex-direction: row-reverse;
align-items: center;
height: 35px;
padding: 15px 50px 15px 30px;
background-color: #096324;
font-size: 20px;
}
.menu-link {
font-weight: 600;
color: #ffffff;
margin-left: 40px;
}
.menu-link {
font-weight: 600;
color: #ffffff;
margin-left: 40px;
}
.inactive {
text-decoration-line: none;
}
.button-lg {
width: 150px;
height: 30px;
background-color: #096324;
color: #ffffff;
font-size: 16px;
font-weight: 700;
border-radius: 10px;
text-align: center;
text-decoration-line: none;
cursor: pointer;
}
.column-container {
display: flex;
flex-direction: row;
justify-content: space-between;
}
.content-container {
flex: 1;
display: flex;
flex-direction: column;
padding: 60px 20px 20px 40px;
}
.balance {
font-size: 50px;
font-weight: 800;
}
.change-label {
font-size: 20px;
margin-right: 5px;
}
.change-input {
font-size: 20px;
height: 40px;
text-align: end;
padding-right: 10px;
}
.change-submit {
font-size: 15px;
height: 40px;
margin-left: 15px;
border-radius: 5px;
}
.change-message {
font-size: 20px;
margin-bottom: 15px;
}
.error-message {
font-size: 20px;
color: #ff0000;
margin-bottom: 15px;
}
.app-container {
flex: 0;
min-width: 440px;
display: flex;
flex-direction: column;
margin-top: 40px;
margin-left: 80px;
}
.change-container {
flex: 1;
}
```
### Login Button
Create a new file in `src/components/LoginButton.tsx` that will be used for a button component. Our login button will only be used on the client side so make sure to add `use client` at the top of this file. For this button you can use the `signIn` and `signOut` functions from `next-auth/react`. By passing in the session from our pages you can determine if the `Log in` or `Log out` should be shown.
Copy the below code for the ChangeBank application into `src/components/LoginButton.tsx`.
```tsx
'use client';
import { signIn, signOut } from 'next-auth/react';
import type { Session } from 'next-auth';
export default function LoginButton({ session }: { session: Session | null }) {
if (session) {
return (
<>
Status: Logged in as {session?.user?.email}
>
);
}
return (
<>
>
);
}
```
### Login Link
Create a new file in `src/components/LoginLink.tsx` that will be used for a link component. Our login link will only be used on the client side so make sure to add `use client` at the top of this file. For this link you can use the `signIn` function from `next-auth/react`.
Copy the below code for the ChangeBank application into `src/components/LoginLink.tsx`.
```tsx
'use client';
import { signIn } from 'next-auth/react';
export default function LoginLink() {
return (
<>
To get started,{' '} signIn()} style={{ textDecoration: 'underline', cursor: 'pointer' }} > log in or create a new account.
> ); } ``` ### Layout Tip If this is your first time using the Next.js App Router, you should read through [Routing Fundamentals](https://nextjs.org/docs/app/getting-started/layouts-and-pages). Below you will find the full code for the [Root Layout](https://nextjs.org/docs/app/getting-started/layouts-and-pages). This has the overall structure of our application. The other pages will be added where `{children}` is located. Create a new file named `src/app/layout.tsx` and copy the below code to create the layout for the ChangeBank application. ```tsx import './globals.css'; import type { Metadata } from 'next'; import Image from 'next/image'; import LoginButton from '../components/LoginButton'; import { getServerSession } from 'next-auth/next'; import { authOptions } from './api/auth/[...nextauth]/route'; export const metadata: Metadata = { title: 'FusionAuth Next.js with NextAuth.js', description: 'Generated by create next app', }; export default async function RootLayout({ children, }: { children: React.ReactNode; }) { const session = await getServerSession(authOptions); return (