Skip to main content

useSignUp()

The useSignUp() hook gives you direct programmatic access to the Wacht registration engine. It bypasses the built-in <SignUpForm /> UI component while still guaranteeing the same robust cryptography, data structure validation, and server-side safety logic. You will typically reach for this hook when you need to embed account creation seamlessly into an existing multi-step onboarding wizard, or when your design system strictly prohibits injecting 3rd-party UI nodes.

Import

import { useSignUp } from "@wacht/react-router";

Architecture

Creating a secure user record is intrinsically a multi-phased operation. The useSignUp hook reflects this securely.
loading
boolean
Indicates if the SDK is actively managing a request state.
signUp
SignUpFunction
signupAttempt
SignupAttempt | null
The current state of the registration attempt.
discardSignupAttempt
() => void
Clears the current registration attempt state.

Constructing a Custom Verification Flow

In modern identity systems, you cannot create an account simply by transmitting an email and password to the database. You must verify that the user fundamentally owns the contact identifier they supplied. Here is an example of using the hook to initiate registration and verify an email via an OTP (One-Time Passcode):
import { useSignUp } from "@wacht/react-router";
import { useState } from "react";
import { useNavigate } from "react-router";

export default function CustomSignUp() {
  const { loading, signUp } = useSignUp();
  const navigate = useNavigate();

  const [email, setEmail] = useState("");
  const [password, setPassword] = useState("");
  const [pendingVerification, setPendingVerification] = useState(false);
  const [code, setCode] = useState("");

  if (loading) return null;

  // Phase 1: Submit initial credentials
  const handleRegistration = async (e: React.FormEvent) => {
    e.preventDefault();
    try {
      await signUp.create({ email, password });
      
      // Dispatch an OTP to the provided email address
      await signUp.prepareVerification({ strategy: "email_otp" });
      
      // Transition our local UI state to show the OTP input
      setPendingVerification(true);
    } catch (err: any) {
      console.error(err);
    }
  };

  // Phase 2: Verify the OTP and establish session
  const handleVerification = async (e: React.FormEvent) => {
    e.preventDefault();
    try {
      await signUp.completeVerification(code);
      
      // The user is fully verified and an HTTP-only session cookie is set natively.
      navigate("/dashboard");
    } catch (err: any) {
      console.error(err);
    }
  };

  if (pendingVerification) {
    return (
      <form onSubmit={handleVerification}>
        <input value={code} placeholder="Enter Code..." onChange={(e) => setCode(e.target.value)} />
        <button type="submit">Verify Email</button>
      </form>
    );
  }

  return (
    <form onSubmit={handleRegistration}>
      <input type="email" value={email} onChange={(e) => setEmail(e.target.value)} />
      <input type="password" value={password} onChange={(e) => setPassword(e.target.value)} />
      <button type="submit">Register</button>
    </form>
  );
}