next.jsreact-hook-formsupabasezod

How to use Zod with @supabase/srr in NextJs


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 ?


Solution

  • 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>
      );
    }