Build a custom login page with Next.js, Tailwind CSS, and Next-Auth
Next-Auth provides a very basic login page for you out of the box. However, once you ship your product to production, you're going to want to elevate that experience and have the login page really pop. To do that, you can create a custom login page and tell Next-Auth where to find it.
This guide walks through creating the page and using Next-Auth to log in.
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.
Link to source code: https://github.com/ethanmick/nextauth-custom-login
The first thing to do is to build the Login
page in the app directory:
The form is interactive, so it needs to be a client component. Create a new file in the same app/login
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 { useRouter, useSearchParams } from 'next/navigation'
import { useState } from 'react'
export const Form = () => {
const router = useRouter()
const searchParams = useSearchParams()
const callbackUrl = searchParams.get('callbackUrl') || '/dashboard'
const [email, setEmail] = useState('')
const [password, setPassword] = useState('')
const [error, setError] = useState('')
const onSubmit = async (e: React.FormEvent) => {
e.preventDefault()
try {
const res = await signIn('credentials', {
redirect: false,
email,
password,
callbackUrl
})
console.log('Res', res)
if (!res?.error) {
router.push(callbackUrl)
} else {
setError('Invalid email or password')
}
} catch (err: any) {}
}
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">
Login
</Button>
</div>
</form>
)
}
The components referenced can be copied and pulled in from here: https://ui.shadcn.com/docs/primitives/accordion
Source Code
You can find the source on my GitHub account: https://github.com/ethanmick/nextauth-custom-login