Skip to content

Instantly share code, notes, and snippets.

@joshmosh
Created April 28, 2026 15:27
Show Gist options
  • Select an option

  • Save joshmosh/16db579571c7bae6d5378318682a0e77 to your computer and use it in GitHub Desktop.

Select an option

Save joshmosh/16db579571c7bae6d5378318682a0e77 to your computer and use it in GitHub Desktop.
ACUL Example
import { useState, useEffect } from 'react';
import { useForm } from 'react-hook-form';
import { yupResolver } from '@hookform/resolvers/yup';
import type { InferType } from 'yup';
import {
login,
useErrors,
useScreen,
useUntrustedData,
} from '@auth0/auth0-acul-react/login';
import { FORGOT_PASSWORD_URL } from '@/constants/externalUrls';
import useAuthParams from '@/lib/hooks/useAuthParams';
import useCustomization from '@/lib/hooks/useCustomization';
import useToast from '@/lib/hooks/useToast';
import { loginSchema } from '@/lib/schemas/loginSchema';
import Input from '@/ui/Input';
import Button from '@/ui/Button';
import Typography from '@/ui/Typography';
import Link from '@/ui/Link';
import Captcha from '@/components/Captcha';
import LegalLinks from '@/components/LegalLinks';
type LoginFormData = InferType<typeof loginSchema>;
const LoginForm = () => {
const [captchaToken, setCaptchaToken] = useState('');
const [hasSubmitted, setHasSubmitted] = useState(false);
const { hasError, errors: loginErrors } = useErrors();
const { toast } = useToast();
const { loginCta } = useCustomization();
const { submittedFormData } = useUntrustedData();
const { qaPassword } = useAuthParams();
const screen = useScreen();
useEffect(() => {
if (hasError) {
setHasSubmitted(false);
toast({
id: 'login-error',
title: 'Oops! Something went wrong.',
description: loginErrors.map((error) => error.message).join(', '),
variant: 'error',
});
}
}, [hasError, loginErrors, toast]);
const {
register,
handleSubmit,
formState: { errors: formErrors, isValid },
} = useForm<LoginFormData>({
resolver: yupResolver(loginSchema),
mode: 'onTouched',
defaultValues: {
email: (submittedFormData?.username as string) || '',
password: qaPassword || '',
},
});
const onSubmit = async (data: LoginFormData) => {
setHasSubmitted(true);
await login({
username: data.email,
password: data.password,
captcha: screen.isCaptchaAvailable ? captchaToken.trim() : '',
});
};
return (
<>
<form onSubmit={handleSubmit(onSubmit)} data-testid="login-form">
<Input
className="mb-2"
data-testid="email-input"
label="Email"
type="email"
autoComplete="email"
error={!!formErrors.email}
errorMessage={formErrors.email?.message}
{...register('email')}
/>
<Input
className="mb-6"
data-testid="password-input"
label="Password"
type="password"
error={!!formErrors.password}
errorMessage={formErrors.password?.message}
{...register('password')}
/>
<Typography variant="bodySm" align="center" className="mb-4">
<Link data-testid="forgot-password-link" href={FORGOT_PASSWORD_URL}>
Forgot your password?
</Link>
</Typography>
{screen.isCaptchaAvailable && (
<Captcha
className="mb-2 flex justify-center"
screen={screen}
onToken={setCaptchaToken}
/>
)}
<div className="mb-4">
<LegalLinks ctaCopy={loginCta} isSignUp={false} />
</div>
<Button
data-testid="login-button"
block
type="submit"
disabled={!isValid || hasSubmitted}
>
{loginCta}
</Button>
</form>
{screen.signupLink && (
<div className="mt-6">
<Typography light align="center" className="mb-2">
Don't have an account?
</Typography>
<Button
data-testid="sign-up-link"
block
variant="secondary"
href={screen.signupLink}
>
Sign Up
</Button>
</div>
)}
</>
);
};
export default LoginForm;
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment