Before starting the project we need to get Supabse anon_key and project_url.
To get that.
Now let's setup the nextjs Project
npx create-next-app@latest .
Use the default template. And make a .env file in root folder and paste the copied url
NEXT_PUBLIC_SUPABASE_URL=YOUR_SUPABASE_URL
NEXT_PUBLIC_SUPABASE_ANON_KEY=YOUR_SUPABASE_ANON_KEY
Then install the supabase SDK using following command.
npm install @supabase/supabase-js @supabase/ssr
Now create a utils folder in root of the project, inside utils create another folder supabase then create a client.ts and server.ts file
Inside client file.
/utils/supabase/client.ts
import { createBrowserClient } from '@supabase/ssr'
export function createClient() {
// Create a supabase client on the browser with project's credentials
return createBrowserClient(
process.env.NEXT_PUBLIC_SUPABASE_URL!,
process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY!
)
}/utils/supabase/server.ts
import { createServerClient } from '@supabase/ssr'
import { cookies } from 'next/headers'
export async function createClient() {
const cookieStore = await cookies()
return createServerClient(
process.env.NEXT_PUBLIC_SUPABASE_URL!,
process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY!,
{
cookies: {
getAll() {
return cookieStore.getAll()
},
setAll(cookiesToSet) {
try {
cookiesToSet.forEach(({ name, value, options }) =>
cookieStore.set(name, value, options)
)
} catch {
// The `setAll` method was called from a Server Component.
// This can be ignored if you have middleware refreshing
// user sessions.
}
},
},
}
)
}Since Server Components can't write cookies, you need middleware to refresh expired Auth tokens and store them.
Create a middleware.ts file in root of the folder, and middleware.ts file in the utils/supabase folder.
/middleware.ts
import { type NextRequest } from 'next/server'
import { updateSession } from '@/utils/supabase/middleware'
export async function middleware(request: NextRequest) {
return await updateSession(request)
}
export const config = {
matcher: [
'/((?!_next/static|_next/image|favicon.ico|.*\\.(?:svg|png|jpg|jpeg|gif|webp)$).*)',
],
}utils/supabase/middleware.ts
import { createServerClient } from '@supabase/ssr'
import { NextResponse, type NextRequest } from 'next/server'
export async function updateSession(request: NextRequest) {
let supabaseResponse = NextResponse.next({
request,
})
const supabase = createServerClient(
process.env.NEXT_PUBLIC_SUPABASE_URL!,
process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY!,
{
cookies: {
getAll() {
return request.cookies.getAll()
},
setAll(cookiesToSet) {
cookiesToSet.forEach(({ name, value, options }) => request.cookies.set(name, value))
supabaseResponse = NextResponse.next({
request,
})
cookiesToSet.forEach(({ name, value, options }) =>
supabaseResponse.cookies.set(name, value, options)
)
},
},
}
)
// refreshing the auth token
await supabase.auth.getUser()
return supabaseResponse
}The actions file will contain the login and signup action. let's make...
Create a Folder in root named action then make a file inside the folder auth.ts
/action/auth.ts
'use server'
import { revalidatePath } from 'next/cache'
import { redirect } from 'next/navigation'
import { createClient } from '@/utils/supabase/server'
export async function login(formData: {email: string, password: string}) {
if(!formData.email || !formData.password) {
throw new Error("FormData is Required")
}
const supabase = await createClient()
const data = {
email: formData.email,
password: formData.password
}
const { error } = await supabase.auth.signInWithPassword(data)
if (error) {
redirect('/error')
}
revalidatePath('/', 'layout')
redirect('/')
}
export async function signup(formData: {email: string, password: string, username: string}) {
if(!formData.email || !formData.password || !formData.username) {
throw new Error("FormData is Required")
}
const supabase = await createClient()
const clientData = {
email: formData.email,
password: formData.password,
}
const { error } = await supabase.auth.signUp(clientData)
if (error) {
redirect('/error')
}
revalidatePath('/', 'layout')
redirect('/login')
}
export async function logout() {
const supabase = await createClient()
await supabase.auth.signOut()
revalidatePath('/', 'layout')
redirect('/login')
}Now make a error.tsx page inside action folder.
export default function ErrorPage() {
return <p>Sorry, something went wrong</p>
}Now Setup the login and signup page. Inside the app folder we will be using the shadcn to design the components and react-hook-form to handel the form.
/app/signup/page.tsx
"use client";
import { signup } from "@/action/auth";
import { Button } from "@/components/ui/button";
import { Input } from "@/components/ui/input";
import Link from "next/link";
import { useForm } from "react-hook-form";
export default function page() {
const { register, handleSubmit } = useForm<{
username: string;
email: string;
password: string;
}>();
return (
<section className="flex items-center mt-10 justify-center">
<form
onSubmit={handleSubmit((data: { email: string; password: string; username: string }) =>
signup(data)
)}
className="flex flex-col border border-black rounded-md p-4 gap-4"
>
<h1 className="text-2xl font-bold">Create your account</h1>
<div>
<Input {...register("username")} placeholder="Username" />
</div>
<div>
<Input {...register("email")} placeholder="Email" />
</div>
<div>
<Input {...register("password")} placeholder="Password" />
</div>
<Button>Signup</Button>
<div>
<p>
Already have an account{" "}
<Link href={"/login"} className="text-blue-800">
Login
</Link>
</p>
</div>
</form>
</section>
);
}
/app/login/page.tsx
"use client";
import { login } from "@/action/auth";
import { Button } from "@/components/ui/button";
import { Input } from "@/components/ui/input";
import Link from "next/link";
import { useForm } from "react-hook-form";
export default function page() {
const { register, handleSubmit } = useForm<{
email: string;
password: string;
}>();
return (
<section className="flex items-center mt-10 justify-center">
<form
onSubmit={handleSubmit((data: { email: string; password: string }) =>
login(data)
)}
className="flex flex-col border border-black rounded-md p-4 gap-4"
>
<h1 className="text-2xl font-bold">Login to your account</h1>
<div>
<Input {...register("email")} placeholder="Email" />
</div>
<div>
<Input {...register("password")} placeholder="Password" />
</div>
<Button>Login</Button>
<div>
<p>
Don't have an account{" "}
<Link href={"/signup"} className="text-blue-800">
Signup
</Link>
</p>
</div>
</form>
</section>
);
}
That's it. run the npm run dev go to the http://localhost:3000/signup create a user with username, email and password then conform the email in side the gmail. then login to your account.
Thank You.