3 min read

Build a custom register page with Next.js, Tailwind CSS, and Next-Auth

Build a custom register page with Next.js, Tailwind CSS, and Next-Auth
Custom Registration Page

Build a custom registration page for NextAuth! The credential provider checks credentials against an external source, but there is nothing in NextAuth that helps get the user into the database in the first place.

This guide walks through creating the page and the API route to insert a new user into the database.

Technologies

It's okay if you have a slightly different technology stack. Just adapt the code and logic to your specific use case.

Guide

Follow along, or download the entire project.

Source: https://github.com/ethanmick/nextauth-custom-register

The first thing to do is to build the Register page in the app directory:

import Link from 'next/link'
import { RegisterForm } from './form'

export default function RegisterPage() {
  return (
    <div className="h-screen w-screen flex justify-center items-center bg-slate-100">
      <div className="sm:shadow-xl px-8 pb-8 pt-12 sm:bg-white rounded-xl space-y-12">
        <h1 className="font-semibold text-2xl">Create your Account</h1>
        <RegisterForm />
        <p className="text-center">
          Have an account?{' '}
          <Link className="text-indigo-500 hover:underline" href="/login">
            Sign in
          </Link>{' '}
        </p>
      </div>
    </div>
  )
}

app/register/page.tsx

The form is interactive, so it needs to be a client component. Create a new file in the same app/register directory called form.tsx that you can import:

'use client'

import { Alert } from '@/components/ui/alert'
import { Button } from '@/components/ui/button'
import { Input } from '@/components/ui/input'
import { Label } from '@/components/ui/label'
import { signIn } from 'next-auth/react'
import { useState } from 'react'

export const RegisterForm = () => {
  const [email, setEmail] = useState('')
  const [password, setPassword] = useState('')
  const [error, setError] = useState<string | null>(null)

  const onSubmit = async (e: React.FormEvent) => {
    e.preventDefault()

    try {
      const res = await fetch('/api/register', {
        method: 'POST',
        body: JSON.stringify({
          email,
          password
        }),
        headers: {
          'Content-Type': 'application/json'
        }
      })
      if (res.ok) {
        signIn()
      } else {
        setError((await res.json()).error)
      }
    } catch (error: any) {
      setError(error?.message)
    }
  }

  return (
    <form onSubmit={onSubmit} className="space-y-12 w-full sm:w-[400px]">
      <div className="grid w-full items-center gap-1.5">
        <Label htmlFor="email">Email</Label>
        <Input
          className="w-full"
          required
          value={email}
          onChange={(e) => setEmail(e.target.value)}
          id="email"
          type="email"
        />
      </div>
      <div className="grid w-full items-center gap-1.5">
        <Label htmlFor="password">Password</Label>
        <Input
          className="w-full"
          required
          value={password}
          onChange={(e) => setPassword(e.target.value)}
          id="password"
          type="password"
        />
      </div>
      {error && <Alert>{error}</Alert>}
      <div className="w-full">
        <Button className="w-full" size="lg">
          Register
        </Button>
      </div>
    </form>
  )
}

The components referenced can be copied and pulled in from here: https://ui.shadcn.com/docs/primitives/accordion

Last, to create the user in Prisma you need to create a new API route that will handle the incoming POST request.

Create a new file: app/api/register/route.ts and put in the following:

import { prisma } from '@/lib/prisma'
import { hash } from 'bcrypt'
import { NextResponse } from 'next/server'

export async function POST(req: Request) {
  try {
    const { email, password } = await req.json()
    const hashed = await hash(password, 12)

    const user = await prisma.user.create({
      data: {
        email,
        password: hashed
      }
    })

    return NextResponse.json({
      user: {
        email: user.email
      }
    })
  } catch (err: any) {
    return new NextResponse(
      JSON.stringify({
        error: err.message
      }),
      {
        status: 500
      }
    )
  }
}

The app/api/register/route.ts file.

Always remember to be careful when dealing with user passwords. Here, we use Bcrypt to hash the password before saving it.

Source Code

You can find the source on my GitHub account: https://github.com/ethanmick/nextauth-custom-register