"use client";
import { AuthForm } from "@/components/form/auth-form"; import { Button } from "@nextui-org/button"; import { signIn } from "next-auth/react"; import { toast } from "sonner";
const SignUp = () => { const signInWithGoogle = async () => { try { await signIn("google"); } catch (error: any) { toast.error(error.message); } };
return ( <div className="flex min-h-screen w-full items-start justify-center md:py-20"> <div className="flex h-screen w-full flex-col gap-7 px-5 py-10 md:h-max md:w-fit md:rounded-3xl md:bg-white md:p-14 md:shadow-2xl"> <h1 className="text-3xl font-light">Sign in/Create account</h1> <div className="flex flex-col gap-5"> <AuthForm /> <div className="flex w-full items-center gap-2"> <hr className="w-full border-zinc-300" /> OR <hr className="w-full border-zinc-300" /> </div> <Button onClick={signInWithGoogle} color="primary" variant="bordered" radius="full" className="!border" > <svg xmlns="http://www.w3.org/2000/svg" height="24" viewBox="0 0 24 24" width="24" > <path d="M22.56 12.25c0-.78-.07-1.53-.2-2.25H12v4.26h5.92c-.26 1.37-1.04 2.53-2.21 3.31v2.77h3.57c2.08-1.92 3.28-4.74 3.28-8.09z" fill="#4285F4" /> <path d="M12 23c2.97 0 5.46-.98 7.28-2.66l-3.57-2.77c-.98.66-2.23 1.06-3.71 1.06-2.86 0-5.29-1.93-6.16-4.53H2.18v2.84C3.99 20.53 7.7 23 12 23z" fill="#34A853" /> <path d="M5.84 14.09c-.22-.66-.35-1.36-.35-2.09s.13-1.43.35-2.09V7.07H2.18C1.43 8.55 1 10.22 1 12s.43 3.45 1.18 4.93l2.85-2.22.81-.62z" fill="#FBBC05" /> <path d="M12 5.38c1.62 0 3.06.56 4.21 1.64l3.15-3.15C17.45 2.09 14.97 1 12 1 7.7 1 3.99 3.47 2.18 7.07l3.66 2.84c.87-2.6 3.3-4.53 6.16-4.53z" fill="#EA4335" /> <path d="M1 1h22v22H1z" fill="none" /> </svg> Sign In with Google </Button> </div> </div> </div> ); };
export default SignUp; in single page show options of login or signup but signup or create account button take name email and password but signin takes only email password
I'll help you create a single page that toggles between login and signup options, with the appropriate fields for each mode.
This component creates a single authentication page that toggles between login and signup modes:
Tabs
component to switch between login and signup modes.When "Login" is selected:
When "Sign Up" is selected:
The form submission logic changes based on the selected mode:
signIn
with credentialsYou'll need to implement the actual signup API call based on your backend setup. The commented code shows where you would make that API call.
"use client";
import { zodResolver } from "@hookform/resolvers/zod"; import * as z from "zod"; import { Form, FormControl, FormField, FormItem, FormMessage, } from "@/components/ui/form"; import { useForm } from "react-hook-form"; import { Eye, EyeOff } from "lucide-react"; import { useState } from "react"; import { ZodAuthSchema } from "@/lib/zodSchemas"; import { motion as m } from "framer-motion"; import { toast } from "sonner"; import { signIn } from "next-auth/react"; import { useCreateAccount } from "@/api-hooks/user/create-user-account"; import { UserResProps } from "@/lib/types/types"; import { deleteCookie, getCookie } from "cookies-next"; import { useRouter } from "next/navigation"; import { Button } from "@nextui-org/button"; import { Input } from "@nextui-org/input";
export function AuthForm() { const [isPassword, setIsPassword] = useState(true); const [error, setError] = useState<string | null>(null); const [signInLoading, setSignInIsLoading] = useState(false); const router = useRouter();
const callbackUrl = getCookie("originCallbackUrl"); // Get callback url from cookie to redirect after login success.
const form = useForm<z.infer<typeof ZodAuthSchema>>({ resolver: zodResolver(ZodAuthSchema), defaultValues: { email: "", password: "", }, });
async function handleSignIn(data: z.infer<typeof ZodAuthSchema>) { setError(null); setSignInIsLoading(true);
try {
const signInResponse = await signIn("credentials", {
email: data.email,
password: data.password,
redirect: false,
callbackUrl: callbackUrl || "/",
});
if (signInResponse?.error) {
form.reset();
throw new Error("Invalid credentials.");
}
toast.success("Signed in successfully. redirecting...");
deleteCookie("originCallbackUrl"); // Delete callbackUrl cookie after successful login
router.refresh();
router.replace(signInResponse?.url || "/");
} catch (error: any) {
setError(error.message);
} finally {
setSignInIsLoading(false);
}
}
const onSuccess = ( data: UserResProps, variables: z.infer<typeof ZodAuthSchema>, ) => { toast.success("Account created successfully!"); handleSignIn(variables); // Auto sign in after account creation. }; const onError = ({ response }: { response: any }) => { setError(response.data.message); };
const mutation = useCreateAccount(onSuccess, onError);
async function handleCreateAccount(data: z.infer<typeof ZodAuthSchema>) { mutation.mutate(data); setError(null); }
return ( <Form {...form}> <form> <FormField control={form.control} name="email" render={({ field }) => ( <FormItem className="mb-3"> <FormControl> <Input placeholder="Email" {...field} radius="sm" size="sm" classNames={{ inputWrapper: "border border-slate-200", }} /> </FormControl> <FormMessage /> </FormItem> )} /> <FormField control={form.control} name="password" render={({ field }) => ( <FormItem> <FormControl> <Input {...field} placeholder="Password" radius="sm" size="sm" classNames={{ inputWrapper: "border border-slate-200", }} endContent={ isPassword ? ( <Eye className="cursor-pointer text-gray-400" onClick={() => setIsPassword(false)} /> ) : ( <EyeOff className="cursor-pointer" onClick={() => setIsPassword(true)} /> ) } type={isPassword ? "password" : "text"} /> </FormControl> <FormMessage /> </FormItem> )} /> {error ? ( <m.span initial={{ opacity: 0, y: -5 }} animate={{ opacity: 1, y: 0 }} transition={{ duration: 0.3, }} className="mt-3 block h-5 text-center text-xs text-destructive" > {error} </m.span> ) : ( <span className="mt-3 block h-5" /> )} <div className="mt-5 flex flex-col gap-3"> <Button isLoading={signInLoading} color="primary" onClick={form.handleSubmit(handleSignIn)} isDisabled={mutation.isLoading} radius="full" type="button" > Sign in </Button> <Button isLoading={mutation.isLoading} color="primary" onClick={form.handleSubmit(handleCreateAccount)} isDisabled={mutation.isLoading} radius="full" type="button" variant="bordered" className="!border" > Create account </Button> </div> </form> </Form> ); } signup and signin tabs only
I'll help you modify your AuthForm component to use tabs for signup and signin functionality.
I've updated your AuthForm component to use tabs for signup and signin functionality. Here are the key changes:
Added Tabs Interface:
Tabs
component to create a tabbed interfaceSeparate Forms for Each Tab:
Conditional Rendering:
Updated Button Logic:
Improved User Experience:
The component maintains all your existing functionality, including:
You can now easily switch between signing in and creating an account using the tabs at the top of the form.