I'm trying to authenticate using @supabase/srr. In the docs the login/signup form is supposed to be a server component as it calls login and signup servers actions https://supabase.com/docs/guides/auth/server-side/nextjs
import { login, signup } from './actions'
export default function LoginPage() {
return (
<form>
<label htmlFor="email">Email:</label>
<input id="email" name="email" type="email" required />
<label htmlFor="password">Password:</label>
<input id="password" name="password" type="password" required />
<button formAction={login}>Log in</button>
<button formAction={signup}>Sign up</button>
</form>
)
}
I want to use zod for this form, but the useForm hook (from react-hook-form) is obviously client side and I'm also using some stateful values.
So what is good practice here ? Is it just not a problem to call my actions from a client component ? or should I make my actions api endpoints ? or any other solutions ?
call server action from client component is common practice. you can pass formdata to server action and than validate it on server side. i will provide two common way. first will be using ssr and second using client side
1.call login action from ssr component
import { z } from "zod";
const loginSchema = z.object({
email: z.string().email("Invalid email format"),
password: z.string().min(6, "Password must be at least 6 characters long"),
});
export default function LoginPage() {
async function login(formData: FormData) {
'use server';
const rawFormData = {
email: formData.get("email"),
password: formData.get("password")
};
// Validate data
const result = loginSchema.safeParse(rawFormData);
if (!result.success) {
// Handle validation errors
return;
}
const validatedData = result.data;
// Proceed with login logic using validatedData
}
return (
<form action={login}>
<label htmlFor="email">Email:</label>
<input id="email" name="email" type="email" required />
<label htmlFor="password">Password:</label>
<input id="password" name="password" type="password" required />
<button formAction={login}>Log in</button>
</form>
);
}
2.call login action from client side (also normal, common and safe way)
"use client";
import { login } from "@/components/actions";
export default function LoginPage() {
async function handleSubmit(event: React.FormEvent<HTMLFormElement>) {
event.preventDefault();
const formData = new FormData(event.currentTarget);
try {
await login(formData);
console.log("Login successful");
// Redirect or update the UI on success
} catch (error) {
console.error("Login failed:", error);
// Display error message in the UI
}
}
return (
<form onSubmit={handleSubmit}>
<label htmlFor="email">Email:</label>
<input id="email" name="email" type="email" required />
<label htmlFor="password">Password:</label>
<input id="password" name="password" type="password" required />
<button type="submit">Log in</button>
</form>
);
}